react-konva 如何实现 根据以鼠标为中心缩放图片?-灵析社区

乘03060920

原生canvas 可以实现 ,但是用的 konva 库, 不知道怎么做了就 需求是 需要想百度地图 高德地图哪种类似的 以所选区域为中心缩放 [https://codesandbox.io/s/affectionate-resonance-2f7lhn?file=/...](https://link.segmentfault.com/?enc=meYWzb9QIseAAc2IP%2BEyEQ%3D%3D.EeYvE9tlpVgDs4qT5ifROb9WCFzOYpWFWk4698k2MudaJlbOS14caLrBx4u5zf6OJPLofP%2BsWS3kVKMs%2FkrdnymE7n%2FhQ7j3RMwipYBw%2FQI%3D) import { useEffect, useRef, useState } from "react" import { useSelector } from "react-redux" import { Stage, Layer, Group, Image } from "react-konva" import { vec2 } from "gl-matrix" import styled from "styled-components" import Konva from "konva" import useImage from "use-image" import type { KonvaEventObject } from "konva/lib/Node" import type { RootState } from "@/stores" const ScStage = styled(Stage)` width: 100%; height: calc(100% - 100px); overflow: hidden; ` const StageContainer2 = () => { const { imgUrl } = useSelector((state: RootState) => state.measure) const { cursor } = useSelector((state: RootState) => state.cursor) const { lateral } = useSelector((state: RootState) => state.showPoint) const { rotate, scaleX, contrast, brightness } = useSelector((state: RootState) => state.transform) const [scale, setScale] = useState(1) const [width, setWidth] = useState(1000) const [height, setHeight] = useState(1000) const [image] = useImage(imgUrl) const stageRef = useRef(null) const layerRef = useRef(null) const imageRef = useRef(null) const [offsetX, setOffsetX] = useState(0) const [offsetY, setOffsetY] = useState(0) const max = 4 // 放大最大的比例 const min = 0.5 // 缩小最小的比例 const step = 0.03 // 每次缩放的比例 function onWheel(e: KonvaEventObject) { const x = e.evt.offsetX const y = e.evt.offsetY const offsetX = (x - layerRef.current.offsetX()) * layerRef.current.scaleX() / (layerRef.current.scaleX() - step) - (x - layerRef.current.offsetX()) const offsetY = (y - layerRef.current.offsetY()) * layerRef.current.scaleY() / (layerRef.current.scaleY() - step) - (y - layerRef.current.offsetY()) // 这里写的不是很对 if (e.evt.wheelDelta && e.evt.wheelDelta > 0) { // 放大 if (layerRef.current.scaleX() min && layerRef.current.scaleY() > min) { layerRef.current.scaleX(layerRef.current.scaleX() - step) layerRef.current.scaleY(layerRef.current.scaleY() - step) layerRef.current.move({ x: offsetX, y: offsetY }) // 跟随鼠标偏移位置 } } // const stage = e.target.getStage() // const mousePos = stage?.getPointerPosition()! // // const imagePos = imageRef.current?.position()! } useEffect(() => { const stage = stageRef.current?.getStage() const stageWrapper = stageRef.current!.attrs.container setStageSize() function setStageSize() { stage?.width(stageWrapper.clientWidth) stage?.height(stageWrapper.clientHeight) setWidth(stageWrapper.clientWidth) setHeight(stageWrapper.clientHeight) } }, [stageRef]) return ( {lateral && ( )} ) } export default StageContainer2

阅读量:28

点赞量:0

问AI
解决这个问题,首先要逆变换求出鼠标点击的位置在变换前的位置,然后将这个位置作为新的变换坐标系原点(原点在线性变换中是不动点,利用这个性质,就可以做出跟随鼠标的效果)。 如果图形已经发生过变换,修改原点会导致“跃变”效果,也就是在鼠标开始动的瞬间图形会跑掉(把"setPosition"去掉就可以重现这种现象)。因此需要对“跃变”的副作用进行修正,也就是在修改原点的同时,把图像“拽”回原位置。 const [scale, setScale] = useState(2); const [offset, setOffset] = useState({ x: 80, y: 80, }); const [position, setPosition] = useState({ x: 0, y: 0 }); const wheelCallback = useCallback( ({ //@ts-ignore evt: { layerX, layerY, deltaY }, }: Konva.KonvaEventObject) => { if (!shapeRef.current) return; const currentTransform = shapeRef.current.getTransform(); const currentTransformMatrix = [...currentTransform.getMatrix()]; const { x, y, offsetX, offsetY } = shapeRef.current.attrs; // 当前的变换坐标原点为 [offsetX, offsetY], 为不变点 // 鼠标点击的位置 const currentViewCursorInLayer = { x: layerX, y: layerY, }; // 当前变换矩阵求逆,以便还原鼠标位置在变换前的真实位置 const currentTransformInvert = currentTransform.copy().invert(); // 逆变换求鼠标位置变换前的位置,所以是 Actual ,该位置将作为新的变换原点 // 该位置实际上是在以 [x, y] 为原点的坐标系中表达的,所以是 InShape const currentActualCursorInShape = currentTransformInvert.point( currentViewCursorInLayer ); // 其实赋值前后是完全等效的,这里为了避免困惑,故重命名 const nextOffset = { x: currentActualCursorInShape.x, y: currentActualCursorInShape.y, }; // 修改原点 setOffset(nextOffset); // 跟随滚轮缩放 if (deltaY 0.1) { setScale(scale - 0.1); } else { setScale(0.1); } // 原点位置变更,变更的矢量由旧原点指向新原点,直接移动一下好了 const nextMatrix = [...currentTransformMatrix]; const nextTransform = new Konva.Transform(nextMatrix).translate( -(nextOffset.x - offsetX), -(nextOffset.y - offsetY) ); // 修改原点,会导致位置突变,需要计算突变发生后,鼠标相对于图像的位置会被移动到何处 // 变换只是视觉效果,所以是 View const nextViewCursorInLayer = nextTransform.point(nextOffset); // 鼠标位置在两次变化间的移动矢量 const cursorMoveVector = { x: layerX - nextViewCursorInLayer.x, y: layerY - nextViewCursorInLayer.y, }; // 使用前述移动矢量,修正位置 setPosition({ x: x + cursorMoveVector.x, y: y + cursorMoveVector.y }); }, [scale, shapeRef] ); return «👆这里用的 Rect ,方便测试,Image 差别应该不大。» 以上代码在单层未变换 "Layer" 中测试可用,但没有测试过复杂的复合变换,注意"shapeRef"要作为"Group"的属性,而非"Image"。