如何优雅地实现图片局部预览组件?-灵析社区

今天吃什么你说吧

如何优雅地实现图片局部预览组件? 下面是[bilibili用户头像上传](https://link.segmentfault.com/?enc=cILJdevgB%2Ba74Eb%2FpXXOww%3D%3D.2ZtA42N7SwiXfvc3VGpBKuALaguBU4%2Bt%2ByQClylY4tF3JN3FyaC%2BP1TmBUiiSKtFKCSelalRQ9OIEva%2Fj3Ikyw%3D%3D)的界面,左侧我们可以通过移动选择框和缩放选择框来选择图片的某一部分,右侧是头像预览。 ![image.png](https://wmprod.oss-cn-shanghai.aliyuncs.com/c/user/20240930/f1a895ce7a04bcfd84380447d9c376f5.png) 我自己尝试使用`react+tailwindcss`来实现了一下,但是实现的并不太理想。 * 通过四个角来缩放的效果并不好。有时候没有反应,有时候又突然之间变换很大。 * 应该是当我按下鼠标之后,抬起鼠标之前,才能移动和缩放选择框。但是有时候,抬起了依然可以移动选择框。 * 鼠标移动操作的速度一快,就会出现一些意料之外的效果(如前两条所示)。 * 性能也不太好,处理了太多的`mousemove`事件了(可以考虑加上防抖?)。 **可以在我实现的基础上给出一些改进建议或者直接重新给出一个新的方案。希望可以尽可能详细,有完整的代码,最终的效果可以交互。** 简要说明一下我的实现思路: HTML结构 * `image`和`选择框`为兄弟元素。它们的父元素为`相对定位`,选择框为`绝对定位`。父元素的宽度固定,`image`的最大宽度为`100%`,父元素的高度由`image`的大小决定。 * `选择框`中有四个元素,分别表示四个角上的小方框,采用的也是`绝对定位` setPress({ ...press, topLeft: true })} > setPress({ ...press, topRight: true })} > setPress({ ...press, bottomLeft: true })} > setPress({ ...press, bottomRight: true })} > 状态 * 鼠标是否在选择框中按下。用一个对象来记录,并设置了5个属性,以区分按下的位置。根据按下的位置不同,鼠标移动操作时的行为也不同,是移动选择框还是缩放选择框,缩放的话该怎么缩放。 * 鼠标的位置。通过记录相邻两次鼠标的位置并计算其差值来决定选择框应移动多远的距离。 * `imagRef`用来获取图片渲染之后的`width`、`height`、`naturalWdith`、`naturalHeight`,在计算图片缩放比例、选择框越界判断的时候有用。 * `selectorRef`获取选择框的`width`和`height`。 const [press, setPress] = React.useState({ topLeft: false, topRight: false, bottomLeft: false, bottomRight: false, other: false, }); const [mousPosition, setMousePosition] = React.useState(null); const [selector, setSelector] = React.useState({ x: 0, y: 0, width: null, height: null, }); const imgRef = React.useRef(); const selectorRef = React.useRef(); 事件处理函数 * `mouseDownUp`重置`press`状态。 * `mouseDownKey`设置对应的`press`状态,设置鼠标的位置。 * 主要逻辑在`mouseMove`上。 * 根据按下位置的不同,更新选择框的位置和大小。 * 防止选择框越界。 * 这里的`onChange`函数主要用来设置一些用于在canvas上绘制图片时所需的信息(`drawImage(img, x, y, sw, sh, dx, dy, dw, dh`)。 * 设置鼠标的位置。 function handleMouseMove(event) { if (Object.values(press).every((e) => !e)) return; const { clientX, clientY } = event; const offsetX = clientX - mousPosition.x; const offsetY = clientY - mousPosition.y; let newX; let newY; let newWidth; let newHeight; if (press.topLeft) { console.log('press top left'); // bottom right don't change newX = selector.x + offsetX; newY = selector.y + offsetY; newWidth = selector.width - offsetX; newHeight = selector.height - offsetY; if (newWidth imgRef.current.width) newX = imgRef.current.width - selector.width; if (newX imgRef.current.height) newY = imgRef.current.height - selector.height; if (newY < 0) newY = 0; if (newHeight < 0) newHeight = 0; if (newWidth < 0) newWidth = 0; setSelector({ ...selector, x: newX, y: newY, width: newWidth, height: newHeight, }); onChange( imgRef.current, newX * imgRef.current.xScale, newY * imgRef.current.yScale, selector.width * imgRef.current.xScale, selector.height * imgRef.current.yScale ); setMousePosition({ x: clientX, y: clientY }); } * [stackblitz](https://link.segmentfault.com/?enc=R7XEe8zJEdeF%2BZnUbB0leA%3D%3D.Wi1%2Be3fmEHg49jMNetZaHPWrTRoNOCc5j17U3iyThTHz5G036rIWFJHfr03H0DPULDRHmxSr%2BkQZYdL4BuWGNQ%3D%3D) * [github](https://link.segmentfault.com/?enc=YacX4EMYJcpguUIsQ5N6vg%3D%3D.HyXu1ALUJHUBFmt76oPZTJ8f0QD5GMHDtxPzFtB9GPcEa1UJddTaLM6lkF8PieeU)

阅读量:167

点赞量:0

问AI
之前看过类似功能的博文,希望对你有帮助:"https://juejin.cn/post/6844903955915341831" (https://link.segmentfault.com/?enc=a%2FG7IDsKTU7nxqgLuSO23g%3D%3D.fXlwaTupxZVR4C9cBsPOHHyiIqBhuAYiVuxNpXUcvPRy4u15Sr81x8hqCFavijnm)。用的是cropper.js