Html5 Canvas实现图片标记、缩放、移动和保存历史状态功能 (附转换公式)
这个就需要用到我们之前推导出来的公式啦!因为呢,仔细想一下,如果我们缩放位移之后,我们鼠标按下的位置,他的坐标可能就相对于画布来说会有变化, 所以我们需要转换一下才能进行鼠标按下的位置与画布的位置一一对应的效果 稍微总结一下: 传入鼠标按下的坐标 通过公式转换,开始在对应坐标下绘制 鼠标抬起时,取消事件监听 // 利用公式转换一下坐标 const generateLinePoint = (x: number, y: number) => { const { current: wrap } = wrapRef const { current: translatePointX } = translatePointXRef const { current: translatePointY } = translatePointYRef const wrapWidth: number = wrap?.offsetWidth || 0 const wrapHeight: number = wrap?.offsetHeight || 0 // 缩放位移坐标变化规律 // (transformOrigin - downX) / scale * (scale-1) + downX - translateX = pointX const pointX: number = ((wrapWidth / 2) - x) / canvasScale * (canvasScale - 1) + x - translatePointX const pointY: number = ((wrapHeight / 2) - y) / canvasScale * (canvasScale - 1) + y - translatePointY return { pointX, pointY } } // 监听鼠标画笔事件 const handleLineMode = (downX: number, downY: number) => { const { current: canvas } = canvasRef const { current: wrap } = wrapRef const context: CanvasRenderingContext2D | undefined | null = canvas?.getContext('2d') if (!canvas || !wrap || !context) return const offsetLeft: number = canvas.offsetLeft const offsetTop: number = canvas.offsetTop // 减去画布偏移的距离(以画布为基准进行计算坐标) downX = downX - offsetLeft downY = downY - offsetTop const { pointX, pointY } = generateLinePoint(downX, downY) context.globalCompositeOperation = "source-over" context.beginPath() // 设置画笔起点 context.moveTo(pointX, pointY) canvas.onmousemove = null canvas.onmousemove = (event: MouseEvent) => { const moveX: number = event.pageX - offsetLeft const moveY: number = event.pageY - offsetTop const { pointX, pointY } = generateLinePoint(moveX, moveY) // 开始绘制画笔线条~ context.lineTo(pointX, pointY) context.stroke() } canvas.onmouseup = () => { context.closePath() canvas.onmousemove = null canvas.onmouseup = null } } 7. 橡皮擦的实现 橡皮擦目前还有点问题,现在的话是通过将 canvas 画布的背景图片 + globalCompositeOperation 这个属性来模拟橡皮擦的实现,不过,这时候图片生成出来之后,橡皮擦的痕迹会变成白色,而不是透明 此步骤与画笔实现差不多,只有一点点小变动 设置属性 context.globalCompositeOperation = "destination-out" // 目前橡皮擦还有点问题,前端显示正常,保存图片下来,擦除的痕迹会变成白色 const handleEraserMode = (downX: number, downY: number) => { const { current: canvas } = canvasRef const { current: wrap } = wrapRef const context: CanvasRenderingContext2D | undefined | null = canvas?.getContext('2d') if (!canvas || !wrap || !context) return const offsetLeft: number = canvas.offsetLeft const offsetTop: number = canvas.offsetTop downX = downX - offsetLeft downY = downY - offsetTop const { pointX, pointY } = generateLinePoint(downX, downY) context.beginPath() context.moveTo(pointX, pointY) canvas.onmousemove = null canvas.onmousemove = (event: MouseEvent) => { const moveX: number = event.pageX - offsetLeft const moveY: number = event.pageY - offsetTop const { pointX, pointY } = generateLinePoint(moveX, moveY) context.globalCompositeOperation = "destination-out" context.lineWidth = lineWidth context.lineTo(pointX, pointY) context.stroke() } canvas.onmouseup = () => { context.closePath() canvas.onmousemove = null canvas.onmouseup = null } } 8. 撤销与恢复的功能实现 这个的话,我们首先需要了解常见的撤销与恢复的功能的逻辑 分几种情况吧 若当前状态处于第一个位置,则不允许撤销 若当前状态处于最后一个位置,则不允许恢复 如果当前撤销了,然而更新了状态,则取当前状态为最新的状态(也就是说不允许恢复了,这个刚更新的状态就是最新的) 画布状态的更新 所以我们需要设置一些变量来存,状态列表,与当前画笔的状态下标 // 定义参数存东东 const canvasHistroyListRef: MutableRefObject<ImageData[]> = useRef([]) const [canvasCurrentHistory, setCanvasCurrentHistory] = useState<number>(0) 我们还需要在初始化canvas的时候,我们就添加入当前的状态存入列表中,作为最先开始的空画布状态 (编辑:威海站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |