Hike News
Hike News

canvas 圖形拖移

前言

canvas 可增加圖形、文字到畫板,但當位置想要移動時就要回到 Javascript 內修改,所以找了一下有沒有可以直接拖移圖形的方式,發現可以用 isDragging 打開拖移功能並取得圖形的設定值,在點擊圖形時就執行變更設定達到移動的目的。

筆記

初始值

1
2
3
4
5
6
7
8
9
// 拖移的變數
var isDragging = false;
var startX, startY;

// 存放圖形、圖片 的陣列
var shapes=[];

// 當移動圖形時記錄的變數
var selectedShapeIndex;

載入圖形

因為需要判斷滑鼠是否在圖形上,所以就不是用 canvas 原本的方式

1
2
3
4
5
6
7
8
9
10
// 圓形
ctx.fillStyle="#FF0000";
ctx.beginPath();
ctx.arc(30, 30, 15, 0, 2*Math.PI);
ctx.closePath();
ctx.fill();

// 矩形
ctx.fillStyle = "#0000ff";
ctx.fillRect(100, -1, 75, 35);

而是先建立 陣列物件 組合的方式

1
2
3
4
5
6
7
8
9
10
11
12
// 建立一個陣列,存放設定圖形的物件
var shapes=[];
// 置入 shapes 一個藍色圓形的物件
shapes.push( {x:30, y:30, radius:15, color:'blue'} );
// 置入 shapes 一個紅色矩形的物件
shapes.push( {x:100, y:-1, width:75, height:35, color:'red'} );

// 滑鼠事件
ctx.onmousedown = handleMouseDown;
ctx.onmousemove = handleMouseMove;
ctx.onmouseup = handleMouseUp;
ctx.onmouseout = handleMouseOut;

再產生圖形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 把陣列的物件畫到 canvas
drawAll();

function drawAll(){
ctx.clearRect(0, 0, cw, ch);
for(var i = 0; i < shapes.length; i++){
var shape = shapes[i];
if(shape.radius){
// 當有 radius 的值時,產生圓形
ctx.beginPath();
ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI*2);
ctx.fillStyle = shape.color;
ctx.fill();
}else if(shape.width){
// 當有 width 的值時,產生矩形
ctx.fillStyle = shape.color;
ctx.fillRect(shape.x, shape.y, shape.width, shape.height);
}
}
}

載入文字

所以也可以載入文字

1
2
3
4
5
6
7
8
9
// 置入文字的物件
shapes.push({x:50, y:50, text:'文字拖移', fontSize: 36});

if(shape.text){
// 當 text 有值時,產生文字
ctx.textBaseline = 'top';
ctx.font = shape.fontSize + "px impact";
ctx.fillText(shape.text, shape.x, shape.y);
}

載入圖片

載入圖片也可以置入到陣列中

1
2
3
4
5
6
7
8
9
10
11
12
13
var card = new Image();
card.onload = function(){
// 置入 shapes 一個圖片 的物件
shapes.push( {x:30, y:10, width:127, height:150, image:card} );
// 載入畫面
drawAll();
// 滑鼠事件
ctx.onmousedown = handleMouseDown;
ctx.onmousemove = handleMouseMove;
ctx.onmouseup = handleMouseUp;
ctx.onmouseout = handleMouseOut;
};
card.src = 'card.png';

判斷滑鼠是否在範圍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function isMouseInShape(mx, my, shape){
// 圓形範圍
if(shape.radius){
var dx = mx - shape.x;
var dy = my - shape.y;
if(dx*dx + dy*dy < shape.radius*shape.radius){
return(true);
}
}else if(shape.width){
// 矩形範圍
var rLeft = shape.x;
var rRight = shape.x + shape.width;
var rTop = shape.y;
var rBott = shape.y + shape.height;
if( mx > rLeft && mx < rRight && my > rTop && my < rBott){
return(true);
}
}else if(shape.image){
// 圖片範圍和矩形範圍相同,所以一起使用時要區分使用的變數
var rLeft = shape.x;
var rRight = shape.x + shape.imgWidth;
var rTop = shape.y;
var rBott = shape.y + shape.imgHeight;
// 判斷滑鼠是否在範圍內
if( mx > rLeft && mx < rRight && my > rTop && my < rBott){
return(true);
}
}else if(shape.text){
// 文字範圍
var rLeft = shape.x;
var rRight = shape.x + ctx.measureText(shape.text).width;
var rTop = shape.y;
var rBott = shape.y + shape.fontSize;
// 判斷滑鼠是否在範圍內
if (mx > rLeft && mx < rRight && my > rTop && my < rBott) {
return true;
}
}
return(false);
}

圖形拖移

  • 滑鼠事件
  • 元素重疊時,移動就需要停止 默認事件、冒泡事件
    • 默認事件 event.preventDefault()
    • 冒泡事件 event.stopPropagation()

滑鼠在圖形上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 點擊時修改 isDragging 成可移動,計算點擊時滑鼠的位置
ctx.onmousedown = handleMouseDown;
function handleMouseDown(e){
e.preventDefault();
e.stopPropagation();
// 滑鼠位置
startX = parseInt(e.clientX-offsetX);
startY = parseInt(e.clientY-offsetY);
for(var i = 0; i < shapes.length; i++){
if(isMouseInShape(startX,startY,shapes[i])){
selectedShapeIndex=i;
isDragging=true;
return;
}
}
}

// 移動時修改位置
ctx.onmousemove = handleMouseMove;
function handleMouseMove(e){
if(!isDragging){return;}
e.preventDefault();
e.stopPropagation();
// 滑鼠位置
mouseX = parseInt(e.clientX-offsetX);
mouseY = parseInt(e.clientY-offsetY);
// 滑鼠移動後和原本位置的距離
var dx = mouseX - startX;
var dy = mouseY - startY;
// 移動圖形時修改值
var selectedShape=shapes[selectedShapeIndex];
selectedShape.x += dx;
selectedShape.y += dy;

drawAll();
// update the starting drag position (== the current mouse position)
startX = mouseX;
startY = mouseY;
}

滑鼠在圖形外

當放開 / 移開滑鼠,目的是一樣的 不拖移任何一個物件 ,所以 isDragging 就應該要是 false ,若是 true 時就要成 false .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ctx.onmouseup = handleMouseUp;
ctx.onmouseout = handleMouseOut;

function handleMouseUp(e){
if(!isDragging){ return; }
e.preventDefault();
e.stopPropagation();
isDragging = false;
}

function handleMouseOut(e){
if(!isDragging){return;}
e.preventDefault();
e.stopPropagation();
isDragging = false;
}

參考

拖動 -畫布- 內的圖像
https://riptutorial.com/zh-TW/html5-canvas/example/18920/在-畫布-周圍拖動圖像

滑鼠事件
HTML DOM MouseEvent