123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613 |
- import { getWindow } from 'ssr-window';
- import $ from '../../shared/dom.js';
- import { getTranslate } from '../../shared/utils.js';
- export default function Zoom(_ref) {
- let {
- swiper,
- extendParams,
- on,
- emit
- } = _ref;
- const window = getWindow();
- extendParams({
- zoom: {
- enabled: false,
- maxRatio: 3,
- minRatio: 1,
- toggle: true,
- containerClass: 'swiper-zoom-container',
- zoomedSlideClass: 'swiper-slide-zoomed'
- }
- });
- swiper.zoom = {
- enabled: false
- };
- let currentScale = 1;
- let isScaling = false;
- let gesturesEnabled;
- let fakeGestureTouched;
- let fakeGestureMoved;
- const gesture = {
- $slideEl: undefined,
- slideWidth: undefined,
- slideHeight: undefined,
- $imageEl: undefined,
- $imageWrapEl: undefined,
- maxRatio: 3
- };
- const image = {
- isTouched: undefined,
- isMoved: undefined,
- currentX: undefined,
- currentY: undefined,
- minX: undefined,
- minY: undefined,
- maxX: undefined,
- maxY: undefined,
- width: undefined,
- height: undefined,
- startX: undefined,
- startY: undefined,
- touchesStart: {},
- touchesCurrent: {}
- };
- const velocity = {
- x: undefined,
- y: undefined,
- prevPositionX: undefined,
- prevPositionY: undefined,
- prevTime: undefined
- };
- let scale = 1;
- Object.defineProperty(swiper.zoom, 'scale', {
- get() {
- return scale;
- },
- set(value) {
- if (scale !== value) {
- const imageEl = gesture.$imageEl ? gesture.$imageEl[0] : undefined;
- const slideEl = gesture.$slideEl ? gesture.$slideEl[0] : undefined;
- emit('zoomChange', value, imageEl, slideEl);
- }
- scale = value;
- }
- });
- function getDistanceBetweenTouches(e) {
- if (e.targetTouches.length < 2) return 1;
- const x1 = e.targetTouches[0].pageX;
- const y1 = e.targetTouches[0].pageY;
- const x2 = e.targetTouches[1].pageX;
- const y2 = e.targetTouches[1].pageY;
- const distance = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
- return distance;
- } // Events
- function onGestureStart(e) {
- const support = swiper.support;
- const params = swiper.params.zoom;
- fakeGestureTouched = false;
- fakeGestureMoved = false;
- if (!support.gestures) {
- if (e.type !== 'touchstart' || e.type === 'touchstart' && e.targetTouches.length < 2) {
- return;
- }
- fakeGestureTouched = true;
- gesture.scaleStart = getDistanceBetweenTouches(e);
- }
- if (!gesture.$slideEl || !gesture.$slideEl.length) {
- gesture.$slideEl = $(e.target).closest(`.${swiper.params.slideClass}`);
- if (gesture.$slideEl.length === 0) gesture.$slideEl = swiper.slides.eq(swiper.activeIndex);
- gesture.$imageEl = gesture.$slideEl.find(`.${params.containerClass}`).eq(0).find('picture, img, svg, canvas, .swiper-zoom-target').eq(0);
- gesture.$imageWrapEl = gesture.$imageEl.parent(`.${params.containerClass}`);
- gesture.maxRatio = gesture.$imageWrapEl.attr('data-swiper-zoom') || params.maxRatio;
- if (gesture.$imageWrapEl.length === 0) {
- gesture.$imageEl = undefined;
- return;
- }
- }
- if (gesture.$imageEl) {
- gesture.$imageEl.transition(0);
- }
- isScaling = true;
- }
- function onGestureChange(e) {
- const support = swiper.support;
- const params = swiper.params.zoom;
- const zoom = swiper.zoom;
- if (!support.gestures) {
- if (e.type !== 'touchmove' || e.type === 'touchmove' && e.targetTouches.length < 2) {
- return;
- }
- fakeGestureMoved = true;
- gesture.scaleMove = getDistanceBetweenTouches(e);
- }
- if (!gesture.$imageEl || gesture.$imageEl.length === 0) {
- if (e.type === 'gesturechange') onGestureStart(e);
- return;
- }
- if (support.gestures) {
- zoom.scale = e.scale * currentScale;
- } else {
- zoom.scale = gesture.scaleMove / gesture.scaleStart * currentScale;
- }
- if (zoom.scale > gesture.maxRatio) {
- zoom.scale = gesture.maxRatio - 1 + (zoom.scale - gesture.maxRatio + 1) ** 0.5;
- }
- if (zoom.scale < params.minRatio) {
- zoom.scale = params.minRatio + 1 - (params.minRatio - zoom.scale + 1) ** 0.5;
- }
- gesture.$imageEl.transform(`translate3d(0,0,0) scale(${zoom.scale})`);
- }
- function onGestureEnd(e) {
- const device = swiper.device;
- const support = swiper.support;
- const params = swiper.params.zoom;
- const zoom = swiper.zoom;
- if (!support.gestures) {
- if (!fakeGestureTouched || !fakeGestureMoved) {
- return;
- }
- if (e.type !== 'touchend' || e.type === 'touchend' && e.changedTouches.length < 2 && !device.android) {
- return;
- }
- fakeGestureTouched = false;
- fakeGestureMoved = false;
- }
- if (!gesture.$imageEl || gesture.$imageEl.length === 0) return;
- zoom.scale = Math.max(Math.min(zoom.scale, gesture.maxRatio), params.minRatio);
- gesture.$imageEl.transition(swiper.params.speed).transform(`translate3d(0,0,0) scale(${zoom.scale})`);
- currentScale = zoom.scale;
- isScaling = false;
- if (zoom.scale === 1) gesture.$slideEl = undefined;
- }
- function onTouchStart(e) {
- const device = swiper.device;
- if (!gesture.$imageEl || gesture.$imageEl.length === 0) return;
- if (image.isTouched) return;
- if (device.android && e.cancelable) e.preventDefault();
- image.isTouched = true;
- image.touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
- image.touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
- }
- function onTouchMove(e) {
- const zoom = swiper.zoom;
- if (!gesture.$imageEl || gesture.$imageEl.length === 0) return;
- swiper.allowClick = false;
- if (!image.isTouched || !gesture.$slideEl) return;
- if (!image.isMoved) {
- image.width = gesture.$imageEl[0].offsetWidth;
- image.height = gesture.$imageEl[0].offsetHeight;
- image.startX = getTranslate(gesture.$imageWrapEl[0], 'x') || 0;
- image.startY = getTranslate(gesture.$imageWrapEl[0], 'y') || 0;
- gesture.slideWidth = gesture.$slideEl[0].offsetWidth;
- gesture.slideHeight = gesture.$slideEl[0].offsetHeight;
- gesture.$imageWrapEl.transition(0);
- } // Define if we need image drag
- const scaledWidth = image.width * zoom.scale;
- const scaledHeight = image.height * zoom.scale;
- if (scaledWidth < gesture.slideWidth && scaledHeight < gesture.slideHeight) return;
- image.minX = Math.min(gesture.slideWidth / 2 - scaledWidth / 2, 0);
- image.maxX = -image.minX;
- image.minY = Math.min(gesture.slideHeight / 2 - scaledHeight / 2, 0);
- image.maxY = -image.minY;
- image.touchesCurrent.x = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
- image.touchesCurrent.y = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
- if (!image.isMoved && !isScaling) {
- if (swiper.isHorizontal() && (Math.floor(image.minX) === Math.floor(image.startX) && image.touchesCurrent.x < image.touchesStart.x || Math.floor(image.maxX) === Math.floor(image.startX) && image.touchesCurrent.x > image.touchesStart.x)) {
- image.isTouched = false;
- return;
- }
- if (!swiper.isHorizontal() && (Math.floor(image.minY) === Math.floor(image.startY) && image.touchesCurrent.y < image.touchesStart.y || Math.floor(image.maxY) === Math.floor(image.startY) && image.touchesCurrent.y > image.touchesStart.y)) {
- image.isTouched = false;
- return;
- }
- }
- if (e.cancelable) {
- e.preventDefault();
- }
- e.stopPropagation();
- image.isMoved = true;
- image.currentX = image.touchesCurrent.x - image.touchesStart.x + image.startX;
- image.currentY = image.touchesCurrent.y - image.touchesStart.y + image.startY;
- if (image.currentX < image.minX) {
- image.currentX = image.minX + 1 - (image.minX - image.currentX + 1) ** 0.8;
- }
- if (image.currentX > image.maxX) {
- image.currentX = image.maxX - 1 + (image.currentX - image.maxX + 1) ** 0.8;
- }
- if (image.currentY < image.minY) {
- image.currentY = image.minY + 1 - (image.minY - image.currentY + 1) ** 0.8;
- }
- if (image.currentY > image.maxY) {
- image.currentY = image.maxY - 1 + (image.currentY - image.maxY + 1) ** 0.8;
- } // Velocity
- if (!velocity.prevPositionX) velocity.prevPositionX = image.touchesCurrent.x;
- if (!velocity.prevPositionY) velocity.prevPositionY = image.touchesCurrent.y;
- if (!velocity.prevTime) velocity.prevTime = Date.now();
- velocity.x = (image.touchesCurrent.x - velocity.prevPositionX) / (Date.now() - velocity.prevTime) / 2;
- velocity.y = (image.touchesCurrent.y - velocity.prevPositionY) / (Date.now() - velocity.prevTime) / 2;
- if (Math.abs(image.touchesCurrent.x - velocity.prevPositionX) < 2) velocity.x = 0;
- if (Math.abs(image.touchesCurrent.y - velocity.prevPositionY) < 2) velocity.y = 0;
- velocity.prevPositionX = image.touchesCurrent.x;
- velocity.prevPositionY = image.touchesCurrent.y;
- velocity.prevTime = Date.now();
- gesture.$imageWrapEl.transform(`translate3d(${image.currentX}px, ${image.currentY}px,0)`);
- }
- function onTouchEnd() {
- const zoom = swiper.zoom;
- if (!gesture.$imageEl || gesture.$imageEl.length === 0) return;
- if (!image.isTouched || !image.isMoved) {
- image.isTouched = false;
- image.isMoved = false;
- return;
- }
- image.isTouched = false;
- image.isMoved = false;
- let momentumDurationX = 300;
- let momentumDurationY = 300;
- const momentumDistanceX = velocity.x * momentumDurationX;
- const newPositionX = image.currentX + momentumDistanceX;
- const momentumDistanceY = velocity.y * momentumDurationY;
- const newPositionY = image.currentY + momentumDistanceY; // Fix duration
- if (velocity.x !== 0) momentumDurationX = Math.abs((newPositionX - image.currentX) / velocity.x);
- if (velocity.y !== 0) momentumDurationY = Math.abs((newPositionY - image.currentY) / velocity.y);
- const momentumDuration = Math.max(momentumDurationX, momentumDurationY);
- image.currentX = newPositionX;
- image.currentY = newPositionY; // Define if we need image drag
- const scaledWidth = image.width * zoom.scale;
- const scaledHeight = image.height * zoom.scale;
- image.minX = Math.min(gesture.slideWidth / 2 - scaledWidth / 2, 0);
- image.maxX = -image.minX;
- image.minY = Math.min(gesture.slideHeight / 2 - scaledHeight / 2, 0);
- image.maxY = -image.minY;
- image.currentX = Math.max(Math.min(image.currentX, image.maxX), image.minX);
- image.currentY = Math.max(Math.min(image.currentY, image.maxY), image.minY);
- gesture.$imageWrapEl.transition(momentumDuration).transform(`translate3d(${image.currentX}px, ${image.currentY}px,0)`);
- }
- function onTransitionEnd() {
- const zoom = swiper.zoom;
- if (gesture.$slideEl && swiper.previousIndex !== swiper.activeIndex) {
- if (gesture.$imageEl) {
- gesture.$imageEl.transform('translate3d(0,0,0) scale(1)');
- }
- if (gesture.$imageWrapEl) {
- gesture.$imageWrapEl.transform('translate3d(0,0,0)');
- }
- zoom.scale = 1;
- currentScale = 1;
- gesture.$slideEl = undefined;
- gesture.$imageEl = undefined;
- gesture.$imageWrapEl = undefined;
- }
- }
- function zoomIn(e) {
- const zoom = swiper.zoom;
- const params = swiper.params.zoom;
- if (!gesture.$slideEl) {
- if (e && e.target) {
- gesture.$slideEl = $(e.target).closest(`.${swiper.params.slideClass}`);
- }
- if (!gesture.$slideEl) {
- if (swiper.params.virtual && swiper.params.virtual.enabled && swiper.virtual) {
- gesture.$slideEl = swiper.$wrapperEl.children(`.${swiper.params.slideActiveClass}`);
- } else {
- gesture.$slideEl = swiper.slides.eq(swiper.activeIndex);
- }
- }
- gesture.$imageEl = gesture.$slideEl.find(`.${params.containerClass}`).eq(0).find('picture, img, svg, canvas, .swiper-zoom-target').eq(0);
- gesture.$imageWrapEl = gesture.$imageEl.parent(`.${params.containerClass}`);
- }
- if (!gesture.$imageEl || gesture.$imageEl.length === 0 || !gesture.$imageWrapEl || gesture.$imageWrapEl.length === 0) return;
- if (swiper.params.cssMode) {
- swiper.wrapperEl.style.overflow = 'hidden';
- swiper.wrapperEl.style.touchAction = 'none';
- }
- gesture.$slideEl.addClass(`${params.zoomedSlideClass}`);
- let touchX;
- let touchY;
- let offsetX;
- let offsetY;
- let diffX;
- let diffY;
- let translateX;
- let translateY;
- let imageWidth;
- let imageHeight;
- let scaledWidth;
- let scaledHeight;
- let translateMinX;
- let translateMinY;
- let translateMaxX;
- let translateMaxY;
- let slideWidth;
- let slideHeight;
- if (typeof image.touchesStart.x === 'undefined' && e) {
- touchX = e.type === 'touchend' ? e.changedTouches[0].pageX : e.pageX;
- touchY = e.type === 'touchend' ? e.changedTouches[0].pageY : e.pageY;
- } else {
- touchX = image.touchesStart.x;
- touchY = image.touchesStart.y;
- }
- zoom.scale = gesture.$imageWrapEl.attr('data-swiper-zoom') || params.maxRatio;
- currentScale = gesture.$imageWrapEl.attr('data-swiper-zoom') || params.maxRatio;
- if (e) {
- slideWidth = gesture.$slideEl[0].offsetWidth;
- slideHeight = gesture.$slideEl[0].offsetHeight;
- offsetX = gesture.$slideEl.offset().left + window.scrollX;
- offsetY = gesture.$slideEl.offset().top + window.scrollY;
- diffX = offsetX + slideWidth / 2 - touchX;
- diffY = offsetY + slideHeight / 2 - touchY;
- imageWidth = gesture.$imageEl[0].offsetWidth;
- imageHeight = gesture.$imageEl[0].offsetHeight;
- scaledWidth = imageWidth * zoom.scale;
- scaledHeight = imageHeight * zoom.scale;
- translateMinX = Math.min(slideWidth / 2 - scaledWidth / 2, 0);
- translateMinY = Math.min(slideHeight / 2 - scaledHeight / 2, 0);
- translateMaxX = -translateMinX;
- translateMaxY = -translateMinY;
- translateX = diffX * zoom.scale;
- translateY = diffY * zoom.scale;
- if (translateX < translateMinX) {
- translateX = translateMinX;
- }
- if (translateX > translateMaxX) {
- translateX = translateMaxX;
- }
- if (translateY < translateMinY) {
- translateY = translateMinY;
- }
- if (translateY > translateMaxY) {
- translateY = translateMaxY;
- }
- } else {
- translateX = 0;
- translateY = 0;
- }
- gesture.$imageWrapEl.transition(300).transform(`translate3d(${translateX}px, ${translateY}px,0)`);
- gesture.$imageEl.transition(300).transform(`translate3d(0,0,0) scale(${zoom.scale})`);
- }
- function zoomOut() {
- const zoom = swiper.zoom;
- const params = swiper.params.zoom;
- if (!gesture.$slideEl) {
- if (swiper.params.virtual && swiper.params.virtual.enabled && swiper.virtual) {
- gesture.$slideEl = swiper.$wrapperEl.children(`.${swiper.params.slideActiveClass}`);
- } else {
- gesture.$slideEl = swiper.slides.eq(swiper.activeIndex);
- }
- gesture.$imageEl = gesture.$slideEl.find(`.${params.containerClass}`).eq(0).find('picture, img, svg, canvas, .swiper-zoom-target').eq(0);
- gesture.$imageWrapEl = gesture.$imageEl.parent(`.${params.containerClass}`);
- }
- if (!gesture.$imageEl || gesture.$imageEl.length === 0 || !gesture.$imageWrapEl || gesture.$imageWrapEl.length === 0) return;
- if (swiper.params.cssMode) {
- swiper.wrapperEl.style.overflow = '';
- swiper.wrapperEl.style.touchAction = '';
- }
- zoom.scale = 1;
- currentScale = 1;
- gesture.$imageWrapEl.transition(300).transform('translate3d(0,0,0)');
- gesture.$imageEl.transition(300).transform('translate3d(0,0,0) scale(1)');
- gesture.$slideEl.removeClass(`${params.zoomedSlideClass}`);
- gesture.$slideEl = undefined;
- } // Toggle Zoom
- function zoomToggle(e) {
- const zoom = swiper.zoom;
- if (zoom.scale && zoom.scale !== 1) {
- // Zoom Out
- zoomOut();
- } else {
- // Zoom In
- zoomIn(e);
- }
- }
- function getListeners() {
- const support = swiper.support;
- const passiveListener = swiper.touchEvents.start === 'touchstart' && support.passiveListener && swiper.params.passiveListeners ? {
- passive: true,
- capture: false
- } : false;
- const activeListenerWithCapture = support.passiveListener ? {
- passive: false,
- capture: true
- } : true;
- return {
- passiveListener,
- activeListenerWithCapture
- };
- }
- function getSlideSelector() {
- return `.${swiper.params.slideClass}`;
- }
- function toggleGestures(method) {
- const {
- passiveListener
- } = getListeners();
- const slideSelector = getSlideSelector();
- swiper.$wrapperEl[method]('gesturestart', slideSelector, onGestureStart, passiveListener);
- swiper.$wrapperEl[method]('gesturechange', slideSelector, onGestureChange, passiveListener);
- swiper.$wrapperEl[method]('gestureend', slideSelector, onGestureEnd, passiveListener);
- }
- function enableGestures() {
- if (gesturesEnabled) return;
- gesturesEnabled = true;
- toggleGestures('on');
- }
- function disableGestures() {
- if (!gesturesEnabled) return;
- gesturesEnabled = false;
- toggleGestures('off');
- } // Attach/Detach Events
- function enable() {
- const zoom = swiper.zoom;
- if (zoom.enabled) return;
- zoom.enabled = true;
- const support = swiper.support;
- const {
- passiveListener,
- activeListenerWithCapture
- } = getListeners();
- const slideSelector = getSlideSelector(); // Scale image
- if (support.gestures) {
- swiper.$wrapperEl.on(swiper.touchEvents.start, enableGestures, passiveListener);
- swiper.$wrapperEl.on(swiper.touchEvents.end, disableGestures, passiveListener);
- } else if (swiper.touchEvents.start === 'touchstart') {
- swiper.$wrapperEl.on(swiper.touchEvents.start, slideSelector, onGestureStart, passiveListener);
- swiper.$wrapperEl.on(swiper.touchEvents.move, slideSelector, onGestureChange, activeListenerWithCapture);
- swiper.$wrapperEl.on(swiper.touchEvents.end, slideSelector, onGestureEnd, passiveListener);
- if (swiper.touchEvents.cancel) {
- swiper.$wrapperEl.on(swiper.touchEvents.cancel, slideSelector, onGestureEnd, passiveListener);
- }
- } // Move image
- swiper.$wrapperEl.on(swiper.touchEvents.move, `.${swiper.params.zoom.containerClass}`, onTouchMove, activeListenerWithCapture);
- }
- function disable() {
- const zoom = swiper.zoom;
- if (!zoom.enabled) return;
- const support = swiper.support;
- zoom.enabled = false;
- const {
- passiveListener,
- activeListenerWithCapture
- } = getListeners();
- const slideSelector = getSlideSelector(); // Scale image
- if (support.gestures) {
- swiper.$wrapperEl.off(swiper.touchEvents.start, enableGestures, passiveListener);
- swiper.$wrapperEl.off(swiper.touchEvents.end, disableGestures, passiveListener);
- } else if (swiper.touchEvents.start === 'touchstart') {
- swiper.$wrapperEl.off(swiper.touchEvents.start, slideSelector, onGestureStart, passiveListener);
- swiper.$wrapperEl.off(swiper.touchEvents.move, slideSelector, onGestureChange, activeListenerWithCapture);
- swiper.$wrapperEl.off(swiper.touchEvents.end, slideSelector, onGestureEnd, passiveListener);
- if (swiper.touchEvents.cancel) {
- swiper.$wrapperEl.off(swiper.touchEvents.cancel, slideSelector, onGestureEnd, passiveListener);
- }
- } // Move image
- swiper.$wrapperEl.off(swiper.touchEvents.move, `.${swiper.params.zoom.containerClass}`, onTouchMove, activeListenerWithCapture);
- }
- on('init', () => {
- if (swiper.params.zoom.enabled) {
- enable();
- }
- });
- on('destroy', () => {
- disable();
- });
- on('touchStart', (_s, e) => {
- if (!swiper.zoom.enabled) return;
- onTouchStart(e);
- });
- on('touchEnd', (_s, e) => {
- if (!swiper.zoom.enabled) return;
- onTouchEnd(e);
- });
- on('doubleTap', (_s, e) => {
- if (!swiper.animating && swiper.params.zoom.enabled && swiper.zoom.enabled && swiper.params.zoom.toggle) {
- zoomToggle(e);
- }
- });
- on('transitionEnd', () => {
- if (swiper.zoom.enabled && swiper.params.zoom.enabled) {
- onTransitionEnd();
- }
- });
- on('slideChange', () => {
- if (swiper.zoom.enabled && swiper.params.zoom.enabled && swiper.params.cssMode) {
- onTransitionEnd();
- }
- });
- Object.assign(swiper.zoom, {
- enable,
- disable,
- in: zoomIn,
- out: zoomOut,
- toggle: zoomToggle
- });
- }
|