Zoom with different scales in swiper

Viewed 283

Is there any way to zoom.in() and zoom.out() at different levels of zoom, instead of have zoom (zoom.in) or not have zoom (zoom.out)? The problem is that I want different levels of zoom, like 100%-150%-200%-250%-300% (100% = minRatio, 300% = maxRatio), when I'm zooming, and not to get the maxRatio zooming when I zoom.in(), I would like to be able to go by steps.

I've found this solution: Zoom in on a mousewheel point (using scale and translate) using js + css, but I think that this feature should be included in the library, instead of doing it by hand.

1 Answers

I needed to achieve this for a React app and managed to do so writing a custom module which extended the Zoom module functionality - sure you could use a similar approach for whatever implementation you needed...

// ExtendedZoom.ts

const ExtendedZoom = (_ref: Ref) => {
  const { swiper } = _ref;
  const gesture: GestureElements = {};

  const setZoom = (scale: number) => {
    gesture.slideEl = swiper.slides[swiper.activeIndex] as HTMLDivElement;

    if (!swiper.params || typeof swiper.params.zoom !== 'object') return;

    gesture.imageEl = gesture.slideEl.querySelector(
      `.${swiper.params.zoom.containerClass} img`,
    ) as HTMLImageElement;
    gesture.imageWrapEl = gesture.imageEl.closest(
      `.${swiper.params.zoom.containerClass}`,
    ) as HTMLDivElement;

    if (swiper.params.zoom.zoomedSlideClass) {
      gesture.slideEl.classList.add(swiper.params.zoom.zoomedSlideClass);
    }

    swiper.zoom.scale = scale;

    gesture.imageWrapEl.style.transition = '0.3s';
    gesture.imageWrapEl.style.transform = 'translate(0, 0)';

    gesture.imageEl.style.transition = '0.3s';
    gesture.imageEl.style.transform = `translate(0, 0) scale(${swiper.zoom.scale})`;
  };

  swiper.zoom.setZoom = setZoom;
};

export default ExtendedZoom;
// ComponentUsingSwiper.tsx

import React, { useState, useMemo } from 'react';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Keyboard, Zoom } from 'swiper';
import { ZoomOptions } from 'swiper/types';

import ExtendedZoom from './ExtendedZoom';

const WithZoomModules = [Keyboard, Zoom, ExtendedZoom];
const MAX_ZOOM = 4;

const WithIncrementalZoom: React.FC<{ images: Image[] }> = ({
  images,
}) => {
  const [swipeNavEnabled, setSwipeNavEnabled] = useState(true);
  const [activeZoomScale, setActiveZoomScale] = useState(1);

  const zoomConfig = useMemo<ZoomOptions>(
    () => ({
      containerClass: 'swiper-zoom-container',
      minRatio: 1,
      maxRatio: activeZoomScale < MAX_ZOOM ? activeZoomScale + 1 : MAX_ZOOM,
    }),
    [activeZoomScale],
  );

  const onZoomChange = (swiper: SwiperInstanceProps, scale: number) => {
    if (scale === 1 && !swipeNavEnabled) setSwipeNavEnabled(true);
    if (scale > 1 && swipeNavEnabled) setSwipeNavEnabled(false);

    setActiveZoomScale(scale);
  };

  return (
    <Swiper
      slidesPerView={1}
      modules={WithZoomModules}
      loop
      keyboard
      onSlideChange={onSlideChange}
      zoom={zoomConfig}
      allowTouchMove={swipeNavEnabled}
      onZoomChange={onZoomChange}
    >
      {images.map((image) => (
         <SwiperSlide key={image.href}>
           <div className="swiper-zoom-container">
             <img src={image.href} alt={image.caption} />
           </div>
         </SwiperSlide>
      ))}
      <ZoomControls maxZoom={MAX_ZOOM} activeZoomScale={activeZoomScale} />
   </Swiper>
  );
};
// ZoomControls.tsx

const ZoomControls: React.FC<{ maxZoom: number, activeZoomScale: number }> = ({ maxZoom, activeZoomScale }) => {

  const swiper = useSwiper();

  const zoomIn = () => {
    if (activeZoomScale < maxZoom) {
      swiper.zoom.setZoom(activeZoomScale + 1);
    }
  };

  const zoomOut = () => {
    if (activeZoomScale > 1) {
      swiper.zoom.setZoom(activeZoomScale - 1);
    }
  };

  return (
    <>
      <button type="button" onClick={zoomIn}>Zoom In</button>
      <button type="button" onClick={zoomOut}>Zoom Out</button>
    </>
  );
}
Related