当前位置: 首页 > Web前端 > HTML5

React实现了div的缩放、旋转、拖动9个控制点

时间:2023-04-05 20:51:17 HTML5

这段时间用一个canvas库实现的元素拖动控制非常好用。所以我用js+div来实现一个。我使用了react框架并实践了它。思路是在受控元素的四个边和四个角上增加8个控制点。拖动控制点时,判断拖动方向并计算偏移量。修改元素的顶部、左侧、宽度和高度。旋转函数是通过三角函数计算鼠标拖动后的角度。动态修改元素的旋转画板(舞台)要控制元素。我们先定义一个画板,规定元素只能在指定范围内变化。然后在画板中插入一个受控的div元素,定义为drawing-item类名。drawing-item需要在画板上绝对定位,八个方向的控制点。这是最简单的结构import"./Drawing.css"//东南西北、东北、西北、东南、西南constpoints=['e','w','s','n','ne','nw','se','sw']functionDrawing(){//constdata=useState()return{points.map(item=>

)}
}导出默认绘图;为它们全部添加样式效果图:拖拽元素结构布置好之后,就可以准备写函数了。首先我们来分析一下拖放缩放的主要功能是什么,拖放!拖放是一种常见且简单的功能。它需要绑定三个事件:onMouseDown(鼠标按下)、onMouseMove(移动)、onMouseUp(抬起)。我们先写拖放函数,实现画板中元素的位移。元素的位置移动只需要动态修改left和top,定义一个style对象添加const[style,setStyle]=useState({left:100,top:100,width:100,height:100})//html我们将drawing-wrap绑定到绘图板上以监听鼠标移动和抬起事件,将drawing-item绑定到监听鼠标按下事件。//鼠标按下functiononMouseDown(e){}//鼠标移动functiononMouseMove(){}//鼠标抬起functiononMouseUp(){}return{points.map(item=>
)}//我们为每个控制点添加了`onMouseDown`事件,当鼠标按下时,传入当前控制点的方向。当鼠标悬停在绘图项上并按下时。可以获得当前元素和鼠标的位置。OffsetOffset是指元素相对于父元素的偏移距离,得到元素相对于画板的偏移量。//元素相对于画板的当前位置。consttop=e.target.offsetTop;constleft=e.target.offsetLeft;//那么鼠标坐标就是constcY=e.clientY;//clientX是相对于可视区域的constcX=e.clientX;当鼠标按下时,需要保存当前鼠标位置和元素的位置。每当鼠标移动时。计算鼠标移动了多远。//constwrapStyleofthedrawingboard={left:100,top:100,width:500,height:500}const[style,setStyle]=useState({left:100,top:100,width:100,height:100})//初始数据,因为不需要重新渲染,所以useRefconstoriPos=useRef({top:0,//元素坐标left:0,cX:0,//鼠标坐标cY:0})constisDown=useRef(false)//鼠标按下functiononMouseDown(e){//防止事件冒泡e.stopPropagation();isDown.current=true;//元素相对于画板的当前位置。consttop=e.target.offsetTop;constleft=e.target.offsetLeft;//那么鼠标坐标就是constcY=e.clientY;//clientX是相对于可视化区域的constcX=e.clientX;oriPos.current={top,left,cX,cY}}//鼠标移动函数onMouseMove(e){//判断鼠标是否按住if(!isDown.current)return//元素位置=初始位置+鼠标偏移量consttop=oriPos.current.top+(e.clientY-oriPos.current.cY)constleft=oriPos.current.left+(e.clientX-oriPos.current.cX)setStyle({top,left})}//鼠标抬起StartfunctiononMouseUp(e){console.log(e,'onMouseUp');isDown.current=false;}看看效果。可以拖着跑,但是再拖一下,哎,范围限制还没加,加个限制函数onMouseMove(e){//判断鼠标是否按下if(!isDown.current)returnletnewStyle={...风格};//元素当前位置+偏移量consttop=oriPos.current.top+e.clientY-oriPos.current.cY;constleft=oriPos.current.left+e.clientX-oriPos.current.cX;//限制必须在这个范围内移动的画板高度-元素的高度newStyle.top=Math.max(0,Math.min(top,wrapStyle.height-style.height));newStyle.left=Math.max(0,Math.min(left,wrapStyle.width-style.width));setStyle(newStyle)}现在拖不出来了。上面的代码还有一些小坑。我们定义的三个方法,onMouseMove、onMouseUp、onMouseDown,都是直接通过函数定义的。这一次,有一些性能问题。每次设置stylestate,都会重新渲染组件,导致重新定义这三个方法。这是不必要的性能浪费。通过使用React的useCallback语法糖来定义方法,可以避免不断地重定义。与上面的useRef相同constonMouseDown=useCallback((e)=>{/*...*/},[])constonMouseMove=useCallback((e)=>{/*...*/},[])constonMouseUp=useCallback((e)=>{/*...*/},[])scaling接下来封装了一个方法。计算元素的缩放比例。我们在一个控制点上按下鼠标,保存当前控制点的方向。鼠标拖动后,根据当前方向计算元素位置和宽高,首先封装了原来的拖动方法。顺便把onMouseMove也改一下。/***元素变化。该方法放在组件外部或其他地方。*@paramdirection//movemove/'e','w','s','n','ne','nw','se','sw'*@paramoriStyle属性widthheighttopleft*@paramoriPos按下鼠标时记录的坐标*@parameeventevent*/functiontransform(direction,oriPos,e){conststyle={...oriPos.current}constoffsetX=e.clientX-oriPos.当前.cX;constoffsetY=e.clientY-oriPos.current.cY;switch(direction.current){//拖放case'move'://元素的当前位置+偏移量consttop=oriPos.当前.top+offsetY;constleft=oriPos.current.left+offsetX;//限制必须在这个范围内移动的画板高度-元素的高度style.top=Math.max(0,Math.min(top,wrapStyle.height-style.height));style.left=Math.max(0,Math.min(left,wrapStyle.width-style.width));break//eastcase'e'://向右拖动添加宽度style.width+=offsetX;returnstyle//westcase'w'://增加宽度,位置同步左移style.width-=offsetX;style.left+=offsetX;returnstyle//southcase's':style.height+=offsetY;returnstyle//北大写'n':style.height-=offsetY;顶部+=偏移Y;break//东北方格'ne':style.height-=offsetY;style.top+=offsetY;样式.width+=offsetX;break//Northwestcase'nw':style.height-=offsetYstyle.top+=offsetY;style.width-=offsetX;style.left+=offsetX;break//东南方格'se':style.height+=offsetY;样式.width+=offsetX;'sw':style.height+=offsetY;style.width-=offsetX;style.left+=offsetX;allback((dir,e)=>{//防止事件冒泡e.stopPropagation();//保存方向direction.current=dir;isDown.current=true;//然后鼠标坐标为constcY=e.clientY;//clientX是相对于可视区域的constcX=e.clientX;oriPos.current={...style,cX,cY}})//鼠标移动constonMouseMove=useCallback((e)=>{//判断themouse是否按下if(!isDown.current)returnletnewStyle=transform(direction,oriPos,e);setStyle(newStyle)},[])这样就完成了元素的拖动缩放功能。旋转将旋转按钮添加到绘图项。//....OK,剩下的只需要在transform方法中添加计算角度的代码functiontransform(direction,oriPos,e){//...omitswitch(direction.current){//...omit//拖拽移动case'rotate'://先计算下一个元素的中心点,x,y为坐标原点constx=style.宽度/2+样式.left;consty=style.height/2+style.top;//当前鼠标坐标constx1=e.clientX;consty1=e.clientY;//使用高中三角学风格。transform=`rotate(${(Math.atan2((y1-y),(x1-x)))*(180/Math.PI)-90}deg)`;break}}测试很漂亮~到这里就完成了元素的拖动、缩放、旋转功能。最后,如果本文对您有帮助,谢谢您的关注和点赞哦?