实现数据大屏图片地图滚动放大以及拖拽功能

357 阅读1分钟

效果图:

z8clb-3t8xg.gif

注:该案例使用的是地图,需要先将碎片的地图模块通过定位属性将其拼接为一个整体

废话不多说直接上代码:

index.tsx

import useDrag from '@/hooks/useDrag';
import useZoom from '@/hooks/useZoom';

const Index = () => {
    // 拖拽
    const { handleMouseDown, handleMouseMove, handleMouseUp } = useDrag();
    // 
    const { handleWheel, scale } = useZoom();

    const [mapActive, setMapActive] = useState(1);
    // 切换地图
    const handleMapChange = (value: number) => {
    console.log('value :>> ', value);
    setMapActive(value);
    };

    <div
        className={styles.map}
        onMouseDown={handleMouseDown}
        onMouseMove={handleMouseMove}
        onMouseUp={handleMouseUp}
        onWheel={handleWheel}
        style={{
          transform: `scale(${scale})`,
        }}
        >
        <div className={styles['map-item-1']}>
          <img
            src={mapActive === 1 ? mapmengheActive : mapmenghe}
            className={styles['map-item-img']}
            draggable="false"
          ></img>
          <div
            className={styles['map-item-mark']}
            onClick={() => {
              handleMapChange(1);
            }}
          >
            <div className={styles['mark-img']}>
              <CollectionBinsIcon />
            </div>
          </div>
        </div>
        <div className={styles['map-item-12']}>
          <img
            src={mapActive === 12 ? mapweicunActive : mapweicun}
            className={styles['map-item-img']}
            draggable="false"
          ></img>
          <div
            className={styles['map-item-mark']}
            onClick={() => {
              handleMapChange(12);
            }}
          >
            <div className={styles['mark-img']}>
              <CollectionBinsIcon />
            </div>
          </div>
        </div>
        <div className={styles['map-item-2']}>
          <img
            src={mapActive === 2 ? mapcunjiangActive : mapcunjiang}
            className={styles['map-item-img']}
            draggable="false"
          ></img>
          <div
            className={styles['map-item-mark']}
            onClick={() => {
              handleMapChange(2);
            }}
          >
            <div className={styles['mark-img']}>
              <CollectionBinsIcon />
            </div>
          </div>
        </div>
        <div className={styles['map-item-11']}>
          <img
            src={mapActive === 11 ? mapbinkaiActive : mapbinkai}
            className={styles['map-item-img']}
            draggable="false"
          ></img>
          <div
            className={styles['map-item-mark']}
            onClick={() => {
              handleMapChange(11);
            }}
          >
            <div className={styles['mark-img']}>
              <CollectionBinsIcon />
            </div>
          </div>
        </div>
        <div className={styles['map-item-3']}>
          <img
            src={mapActive === 3 ? mapxixiayeActive : mapxixiaye}
            className={styles['map-item-img']}
            draggable="false"
          ></img>
          <div
            className={styles['map-item-mark']}
            onClick={() => {
              handleMapChange(3);
            }}
          >
            <div className={styles['mark-img']}>
              <CollectionBinsIcon />
            </div>
          </div>
        </div>
        <div className={styles['map-item-4']}>
          <img
            src={mapActive === 4 ? maplonghutangActive : maplonghutang}
            className={styles['map-item-img']}
            draggable="false"
          ></img>
          <div
            className={styles['map-item-mark']}
            onClick={() => {
              handleMapChange(4);
            }}
          >
            <div className={styles['mark-img']}>
              <CollectionBinsIcon />
            </div>
          </div>
        </div>
        <div className={styles['map-item-5']}>
          <img
            src={mapActive === 5 ? mapxinqiaoActive : mapxinqiao}
            className={styles['map-item-img']}
            draggable="false"
          ></img>
          <div
            className={styles['map-item-mark']}
            onClick={() => {
              handleMapChange(5);
            }}
          >
            <div className={styles['mark-img']}>
              <CollectionBinsIcon />
            </div>
          </div>
        </div>
        <div className={styles['map-item-6']}>
          <img
            src={mapActive === 6 ? mapxuejiaActive : mapxuejia}
            className={styles['map-item-img']}
            draggable="false"
          ></img>
          <div
            className={styles['map-item-mark']}
            onClick={() => {
              handleMapChange(6);
            }}
          >
            <div className={styles['mark-img']}>
              <CollectionBinsIcon />
            </div>
          </div>
        </div>
        <div className={styles['map-item-7']}>
          <img
            src={mapActive === 7 ? mapluoxiActive : mapluoxi}
            className={styles['map-item-img']}
            draggable="false"
          ></img>
          <div
            className={styles['map-item-mark']}
            onClick={() => {
              handleMapChange(7);
            }}
          >
            <div className={styles['mark-img']}>
              <CollectionBinsIcon />
            </div>
          </div>
        </div>
        <div className={styles['map-item-8']}>
          <img
            src={mapActive === 8 ? maphehaiActive : maphehai}
            className={styles['map-item-img']}
            draggable="false"
          ></img>
          <div
            className={styles['map-item-mark']}
            onClick={() => {
              handleMapChange(8);
            }}
          >
            </span> */}
            <div style={{ left: 5 }} className={styles['mark-img']}>
              <CollectionBinsIcon />
            </div>
          </div>
        </div>
        <div className={styles['map-item-9']}>
          <img
            src={mapActive === 9 ? mapsanjingActive : mapsanjing}
            className={styles['map-item-img']}
            draggable="false"
          ></img>
          <div
            className={styles['map-item-mark']}
            onClick={() => {
              handleMapChange(9);
            }}
          >
            <div className={styles['mark-img']}>
              <CollectionBinsIcon />
            </div>
          </div>
        </div>
        <div className={styles['map-item-10']}>
          <img
            src={mapActive === 10 ? mapbenniuActive : mapbenniu}
            className={styles['map-item-img']}
            draggable="false"
          ></img>
          <div
            className={styles['map-item-mark']}
            onClick={() => {
              handleMapChange(10);
            }}
          >
            <div className={styles['mark-img']}>
              <CollectionBinsIcon />
            </div>
          </div>
        </div>
    </div>
}

index.scss

.container {
  width: 1920px;
  position: relative;
  height: 1080px;
  overflow: hidden;
  box-sizing: border-box;
  background-image: url(/src/assets/images/bg.jpg) no-repeat;
  .map {
    position: absolute;
    top: 0;
    left: 600px;
    background: transparent;
    width: 900px;
    height: 730px;
    overflow: hidden;
    z-index: 1;
    cursor: grab;
    transition: transform 0.3s ease;

    &>div {
      transform: scale(0.30);
      position: relative;
    }

    .map-item-mark {
      transform: scale(2.5);
      position: absolute;
      cursor: pointer;
      color: #fff;
      font-weight: 600;

      &>.mark-img {
        position: absolute;
        top: 25px;
        left: 20px;
        z-index: 3;
      }

      &>.mark-text {
        width: 100px;
        text-align: center;
        position: absolute;
        z-index: 3;
        left: 40px;
      }
    }

    .map-item-1 {
      position: absolute;
      top: -45px;
      right: -290px;

      &>.map-item-mark {
        position: absolute;
        top: 100px;
        left: 700px;
      }
    }

    .map-item-2 {
      position: absolute;
      top: 121px;
      right: -164px;
      z-index: 2;

      &>.map-item-mark {
        position: absolute;
        top: 140px;
        left: 210px;
      }
    }
    .map-item-11 {
      position: absolute;
      top: -34px;
      right: -239px;

      &>.map-item-mark {
        position: absolute;
        top: 38px;
        left: 253px;
      }
    }
    .map-item-12 {
      position: absolute;
      top: 8px;
      right: -212px;

      &>.map-item-mark {
        position: absolute;
        top: 190px;
        left: 540px;
      }
    }


    .map-item-3 {
      position: absolute;
      top: 109px;
      right: -18px;

      &>.map-item-mark {
        position: absolute;
        top: 115px;
        left: 380px;
      }
    }

    .map-item-4 {
      position: absolute;
      top: 251px;
      right: -160px;

      &>.map-item-mark {
        position: absolute;
        top: 86px;
        left: 368px;

        &>.mark-text {
          top: -10px;
        }
      }
    }

    .map-item-5 {
      position: absolute;
      top: 234px;
      right: -66px;
      z-index: 2;

      &>.map-item-mark {
        position: absolute;
        top: 36px;
        left: 310px;

        &>.mark-text {
          top: 5px;
        }
      }
    }

    .map-item-6 {
      position: absolute;
      top: 222px;
      right: -22px;
      z-index: 2;

      &>.map-item-mark {
        position: absolute;
        top: 215px;
        left: 510px;
      }
    }

    .map-item-7 {
      position: absolute;
      top: 192px;
      right: 4px;
      z-index: 2;

      &>.map-item-mark {
        position: absolute;
        top: 96px;
        left: 505px;

        &>.mark-text {
          left: -58px;
        }
      }
    }

    .map-item-8 {
      position: absolute;
      top: 366px;
      right: 52px;
      z-index: 2;

      &>.map-item-mark {
        position: absolute;
        top: 125px;
        left: 116px;

        &>.mark-text {
          top: -37px;
          left: -9px;
        }
      }
    }

    .map-item-9 {
      position: absolute;
      top: 349px;
      right: -169px;

      &>.map-item-mark {
        position: absolute;
        top: 208px;
        left: 277px;

        &>.mark-text {
          top: -31px;
          left: 29px;
        }
      }
    }

    .map-item-10 {
      position: absolute;
      top: 172px;
      right: 266px;
      z-index: 2;

      &>.map-item-mark {
        position: absolute;
        top: 126px;
        left: 330px;

        &>.mark-text {
          left: -58px;
        }
      }
    }

  }
}

/hook/useDrag.ts

import { useCallback, useState } from 'react';
// 拖拽
export default function useDrag() {
    const [isDragging, setIsDragging] = useState(false);
    const [initialPosition, setInitialPosition] = useState({ x: 0, y: 0 });
    const [initialMousePosition, setInitialMousePosition] = useState({ x: 0, y: 0 });
  
    const handleMouseDown = useCallback((e: { clientX: any; clientY: any; currentTarget: { offsetLeft: any; offsetTop: any; }; }) => {
      setIsDragging(true);
      setInitialMousePosition({ x: e.clientX, y: e.clientY });
      setInitialPosition({ x: e.currentTarget.offsetLeft, y: e.currentTarget.offsetTop });
    }, []);
  
    const handleMouseMove = useCallback(
        (    e: { clientX: number; clientY: number; currentTarget: { style: { left: string; top: string; }; }; }) => {
        if (isDragging) {
          const dx = e.clientX - initialMousePosition.x;
          const dy = e.clientY - initialMousePosition.y;
          e.currentTarget.style.left = `${initialPosition.x + dx}px`;
          e.currentTarget.style.top = `${initialPosition.y + dy}px`;
        }
      },
      [isDragging, initialMousePosition, initialPosition]
    );
  
    const handleMouseUp = useCallback(() => {
      setIsDragging(false);
    }, []);
  
    return {
      handleMouseDown,
      handleMouseMove,
      handleMouseUp,
    };
  }

/hook/useZoom.ts

import { useCallback, useState } from 'react';

// 缩放
export default function useZoom(initialScale = 1) {
  const [scale, setScale] = useState(initialScale);

  const handleWheel = useCallback(
      (    e: { deltaY: number; currentTarget: { style: { transform: string; }; }; }) => {
      if (e.deltaY !== 0) {
        const newScale = scale + (e.deltaY > 0 ? -0.05 : 0.05);
        setScale(newScale < 0.5 ? 0.5 : newScale); // 防止缩放过小
        e.currentTarget.style.transform = `scale(${newScale})`;
      }
    },
    [scale]
  );

  return {
    handleWheel,
    scale,
  };
}