ps:滑动条款式间接搬运的下方

https://www.chaos.com/blog/ho...

组件实现局部重点:

  1. visibility: "hidden"一份左半的图片兜底占位,左右两半图片都相对定位。
  2. 配置滑动条的鼠标下按、抬起事件,并在鼠标下按时监听mousemove事件。
  3. 为了实现“即便鼠标挪动到滚动条、浏览器外、浏览器内的f12控制台处松开”也能勾销鼠标拖拽————事件监听间接配在了document上,而不是document.body。
  4. 图片缩放的时候,按比例变动鼠标程度挪动量moveX,实现绝对静止滑动条地位。
  5. 为了能拿到正确的鼠标挪动值,“上一次鼠标x地位”cursorX没有存成state,仅仅是一个一般的全局变量。

源码

.tsx

import React, { useState, useEffect } from "react";import "./styles.less";export default function App() {  return (    <div className="container">      测试拖拽组件      <SwiperWindow        leftSrc="//z3.ax1x.com/2021/09/24/407zfs.jpg"        rightSrc="//s4.ax1x.com/2021/12/29/T62gcF.jpg"      />    </div>  );}const SwiperWindow = ({ leftSrc = "", rightSrc = "" }) => {  let cursorX = 0;  const [boxState, setBoxState] = useState({    boxW: 0,    boxH: 0  });  const [sliderW, setSliderW] = useState(40); // 滑动条的宽度  const INITLEFT = -(sliderW / 2);  const [sliderLeft, setSliderLeft] = useState(INITLEFT);  const [sliderMax, setSliderMax] = useState(0);  const [moveX, setMoveX] = useState(0);  const [scale, setScale] = useState(1);  useEffect(() => {    setSliderW(document.querySelector(".slider")?.clientWidth || 40);    setSize(true);    return () => {      document.removeEventListener("mousemove", onMouseMove);      document.removeEventListener("mouseup", onMouseUp);    };  }, []);  useEffect(() => {    if (boxState.boxW) {      setSize(false);    }  }, [document.querySelector(".scanBox")?.clientWidth, boxState.boxW]);  const setSize = (init: boolean = false) => {    let new_boxW = document.querySelector(".scanBox")?.clientWidth;    let new_boxH = document.querySelector(".scanBox")?.clientHeight;    const Half = sliderW / 2;    if (new_boxW) {      const new_sliderMax = new_boxW - Half;      setSliderMax(new_sliderMax);      if (init) {        setSliderLeft(INITLEFT + new_boxW / 2);        setMoveX(new_boxW / 2);        setScale(new_boxW / 2 / new_boxW);      } else if (moveX) {        let new_sliderLeft = INITLEFT + new_boxW * scale;        const Half = sliderW / 2;        const MIN = -Half;        const MAX = new_sliderMax;        if (new_sliderLeft <= MIN) {          new_sliderLeft = MIN;        }        if (new_sliderLeft >= MAX) {          new_sliderLeft = MAX;        }        setSliderLeft(new_sliderLeft);      }      setBoxState(        Object.assign({          boxW: new_boxW,          boxH: new_boxH        })      );    }  };  const onMouseDown = (e: any) => {    let new_cursorX = e.clientX;    cursorX = new_cursorX;    document.addEventListener("mousemove", onMouseMove);    document.addEventListener("mouseup", onMouseUp);  };  const onMouseMove = (e: any) => {    // 鼠标挪动间隔    let moveX = e.clientX - cursorX;    setMoveX(moveX);    let new_sliderLeft = sliderLeft + moveX;    setScale((new_sliderLeft - INITLEFT) / boxState.boxW);    const Half = sliderW / 2;    const MIN = -Half;    const MAX = sliderMax;    if (new_sliderLeft <= MIN) {      new_sliderLeft = MIN;    }    if (new_sliderLeft >= MAX) {      new_sliderLeft = MAX;    }    setSliderLeft(new_sliderLeft);  };  // 当鼠标弹起触发事件  const onMouseUp = (e: any) => {    cursorX = e.clientX;    document.removeEventListener("mousemove", onMouseMove);    document.removeEventListener("mouseup", onMouseUp);  };  const { boxW, boxH } = boxState;  return (    <div className="scanBox noSelect">      <div        className="left noSelect"        style={{          width: sliderLeft + sliderW / 2        }}      >        <img          alt="after"          src={leftSrc}          className="leftPic"          style={{            width: boxW,            height: "auto"          }}        />      </div>      {/* 滑动条 */}      <div        onMouseDown={(e) => onMouseDown(e)}        onMouseUp={(e) => onMouseUp(e)}        className="slider"        style={{          left: sliderLeft        }}      >        <div className="line-top"></div>        <div className="round">          <div className="arrow-left"></div>          <div className="arrow-right"></div>        </div>        <div className="line-bottom"></div>      </div>      <div className="right noSelect">        <img          alt="before"          src={rightSrc}          className="rightPic"          style={{            height: "auto"          }}        />      </div>      <img        alt="after"        src={leftSrc}        className="leftPic"        style={{          width: boxW,          height: "auto",          visibility: "hidden"        }}      />    </div>  );};

.less

.container {  max-width: 1200px;  margin: auto;  width: 100%;  height: auto;}.noSelect {  user-select: none;}.scanBox {  position: relative;  width: inherit;  overflow: hidden;}.scanBox {  .slider {    align-items: center;    cursor: ew-resize;    display: flex;    flex-direction: column;    height: 100%;    justify-content: center;    left: 268.768px;    position: absolute;    top: 0px;    width: 40px;    z-index: 5;    .line-top {      background: #ffffff;      box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2),        0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12);      flex: 0 1 auto;      height: 100%;      width: 2px;    }    .line-bottom {      background: #ffffff;      box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2),        0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12);      flex: 0 1 auto;      height: 100%;      width: 2px;    }    .round {      align-items: center;      border: 2px solid #ffffff;      border-radius: 100%;      box-shadow: 0px 3px 1px -2px rgb(0 0 0 / 20%),        0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%);      box-sizing: border-box;      display: flex;      flex: 1 0 auto;      height: 40px;      justify-content: center;      width: 40px;      transform: none;      .arrow-left {        border: inset 6px rgba(0, 0, 0, 0);        border-right: 6px solid #ffffff;        height: 0px;        margin-left: -10px;        margin-right: 10px;        width: 0px;      }      .arrow-right {        border: inset 6px rgba(0, 0, 0, 0);        border-left: 6px solid #ffffff;        height: 0px;        margin-right: -10px;        width: 0px;      }    }  }  .left {    position: absolute;    top: 0;    left: 0;    z-index: 2;    width: 100%;    height: 100%;    overflow: hidden;    .leftPic {      width: 100%;      height: auto;      -webkit-user-drag: none; //不可拖动    }  }  .right {    position: absolute;    top: 0;    left: 0px;    z-index: 1;    width: 100%;    height: 100%;    overflow: hidden;    .rightPic {      width: 100%;      height: auto;      -webkit-user-drag: none; //不可拖动    }  }}