如何在画布上制作 HTML5 可拖动对象?
Posted
技术标签:
【中文标题】如何在画布上制作 HTML5 可拖动对象?【英文标题】:How to make HTML5 draggable objects over canvas? 【发布时间】:2013-08-04 18:30:57 【问题描述】:我刚开始学习html5,我正在尝试用可拖动的船只创建一个战舰界面。我需要帮助使我的拖动方法起作用。我故意不使用库,因为我需要使船只可拖动到另一个画布界面(战舰板)上,我不知道如何使用 Kinetic 库。我觉得我很接近,但我无法弄清楚最后一点。船只应该可以顺利拖动,但在单击时它们似乎会捕捉到鼠标的位置......
这是我的代码:
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Canvas Drag and Drop Test</title>
</head>
<body>
<section>
<div align=center>
<canvas id="canvas" >
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
</div>
<script type="text/javascript">
var canvas;
var ctx;
var x = 75;
var y = 50;
var WIDTH = 550;
var HEIGHT = 550;
var dragok = false;
var ships = [];
var ship;
var shipFill = "#FF0000";
//Definitions
//Draggable Carrier
var caRectX = 100;
var caRectY = 50;
var caRectHeight = 50;
var caRectWidth = 5 * 50;
var carrier =
x : caRectX,
y : caRectY,
width : caRectWidth,
height : caRectHeight,
fill : shipFill,
dragging : false,
offsetX : 0,
offsetY : 0,
;
ships.push(carrier);
//Draggable Battleship
var bsRectX = 100;
var bsRectY = 150;
var bsRectHeight = 50;
var bsRectWidth = 4 * 50;
var battleship =
x : bsRectX,
y : bsRectY,
width : bsRectWidth,
height : bsRectHeight,
fill : shipFill,
dragging : false,
offsetX : 0,
offsetY : 0,
;
ships.push(battleship);
//Draggable Patrolboat
var pbRectX = 100;
var pbRectY = 250;
var pbRectHeight = 50;
var pbRectWidth = 2 * 50;
var patrolboat =
x : pbRectX,
y : pbRectY,
width : pbRectWidth,
height : pbRectHeight,
fill : shipFill,
dragging : false,
offsetX : 0,
offsetY : 0,
;
ships.push(patrolboat);
//Draggable Submarine
var suRectX = 100;
var suRectY = 350;
var suRectHeight = 50;
var suRectWidth = 3 * 50;
var submarine =
x : suRectX,
y : suRectY,
width : suRectWidth,
height : suRectHeight,
fill : shipFill,
dragging : false,
offsetX : 0,
offsetY : 0,
;
ships.push(submarine);
//Draggable destroyer
var deRectX = 100;
var deRectY = 450;
var deRectHeight = 50;
var deRectWidth = 3 * 50;
var destroyer =
x : deRectX,
y : deRectY,
width : deRectWidth,
height : deRectHeight,
dragging : false,
fill : shipFill
;
ships.push(destroyer)
function rect(x, y, w, h)
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.closePath();
ctx.fill();
function clear()
ctx.clearRect(0, 0, WIDTH, HEIGHT);
function init()
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
return setInterval(draw, 10);
function draw()
clear();
ctx.fillStyle = "#FAF7F8";
rect(0, 0, WIDTH, HEIGHT);
ctx.fillStyle = "#444444";
for (var i = 0; i < ships.length; i++)
rect(ships[i].x, ships[i].y, ships[i].width, ships[i].height);
function myMove(e)
if (ship.dragging)
ship.x = e.pageX - canvas.offsetLeft;
ship.y = e.pageY - canvas.offsetTop;
draw()
function myDown(e)
ship = getClickedShip(e.pageX,e.pageY);
if (ship!=null)
ship.x = e.pageX - canvas.offsetLeft;
ship.y = e.pageY - canvas.offsetTop;
ship.dragging = true;
canvas.onmousemove = myMove();
function myUp()
ship.dragging = false;
canvas.onmousemove = null;
function getClickedShip(sx,sy)
for (var i = 0; i < ships.length; i++)
if(sx > (ships[i].x )+ canvas.offsetLeft && sx < (ships[i].x+ships[i].width+ canvas.offsetLeft) && sy > (ships[i].y + canvas.offsetTop) && sy < (ships[i].y+ships[i].height))
return ships[i];
init();
canvas.onmousedown = myDown;
canvas.onmouseup = myUp;
</script>
</section>
</body>
</html>
【问题讨论】:
【参考方案1】:这是使 html 形状可拖动的过程
请注意,这个问题之前已经在 SO 上回答过(很多次了!)
但是这个答案说明了新的 context.isPointInPath 方法来命中测试一个点是否在 html 画布路径内。
希望这种新的命中测试方法对 OP 和其他人来说是新的和有用的 :)
以下是在 html 画布中拖动形状的一般过程:
鼠标按下:
将此 mouseX 位置保存在变量中 (lastX) 将此 mouseY 位置保存在变量中 (lastY) 将 mouseIsDown 标志设置为 true鼠标上移
将 mouseIsDown 标志设置为 false鼠标移动
对每艘船进行命中测试,看看它是否应该被拖动。 如果 lastX/lastY 在船内,则该船正在被拖拽 将拖动的船只移动鼠标刚刚移动的距离MouseDown 处理程序代码:
function handleMouseDown(e)
// get the current mouse position relative to the canvas
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// save this last mouseX/mouseY
lastX=mouseX;
lastY=mouseY;
// set the mouseIsDown flag
mouseIsDown=true;
MouseUp 处理程序代码:
function handleMouseUp(e)
// clear the mouseIsDown flag
mouseIsDown=false;
MouseMove 处理程序代码:
此代码说明了使用context.isPointInPath
对 html 画布路径进行命中测试
这样做的过程是:
定义路径(但不绘制——无填充,无描边) 使用context.isPointInPath(x,y)
测试x,y 是否在上面定义的路径内。
这是使用 context.isPointInPath 的 mouseMove 处理程序
function handleMouseMove(e)
// if the mouseIsDown flag is’nt set, no work to do
if(!mouseIsDown) return;
// get mouseX/mouseY
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// for each ship in the ships array
// use context.isPointInPath to test if it’s being dragged
for(var i=0;i<ships.length;i++)
var ship=ships[i];
drawShip(ship);
if(ctx.isPointInPath(lastX,lastY))
// if this ship’s being dragged,
// move it by the change in mouse position from lastXY to currentXY
ship.x+=(mouseX-lastX);
ship.y+=(mouseY-lastY);
ship.right=ship.x+ship.width;
ship.bottom=ship.y+ship.height;
// update the lastXY to the current mouse position
lastX=mouseX;
lastY=mouseY;
// draw all ships in their new positions
drawAllShips();
关于提高性能的注意事项:
在生产中,您会希望 mouseMove 仅保存鼠标位置。 然后让另一个程序检索那些保存的位置并进行命中测试/重绘。 其他过程可能会在像 requestAnimationFrame 这样的定时循环中。这是代码和小提琴:http://jsfiddle.net/m1erickson/sEBAC/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body background-color: ivory;
canvasborder:1px solid red;
</style>
<script>
$(function()
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.strokeStyle="lightgray";
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var mouseIsDown=false;
var lastX=0;
var lastY=0;
var ships=[];
// make some ship
makeShip(20,30,50,25,"skyblue");
makeShip(20,100,30,25,"skyblue");
makeShip(20,170,50,25,"salmon");
makeShip(20,240,30,25,"salmon");
function makeShip(x,y,width,height,fill)
var ship=
x:x,
y:y,
width:width,
height:height,
right:x+width,
bottom:y+height,
fill:fill
ships.push(ship);
return(ship);
drawAllShips();
function drawAllShips()
ctx.clearRect(0,0,canvas.width,canvas.height);
for(var i=0;i<ships.length;i++)
var ship=ships[i]
drawShip(ship);
ctx.fillStyle=ship.fill;
ctx.fill();
ctx.stroke();
function drawShip(ship)
ctx.beginPath();
ctx.moveTo(ship.x,ship.y);
ctx.lineTo(ship.right,ship.y);
ctx.lineTo(ship.right+10,ship.y+ship.height/2);
ctx.lineTo(ship.right,ship.bottom);
ctx.lineTo(ship.x,ship.bottom);
ctx.closePath();
function handleMouseDown(e)
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// mousedown stuff here
lastX=mouseX;
lastY=mouseY;
mouseIsDown=true;
function handleMouseUp(e)
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// mouseup stuff here
mouseIsDown=false;
function handleMouseMove(e)
if(!mouseIsDown) return;
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// mousemove stuff here
for(var i=0;i<ships.length;i++)
var ship=ships[i];
drawShip(ship);
if(ctx.isPointInPath(lastX,lastY))
ship.x+=(mouseX-lastX);
ship.y+=(mouseY-lastY);
ship.right=ship.x+ship.width;
ship.bottom=ship.y+ship.height;
lastX=mouseX;
lastY=mouseY;
drawAllShips();
$("#canvas").mousedown(function(e)handleMouseDown(e););
$("#canvas").mousemove(function(e)handleMouseMove(e););
$("#canvas").mouseup(function(e)handleMouseUp(e););
); // end $(function());
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
【讨论】:
如果我们想使用图像,我们能否有相同的演示工作......画布中的多个图像可拖动。 在 drawAllShips 中使用ctx.drawImage
代替 'ctx.fill 和 ctx.stroke'。图像将显示,路径仍然提供isPointInPath
中使用的命中区域。我有信心你可以做出必要的调整。干杯!
效果很好,谢谢,除此之外,实际上我还有一些关于画布的其他问题,所有[无法在这里分享]..我希望得到像你这样的人的建议[完美]。您是否也咨询了外部堆栈溢出...如果可能的话,如果您能抽出一些时间,那将是非常好的和感激的。以上是关于如何在画布上制作 HTML5 可拖动对象?的主要内容,如果未能解决你的问题,请参考以下文章