【问题标题】:How to make HTML5 draggable objects over canvas?如何在画布上制作 HTML5 可拖动对象?
【发布时间】: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" width="550" height="550">
                    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> 

【问题讨论】:

    标签: javascript html canvas draggable


    【解决方案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; }
        canvas{border: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 中使用的命中区域。我有信心你可以做出必要的调整。干杯!
    • 效果很好,谢谢,除此之外,实际上我还有一些关于画布的其他问题,所有[无法在这里分享]..我希望得到像你这样的人的建议[完美]。您是否也咨询了外部堆栈溢出...如果可能的话,如果您能抽出一些时间,那将是非常好的和感激的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-16
    • 1970-01-01
    • 1970-01-01
    • 2016-11-12
    • 1970-01-01
    相关资源
    最近更新 更多