React滑动条扫描图片组件(.tsx|React滑动条扫描图片组件(.tsx + .less, hooks组件)

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 (测试拖拽组件); }const SwiperWindow = ({ leftSrchttps://www.it610.com/article/= "", rightSrchttps://www.it610.com/article/= "" }) => { 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 (after{/* 滑动条 */} onMouseDown(e)} onMouseUp={(e) => onMouseUp(e)} className="slider" style={{ left: sliderLeft }} >beforeafter); };

【React滑动条扫描图片组件(.tsx|React滑动条扫描图片组件(.tsx + .less, hooks组件)】.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; //不可拖动 } } }

    推荐阅读