587 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			JavaScript
		
	
	
			
		
		
	
	
			587 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			JavaScript
		
	
	
import { getWindow } from 'ssr-window';
 | 
						|
import { elementChildren, elementOffset, elementParents, getTranslate } from '../../shared/utils.js';
 | 
						|
export default function Zoom({
 | 
						|
  swiper,
 | 
						|
  extendParams,
 | 
						|
  on,
 | 
						|
  emit
 | 
						|
}) {
 | 
						|
  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 fakeGestureTouched;
 | 
						|
  let fakeGestureMoved;
 | 
						|
  const evCache = [];
 | 
						|
  const gesture = {
 | 
						|
    originX: 0,
 | 
						|
    originY: 0,
 | 
						|
    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;
 | 
						|
        const slideEl = gesture.slideEl;
 | 
						|
        emit('zoomChange', value, imageEl, slideEl);
 | 
						|
      }
 | 
						|
      scale = value;
 | 
						|
    }
 | 
						|
  });
 | 
						|
  function getDistanceBetweenTouches() {
 | 
						|
    if (evCache.length < 2) return 1;
 | 
						|
    const x1 = evCache[0].pageX;
 | 
						|
    const y1 = evCache[0].pageY;
 | 
						|
    const x2 = evCache[1].pageX;
 | 
						|
    const y2 = evCache[1].pageY;
 | 
						|
    const distance = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
 | 
						|
    return distance;
 | 
						|
  }
 | 
						|
  function getScaleOrigin() {
 | 
						|
    if (evCache.length < 2) return {
 | 
						|
      x: null,
 | 
						|
      y: null
 | 
						|
    };
 | 
						|
    const box = gesture.imageEl.getBoundingClientRect();
 | 
						|
    return [(evCache[0].pageX + (evCache[1].pageX - evCache[0].pageX) / 2 - box.x) / currentScale, (evCache[0].pageY + (evCache[1].pageY - evCache[0].pageY) / 2 - box.y) / currentScale];
 | 
						|
  }
 | 
						|
  function getSlideSelector() {
 | 
						|
    return swiper.isElement ? `swiper-slide` : `.${swiper.params.slideClass}`;
 | 
						|
  }
 | 
						|
  function eventWithinSlide(e) {
 | 
						|
    const slideSelector = getSlideSelector();
 | 
						|
    if (e.target.matches(slideSelector)) return true;
 | 
						|
    if (swiper.slides.filter(slideEl => slideEl.contains(e.target)).length > 0) return true;
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  function eventWithinZoomContainer(e) {
 | 
						|
    const selector = `.${swiper.params.zoom.containerClass}`;
 | 
						|
    if (e.target.matches(selector)) return true;
 | 
						|
    if ([...swiper.el.querySelectorAll(selector)].filter(containerEl => containerEl.contains(e.target)).length > 0) return true;
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // Events
 | 
						|
  function onGestureStart(e) {
 | 
						|
    if (e.pointerType === 'mouse') {
 | 
						|
      evCache.splice(0, evCache.length);
 | 
						|
    }
 | 
						|
    if (!eventWithinSlide(e)) return;
 | 
						|
    const params = swiper.params.zoom;
 | 
						|
    fakeGestureTouched = false;
 | 
						|
    fakeGestureMoved = false;
 | 
						|
    evCache.push(e);
 | 
						|
    if (evCache.length < 2) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    fakeGestureTouched = true;
 | 
						|
    gesture.scaleStart = getDistanceBetweenTouches();
 | 
						|
    if (!gesture.slideEl) {
 | 
						|
      gesture.slideEl = e.target.closest(`.${swiper.params.slideClass}, swiper-slide`);
 | 
						|
      if (!gesture.slideEl) gesture.slideEl = swiper.slides[swiper.activeIndex];
 | 
						|
      let imageEl = gesture.slideEl.querySelector(`.${params.containerClass}`);
 | 
						|
      if (imageEl) {
 | 
						|
        imageEl = imageEl.querySelectorAll('picture, img, svg, canvas, .swiper-zoom-target')[0];
 | 
						|
      }
 | 
						|
      gesture.imageEl = imageEl;
 | 
						|
      if (imageEl) {
 | 
						|
        gesture.imageWrapEl = elementParents(gesture.imageEl, `.${params.containerClass}`)[0];
 | 
						|
      } else {
 | 
						|
        gesture.imageWrapEl = undefined;
 | 
						|
      }
 | 
						|
      if (!gesture.imageWrapEl) {
 | 
						|
        gesture.imageEl = undefined;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      gesture.maxRatio = gesture.imageWrapEl.getAttribute('data-swiper-zoom') || params.maxRatio;
 | 
						|
    }
 | 
						|
    if (gesture.imageEl) {
 | 
						|
      const [originX, originY] = getScaleOrigin();
 | 
						|
      gesture.originX = originX;
 | 
						|
      gesture.originY = originY;
 | 
						|
      gesture.imageEl.style.transitionDuration = '0ms';
 | 
						|
    }
 | 
						|
    isScaling = true;
 | 
						|
  }
 | 
						|
  function onGestureChange(e) {
 | 
						|
    if (!eventWithinSlide(e)) return;
 | 
						|
    const params = swiper.params.zoom;
 | 
						|
    const zoom = swiper.zoom;
 | 
						|
    const pointerIndex = evCache.findIndex(cachedEv => cachedEv.pointerId === e.pointerId);
 | 
						|
    if (pointerIndex >= 0) evCache[pointerIndex] = e;
 | 
						|
    if (evCache.length < 2) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    fakeGestureMoved = true;
 | 
						|
    gesture.scaleMove = getDistanceBetweenTouches();
 | 
						|
    if (!gesture.imageEl) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    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.style.transform = `translate3d(0,0,0) scale(${zoom.scale})`;
 | 
						|
  }
 | 
						|
  function onGestureEnd(e) {
 | 
						|
    if (!eventWithinSlide(e)) return;
 | 
						|
    if (e.pointerType === 'mouse' && e.type === 'pointerout') return;
 | 
						|
    const params = swiper.params.zoom;
 | 
						|
    const zoom = swiper.zoom;
 | 
						|
    const pointerIndex = evCache.findIndex(cachedEv => cachedEv.pointerId === e.pointerId);
 | 
						|
    if (pointerIndex >= 0) evCache.splice(pointerIndex, 1);
 | 
						|
    if (!fakeGestureTouched || !fakeGestureMoved) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    fakeGestureTouched = false;
 | 
						|
    fakeGestureMoved = false;
 | 
						|
    if (!gesture.imageEl) return;
 | 
						|
    zoom.scale = Math.max(Math.min(zoom.scale, gesture.maxRatio), params.minRatio);
 | 
						|
    gesture.imageEl.style.transitionDuration = `${swiper.params.speed}ms`;
 | 
						|
    gesture.imageEl.style.transform = `translate3d(0,0,0) scale(${zoom.scale})`;
 | 
						|
    currentScale = zoom.scale;
 | 
						|
    isScaling = false;
 | 
						|
    if (zoom.scale > 1 && gesture.slideEl) {
 | 
						|
      gesture.slideEl.classList.add(`${params.zoomedSlideClass}`);
 | 
						|
    } else if (zoom.scale <= 1 && gesture.slideEl) {
 | 
						|
      gesture.slideEl.classList.remove(`${params.zoomedSlideClass}`);
 | 
						|
    }
 | 
						|
    if (zoom.scale === 1) {
 | 
						|
      gesture.originX = 0;
 | 
						|
      gesture.originY = 0;
 | 
						|
      gesture.slideEl = undefined;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  function onTouchStart(e) {
 | 
						|
    const device = swiper.device;
 | 
						|
    if (!gesture.imageEl) return;
 | 
						|
    if (image.isTouched) return;
 | 
						|
    if (device.android && e.cancelable) e.preventDefault();
 | 
						|
    image.isTouched = true;
 | 
						|
    const event = evCache.length > 0 ? evCache[0] : e;
 | 
						|
    image.touchesStart.x = event.pageX;
 | 
						|
    image.touchesStart.y = event.pageY;
 | 
						|
  }
 | 
						|
  function onTouchMove(e) {
 | 
						|
    if (!eventWithinSlide(e) || !eventWithinZoomContainer(e)) return;
 | 
						|
    const zoom = swiper.zoom;
 | 
						|
    if (!gesture.imageEl) return;
 | 
						|
    if (!image.isTouched || !gesture.slideEl) return;
 | 
						|
    if (!image.isMoved) {
 | 
						|
      image.width = gesture.imageEl.offsetWidth;
 | 
						|
      image.height = gesture.imageEl.offsetHeight;
 | 
						|
      image.startX = getTranslate(gesture.imageWrapEl, 'x') || 0;
 | 
						|
      image.startY = getTranslate(gesture.imageWrapEl, 'y') || 0;
 | 
						|
      gesture.slideWidth = gesture.slideEl.offsetWidth;
 | 
						|
      gesture.slideHeight = gesture.slideEl.offsetHeight;
 | 
						|
      gesture.imageWrapEl.style.transitionDuration = '0ms';
 | 
						|
    }
 | 
						|
    // 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 = evCache.length > 0 ? evCache[0].pageX : e.pageX;
 | 
						|
    image.touchesCurrent.y = evCache.length > 0 ? evCache[0].pageY : e.pageY;
 | 
						|
    const touchesDiff = Math.max(Math.abs(image.touchesCurrent.x - image.touchesStart.x), Math.abs(image.touchesCurrent.y - image.touchesStart.y));
 | 
						|
    if (touchesDiff > 5) {
 | 
						|
      swiper.allowClick = false;
 | 
						|
    }
 | 
						|
    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;
 | 
						|
    const scaleRatio = (zoom.scale - currentScale) / (gesture.maxRatio - swiper.params.zoom.minRatio);
 | 
						|
    const {
 | 
						|
      originX,
 | 
						|
      originY
 | 
						|
    } = gesture;
 | 
						|
    image.currentX = image.touchesCurrent.x - image.touchesStart.x + image.startX + scaleRatio * (image.width - originX * 2);
 | 
						|
    image.currentY = image.touchesCurrent.y - image.touchesStart.y + image.startY + scaleRatio * (image.height - originY * 2);
 | 
						|
    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.style.transform = `translate3d(${image.currentX}px, ${image.currentY}px,0)`;
 | 
						|
  }
 | 
						|
  function onTouchEnd() {
 | 
						|
    const zoom = swiper.zoom;
 | 
						|
    if (!gesture.imageEl) 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.style.transitionDuration = `${momentumDuration}ms`;
 | 
						|
    gesture.imageWrapEl.style.transform = `translate3d(${image.currentX}px, ${image.currentY}px,0)`;
 | 
						|
  }
 | 
						|
  function onTransitionEnd() {
 | 
						|
    const zoom = swiper.zoom;
 | 
						|
    if (gesture.slideEl && swiper.activeIndex !== swiper.slides.indexOf(gesture.slideEl)) {
 | 
						|
      if (gesture.imageEl) {
 | 
						|
        gesture.imageEl.style.transform = 'translate3d(0,0,0) scale(1)';
 | 
						|
      }
 | 
						|
      if (gesture.imageWrapEl) {
 | 
						|
        gesture.imageWrapEl.style.transform = 'translate3d(0,0,0)';
 | 
						|
      }
 | 
						|
      gesture.slideEl.classList.remove(`${swiper.params.zoom.zoomedSlideClass}`);
 | 
						|
      zoom.scale = 1;
 | 
						|
      currentScale = 1;
 | 
						|
      gesture.slideEl = undefined;
 | 
						|
      gesture.imageEl = undefined;
 | 
						|
      gesture.imageWrapEl = undefined;
 | 
						|
      gesture.originX = 0;
 | 
						|
      gesture.originY = 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  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}, swiper-slide`);
 | 
						|
      }
 | 
						|
      if (!gesture.slideEl) {
 | 
						|
        if (swiper.params.virtual && swiper.params.virtual.enabled && swiper.virtual) {
 | 
						|
          gesture.slideEl = elementChildren(swiper.slidesEl, `.${swiper.params.slideActiveClass}`)[0];
 | 
						|
        } else {
 | 
						|
          gesture.slideEl = swiper.slides[swiper.activeIndex];
 | 
						|
        }
 | 
						|
      }
 | 
						|
      let imageEl = gesture.slideEl.querySelector(`.${params.containerClass}`);
 | 
						|
      if (imageEl) {
 | 
						|
        imageEl = imageEl.querySelectorAll('picture, img, svg, canvas, .swiper-zoom-target')[0];
 | 
						|
      }
 | 
						|
      gesture.imageEl = imageEl;
 | 
						|
      if (imageEl) {
 | 
						|
        gesture.imageWrapEl = elementParents(gesture.imageEl, `.${params.containerClass}`)[0];
 | 
						|
      } else {
 | 
						|
        gesture.imageWrapEl = undefined;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (!gesture.imageEl || !gesture.imageWrapEl) return;
 | 
						|
    if (swiper.params.cssMode) {
 | 
						|
      swiper.wrapperEl.style.overflow = 'hidden';
 | 
						|
      swiper.wrapperEl.style.touchAction = 'none';
 | 
						|
    }
 | 
						|
    gesture.slideEl.classList.add(`${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.pageX;
 | 
						|
      touchY = e.pageY;
 | 
						|
    } else {
 | 
						|
      touchX = image.touchesStart.x;
 | 
						|
      touchY = image.touchesStart.y;
 | 
						|
    }
 | 
						|
    const forceZoomRatio = typeof e === 'number' ? e : null;
 | 
						|
    if (currentScale === 1 && forceZoomRatio) {
 | 
						|
      touchX = undefined;
 | 
						|
      touchY = undefined;
 | 
						|
    }
 | 
						|
    zoom.scale = forceZoomRatio || gesture.imageWrapEl.getAttribute('data-swiper-zoom') || params.maxRatio;
 | 
						|
    currentScale = forceZoomRatio || gesture.imageWrapEl.getAttribute('data-swiper-zoom') || params.maxRatio;
 | 
						|
    if (e && !(currentScale === 1 && forceZoomRatio)) {
 | 
						|
      slideWidth = gesture.slideEl.offsetWidth;
 | 
						|
      slideHeight = gesture.slideEl.offsetHeight;
 | 
						|
      offsetX = elementOffset(gesture.slideEl).left + window.scrollX;
 | 
						|
      offsetY = elementOffset(gesture.slideEl).top + window.scrollY;
 | 
						|
      diffX = offsetX + slideWidth / 2 - touchX;
 | 
						|
      diffY = offsetY + slideHeight / 2 - touchY;
 | 
						|
      imageWidth = gesture.imageEl.offsetWidth;
 | 
						|
      imageHeight = gesture.imageEl.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;
 | 
						|
    }
 | 
						|
    if (forceZoomRatio && zoom.scale === 1) {
 | 
						|
      gesture.originX = 0;
 | 
						|
      gesture.originY = 0;
 | 
						|
    }
 | 
						|
    gesture.imageWrapEl.style.transitionDuration = '300ms';
 | 
						|
    gesture.imageWrapEl.style.transform = `translate3d(${translateX}px, ${translateY}px,0)`;
 | 
						|
    gesture.imageEl.style.transitionDuration = '300ms';
 | 
						|
    gesture.imageEl.style.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 = elementChildren(swiper.slidesEl, `.${swiper.params.slideActiveClass}`)[0];
 | 
						|
      } else {
 | 
						|
        gesture.slideEl = swiper.slides[swiper.activeIndex];
 | 
						|
      }
 | 
						|
      let imageEl = gesture.slideEl.querySelector(`.${params.containerClass}`);
 | 
						|
      if (imageEl) {
 | 
						|
        imageEl = imageEl.querySelectorAll('picture, img, svg, canvas, .swiper-zoom-target')[0];
 | 
						|
      }
 | 
						|
      gesture.imageEl = imageEl;
 | 
						|
      if (imageEl) {
 | 
						|
        gesture.imageWrapEl = elementParents(gesture.imageEl, `.${params.containerClass}`)[0];
 | 
						|
      } else {
 | 
						|
        gesture.imageWrapEl = undefined;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (!gesture.imageEl || !gesture.imageWrapEl) return;
 | 
						|
    if (swiper.params.cssMode) {
 | 
						|
      swiper.wrapperEl.style.overflow = '';
 | 
						|
      swiper.wrapperEl.style.touchAction = '';
 | 
						|
    }
 | 
						|
    zoom.scale = 1;
 | 
						|
    currentScale = 1;
 | 
						|
    gesture.imageWrapEl.style.transitionDuration = '300ms';
 | 
						|
    gesture.imageWrapEl.style.transform = 'translate3d(0,0,0)';
 | 
						|
    gesture.imageEl.style.transitionDuration = '300ms';
 | 
						|
    gesture.imageEl.style.transform = 'translate3d(0,0,0) scale(1)';
 | 
						|
    gesture.slideEl.classList.remove(`${params.zoomedSlideClass}`);
 | 
						|
    gesture.slideEl = undefined;
 | 
						|
    gesture.originX = 0;
 | 
						|
    gesture.originY = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  // 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 passiveListener = swiper.params.passiveListeners ? {
 | 
						|
      passive: true,
 | 
						|
      capture: false
 | 
						|
    } : false;
 | 
						|
    const activeListenerWithCapture = swiper.params.passiveListeners ? {
 | 
						|
      passive: false,
 | 
						|
      capture: true
 | 
						|
    } : true;
 | 
						|
    return {
 | 
						|
      passiveListener,
 | 
						|
      activeListenerWithCapture
 | 
						|
    };
 | 
						|
  }
 | 
						|
 | 
						|
  // Attach/Detach Events
 | 
						|
  function enable() {
 | 
						|
    const zoom = swiper.zoom;
 | 
						|
    if (zoom.enabled) return;
 | 
						|
    zoom.enabled = true;
 | 
						|
    const {
 | 
						|
      passiveListener,
 | 
						|
      activeListenerWithCapture
 | 
						|
    } = getListeners();
 | 
						|
 | 
						|
    // Scale image
 | 
						|
    swiper.wrapperEl.addEventListener('pointerdown', onGestureStart, passiveListener);
 | 
						|
    swiper.wrapperEl.addEventListener('pointermove', onGestureChange, activeListenerWithCapture);
 | 
						|
    ['pointerup', 'pointercancel', 'pointerout'].forEach(eventName => {
 | 
						|
      swiper.wrapperEl.addEventListener(eventName, onGestureEnd, passiveListener);
 | 
						|
    });
 | 
						|
 | 
						|
    // Move image
 | 
						|
    swiper.wrapperEl.addEventListener('pointermove', onTouchMove, activeListenerWithCapture);
 | 
						|
  }
 | 
						|
  function disable() {
 | 
						|
    const zoom = swiper.zoom;
 | 
						|
    if (!zoom.enabled) return;
 | 
						|
    zoom.enabled = false;
 | 
						|
    const {
 | 
						|
      passiveListener,
 | 
						|
      activeListenerWithCapture
 | 
						|
    } = getListeners();
 | 
						|
 | 
						|
    // Scale image
 | 
						|
    swiper.wrapperEl.removeEventListener('pointerdown', onGestureStart, passiveListener);
 | 
						|
    swiper.wrapperEl.removeEventListener('pointermove', onGestureChange, activeListenerWithCapture);
 | 
						|
    ['pointerup', 'pointercancel', 'pointerout'].forEach(eventName => {
 | 
						|
      swiper.wrapperEl.removeEventListener(eventName, onGestureEnd, passiveListener);
 | 
						|
    });
 | 
						|
 | 
						|
    // Move image
 | 
						|
    swiper.wrapperEl.removeEventListener('pointermove', 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
 | 
						|
  });
 | 
						|
} |