PageSourceSearch

https://shtabeler-kzn.ru/wp-content/mu-plugins/src/js/helper/Zoom.js

js shtabeler-kzn.ru collected 2026-10-07 13:18:20 UTC 6,769 bytes, 191 lines download raw bytes

1(function($) {
2    class SlickZoom {
3        constructor(sliderSelector) {
4            this.slider = $(sliderSelector);
5            if (this.slider.length === 0) return;
6
7            this._initStyles();
8            this._initVariables();
9            this._bindEvents();
10            this._startAnimationLoop();
11        }
12
13        _initStyles() {
14            const style = document.createElement('style');
15            style.textContent = `
16                .slick-slider {
17                    touch-action: auto !important;
18                    user-select: none;
19                    -webkit-user-drag: none;
20                    -webkit-touch-callout: none;
21                    overscroll-behavior: contain;
22                }
23                .slick-slide img {
24                    will-change: transform;
25                    -webkit-user-select: none;
26                    -webkit-user-drag: none;
27                    pointer-events: none;
28                }
29            `;
30            document.head.appendChild(style);
31        }
32
33        _initVariables() {
34            this.scale = 1;
35            this.targetScale = 1;
36
37            this.translateX = 0;
38            this.translateY = 0;
39            this.targetX = 0;
40            this.targetY = 0;
41
42            this.initialDistance = null;
43            this.isPanning = false;
44            this.startX = 0;
45            this.startY = 0;
46            this.isZoomActive = false;
47        }
48
49        _bindEvents() {
50            const sliderEl = this.slider[0];
51
52            sliderEl.addEventListener('touchstart', (e) => this._handleTouchStart(e), { passive: false });
53            sliderEl.addEventListener('touchmove', (e) => this._handleTouchMove(e), { passive: false });
54            sliderEl.addEventListener('touchend', (e) => this._handleTouchEnd(e), { passive: false });
55
56            this.slider.on('beforeChange', (event, slick, currentSlide, nextSlide, changeType) => {
57                if (this.isZoomActive) {
58                    if (changeType === 'swipe') {
59                        event.preventDefault();
60                        event.stopPropagation();
61                        slick.pauseAuto();
62                        return false;
63                    } else {
64                        this._resetZoom();
65                    }
66                }
67            });
68        }
69
70        _disableSwipe() {
71            this.slider.slick('slickSetOption', 'swipe', false, true);
72            this.slider.slick('slickSetOption', 'touchMove', false, true);
73        }
74
75        _enableSwipe() {
76            this.slider.slick('slickSetOption', 'swipe', true, true);
77            this.slider.slick('slickSetOption', 'touchMove', true, true);
78        }
79
80        _handleTouchStart(event) {
81            const touches = event.touches;
82
83            if (touches.length === 2) {
84                event.preventDefault();
85                this.initialDistance = this._getTouchDistance(touches[0], touches[1]);
86                this._disableSwipe();
87                this.isZoomActive = true;
88            } else if (touches.length === 1 && this.scale > 1) {
89                event.preventDefault();
90                this.isPanning = true;
91                this.startX = touches[0].clientX - this.targetX;
92                this.startY = touches[0].clientY - this.targetY;
93            }
94        }
95
96        _handleTouchMove(event) {
97            const touches = event.touches;
98
99            if (touches.length === 2 && this.initialDistance !== null) {
100                event.preventDefault();
101                const currentDistance = this._getTouchDistance(touches[0], touches[1]);
102                const newScale = Math.max(1, Math.min(this.scale * (currentDistance / this.initialDistance), 3));
103                this.targetScale = newScale;
104            } else if (this.isPanning && this.scale > 1) {
105                event.preventDefault();
106                this.targetX = touches[0].clientX - this.startX;
107                this.targetY = touches[0].clientY - this.startY;
108                this._adjustTranslation(this.targetScale);
109            }
110        }
111
112        _handleTouchEnd() {
113            this.initialDistance = null;
114            this.isPanning = false;
115
116            if (Math.abs(this.targetScale - 1) < 0.01) {
117                this._resetZoom();
118            } else {
119                this.isZoomActive = true;
120                this._disableSwipe();
121            }
122        }
123
124        _getTouchDistance(touch1, touch2) {
125            const dx = touch2.clientX - touch1.clientX;
126            const dy = touch2.clientY - touch1.clientY;
127            return Math.sqrt(dx * dx + dy * dy);
128        }
129
130        _applyTransform() {
131            const activeSlide = this.slider.find('.slick-active');
132            const image = activeSlide.find('img');
133            image.css('transform', `translate(${this.translateX}px, ${this.translateY}px) scale(${this.scale})`);
134        }
135
136        _calculateBounds(imageElement, scale) {
137            const rect = imageElement.getBoundingClientRect();
138            const width = rect.width * scale;
139            const height = rect.height * scale;
140
141            return {
142                left: -(width - window.innerWidth) / 2,
143                right: (width - window.innerWidth) / 2,
144                top: -(height - window.innerHeight) / 2,
145                bottom: (height - window.innerHeight) / 2
146            };
147        }
148
149        _adjustTranslation(newScale) {
150            const activeSlide = this.slider.find('.slick-active');
151            const image = activeSlide.find('img')[0];
152            const bounds = this._calculateBounds(image, newScale);
153
154            this.targetX = Math.max(bounds.left, Math.min(bounds.right, this.targetX));
155            this.targetY = Math.max(bounds.top, Math.min(bounds.bottom, this.targetY));
156
157            if (Math.abs(newScale - 1) < 0.01) {
158                this.targetX = 0;
159                this.targetY = 0;
160            }
161        }
162
163        _resetZoom() {
164            this.scale = 1;
165            this.targetScale = 1;
166            this.translateX = 0;
167            this.translateY = 0;
168            this.targetX = 0;
169            this.targetY = 0;
170            this.isZoomActive = false;
171            this._applyTransform();
172            this._enableSwipe();
173        }
174
175        _startAnimationLoop() {
176            const animate = () => {
177                this.scale += (this.targetScale - this.scale) * 0.2;
178                this.translateX += (this.targetX - this.translateX) * 0.2;
179                this.translateY += (this.targetY - this.translateY) * 0.2;
180
181                this._applyTransform();
182                requestAnimationFrame(animate);
183            };
184            animate();
185        }
186    }
187
188    $(document).ready(function() {
189        new SlickZoom(zoomSettings.containerSelector);
190    });
191})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.