項目的一個新需求,動態生成矩形框,鼠標點擊拖動改變矩形框的位置,並可以調整大小。
之前做過一個小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;
; };