canvas实现鼠标拖拽矩形移动改变大小
项目的一个新需求,动态生成矩形框,鼠标点击拖动改变矩形框的位置,并可以调整大小。
之前做过一个小demo,需求类似,但是在canvas内只有一个矩形框,拖动移动,当时记得是用isPointInPath()直接判断鼠标是否点在了矩形框以内。新需求的矩形框个数为n,经过测试,isPointinPath实现过程中有bug,并不能精准定位到具体点击到canvas的某一个矩形框。经过一系列的头脑风暴,才想出了解决办法,才发现原来是最简单的方法,但是在思考的当初就被pass了,见代码:
html:
<body> <canvas id="canvas" width="400" height="300"> </canvas> </body>
小demo,不做其他修饰,直接写逻辑吧。
js:
第一步,创建一个容器,以保存Canvas内绘制的元素点。Canvas是一种非保留性的绘图界面,即不会记录过去执行的绘图操作,而是保持最终结果(构成图像的彩色像素)。
1 // canvas 矩形框集合 2 var rects=[]; 3 function rectar(x,y,width,height){ 4 this.x = x; 5 this.y = y; 6 this.width = width; 7 this.height = height; 8 this.isSelected = false; 9 };
绘制矩形框:
1 function drawRect() { 2 // 清除画布,准备绘制 3 context.clearRect(0, 0, canvas.width, canvas.height); 4 5 // 遍历所有矩形框 6 for(var i=0; i<rects.length; i++) { 7 var rect = rects[i]; 8 9 // 绘制矩形 10 context.strokeStyle="#FF0000"; 11 context.strokeRect(rect.x,rect.y,rect.width,rect.height,rect.color); 12 13 if (rect.isSelected) { 14 context.lineWidth = 50; 15 } 16 else { 17 context.lineWidth = 10; 18 } 19 } 20 }
这是一个绘制函数,因为在Canvas的所有操作,全部都是重新绘制的(先清除,在绘制),每次程序刷新画布时,会先使用 clearRect() 方法清除画布上的所有内容。但不用当心这样会造成画布闪烁,即画布上的圆圈一下子全部消失,然后一下子又重新出现。因为Canvas针对这个问题进行了优化,会在所有绘图逻辑执行完毕后才清除或绘制所有内容,保证最终结果的流畅。然后遍历矩形数组 其中的x,y,width,height来画矩形。
*这里我的项目是根据病变位置动态生成的矩形框,每一次生成矩形框,都要把它的位置信息添加到数组中,这里就直接创建矩形框了,可以根据自己需求改造
1 function addRandomRect() { 2 var x=10; 3 var y=10; 4 var width=100; 5 var height=100; 6 // 创建一个新的矩形对象 7 var rect=new rectar(x,y,width,height); 8 9 // 把它保存在数组中 10 rects.push(rect); 11 // 重新绘制画布 12 drawRect(); 13 };
*Canvas点击事件
1 var SelectedRect; 2 var x1; 3 var y1; 4 var right=false; 5 var widthstart,widthend; 6 var heightstart,heightend; 7 8 function canvasClick(e) { 9 // 取得画布上被单击的点 10 var clickX = e.pageX - canvas.offsetLeft; 11 var clickY = e.pageY - canvas.offsetTop; 12 13 // 查找被单击的矩形框 14 for(var i=rects.length-1; i>=0; i--) { 15 var rect = rects[i]; 16 17 widthstart=rect.x; 18 widthend=rect.x+rect.width; 19 20 heightstart=rect.y; 21 heightend=rect.y+rect.height; 22 23 // 判断这个点是否在矩形框中 24 if ((clickX>=widthstart&&clickX<(widthend-20))&&(clickY>=heightstart)&&(clickY<(heightend-20))) { 25 console.log(clickX); 26 // 清除之前选择的矩形框 27 if (SelectedRect != null) SelectedRect.isSelected = false; 28 SelectedRect = rect; 29 x1=clickX-SelectedRect.x; 30 y1=clickY-SelectedRect.y; 31 //选择新圆圈 32 rect.isSelected = true; 33 34 // 使圆圈允许拖拽 35 isDragging = true; 36 37 //更新显示 38 drawRect(); 39 //停止搜索 40 return; 41 }; 42 /* 43 设置拉伸的界限。 44 */ 45 // if ((clickX>=(widthend-20))&&(clickY>=(heightend-20))) 46 // { 47 // SelectedRect = rect; 48 // right=true; 49 // }
//18-02-01改
if ((clickX>=(widthend-20)&&((clickX<=(widthend+20)))&&(clickY>=(heightend-20))&&(clickY>=(heightend+20)))
{
SelectedRect = rect;
right=true;
}
50 }
51 }
代码中23行为判断具体点击哪个元素的语句,其实很简单,当初绕了很久,很简单直接判断鼠标点击点是否在矩形框之内即可,无论是哪个矩形框,只要在矩形框之内,就把当前矩形框设置为点击的矩形框。29行判断鼠标点击点相对于矩形框的位置。42-49行,是鼠标拉伸改变大小的判断,可以设置矩形四个角拉伸,但我认为太复杂了,只保留了右下角拉伸的点击判断,操作更简单一些。
响应事件:
function dragRect(e) { // 判断矩形是否开始拖拽 if (isDragging == true) { // 判断拖拽对象是否存在 if (SelectedRect != null) { // 取得鼠标位置 var x = e.pageX - canvas.offsetLeft; var y = e.pageY - canvas.offsetTop; // 将圆圈移动到鼠标位置 SelectedRect.x= x-x1; SelectedRect.y= y-y1; // 更新画布 drawRect(); } }
//判断是否开始拉伸 if (right) {
//设置拉伸最小的边界 if ((e.pageX - canvas.offsetLeft-SelectedRect.x)>50) { SelectedRect.width=e.pageX - canvas.offsetLeft-SelectedRect.x; } else { SelectedRect.width=50; } console.log(SelectedRect.width); if((e.pageY - canvas.offsetTop-SelectedRect.y)>50){ SelectedRect.height=e.pageY - canvas.offsetTop-SelectedRect.y; } else { SelectedRect.height=50; } drawRect(); } };
以上就完成了对矩形框的基本操作,然后添加onmouseup的函数和调用函数:
var isDragging = false; function stopDragging() { isDragging = false; right=false; };
function clearCanvas() {
// 去除所有矩形
rects = [];
// 重新绘制画布.
drawCircles();
}
window.onload = function() { canvas = document.getElementById("canvas"); context = canvas.getContext("2d"); canvas.onmousedown = canvasClick; canvas.onmouseup = stopDragging; canvas.onmouseout = stopDragging; canvas.onmousemove =dragRect;
; };