【问题标题】:HTML5 Canvas drag image along path with coordinatesHTML5 Canvas 沿着带有坐标的路径拖动图像
【发布时间】:2014-02-04 21:30:12
【问题描述】:

是否可以有一个坐标数组并仅沿这些坐标拖放图像?我只想使用 javascript 而不是使用 javascript 库。我一直在挠头并永远在谷歌上搜索,但找不到如何做到这一点或是否可能。

【问题讨论】:

    标签: javascript html html5-canvas


    【解决方案1】:

    演示:http://jsfiddle.net/m1erickson/7vmML/

    示例代码:

    <!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");
        var $canvas=$("#canvas");
        var canvasOffset=$canvas.offset();
        var offsetX=canvasOffset.left;
        var offsetY=canvasOffset.top;
        var scrollX=$canvas.scrollLeft();
        var scrollY=$canvas.scrollTop();
    
        var isDown=false;
        var startX;
        var startY;
    
        var points=[];
        points.push({x:10,y:10});
        points.push({x:75,y:100});
        points.push({x:150,y:125});
        points.push({x:125,y:200});
    
        var imageX=-200;
        var imageY=-200;
    
        var img=new Image();
        img.onload=start;
        img.crossOrigin="anonymous";
        img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house32x32transparent.png";
        function start(){
            drawAll();
        }
    
        function drawAll(){
    
            ctx.clearRect(0,0,canvas.width,canvas.height);
    
            ctx.beginPath();
            ctx.moveTo(points[0].x+4,points[0].y+4)
            for(var i=1;i<points.length;i++){
                var pt=points[i];
                ctx.lineTo(pt.x+4,pt.y+4);
            }
            ctx.stroke();
            //
            for(var i=0;i<points.length;i++){
                var pt=points[i];
                ctx.fillRect(pt.x,pt.y,8,8);
            }
            //
            ctx.drawImage(img,imageX,imageY);
    
        }
    
        function handleMouseDown(e){
          e.preventDefault();
          isDown=true;
        }
    
        function handleMouseUp(e){
          e.preventDefault();
          isDown=false;
        }
    
        function handleMouseOut(e){
          e.preventDefault();
          isDown=false;
        }
    
        function handleMouseMove(e){
          if(!isDown){return;}
          e.preventDefault();
          mouseX=parseInt(e.clientX-offsetX);
          mouseY=parseInt(e.clientY-offsetY);
    
          var minDistance=1000;
          var minPoint=-1;
          for(var i=0;i<points.length;i++){
              var pt=points[i];
              var dx=mouseX-pt.x;
              var dy=mouseY-pt.y;
              var distance=Math.sqrt(dx*dx+dy*dy);
              if(distance<minDistance){
                  minDistance=distance;
                  imageX=pt.x-img.width/2;
                  imageY=pt.y-img.height/2;
              }
          }
          drawAll();
        }
    
        $("#canvas").mousedown(function(e){handleMouseDown(e);});
        $("#canvas").mousemove(function(e){handleMouseMove(e);});
        $("#canvas").mouseup(function(e){handleMouseUp(e);});
        $("#canvas").mouseout(function(e){handleMouseOut(e);});
    
    }); // end $(function(){});
    </script>
    </head>
    <body>
        <h4>Drag mouse.  Image will snap to nearest point.</h4>
        <canvas id="canvas" width=300 height=300></canvas>
    </body>
    </html>
    

    请注意,此示例代码使用 jQuery 来监听鼠标事件。如果您更喜欢纯 javascript,则可以使用这些事件绑定:

    canvas.onmousedown=handleMouseDown;
    canvas.onmouseup=handleMouseUp;
    canvas.onmouseout=handleMouseOut;
    canvas.onmousemove=handleMouseMove;
    

    你可以这样计算鼠标位置:

      function getMousePos(canvas,e) {
          var rect=canvas.getBoundingClientRect();
          return{ x:e.clientX-rect.left, y:e.clientY-rect.top };
      }
    

    【讨论】:

    • 我试着做一个小提琴来告诉你我要问什么以及我做了什么,但由于某种原因,在网上完全正常的东西在小提琴上根本不起作用,我不知道不知道为什么。但我的问题是,如果我想要两个图像在不同的时间移动怎么办。我会为此做些什么?例如,您单击一个图像以移动它,图像移动,第二个图像保持原位,直到它被单击。我可能有很多图片在线,我已经做了很多工作,但完全坚持如何做到这一点:(
    • 好吧,让我们看看...您可以通过单击图像的边界框来“选择”图像。然后您可以只移动该选定的图像。为此:图像内的鼠标按下是选择,不会触发isDown=true。图像外的鼠标按下是一个移动,会触发isDown=true。干杯!
    • 这是我保证的最后一个问题!现在,当用户单击按钮时,我在画布(按钮)旁边有一堆图片,它将图像添加到画布上的行中。我怎样才能做到这一点,如果用户点击第二张图片,它也会添加第二张图片,而不是仅仅覆盖前一张图片?
    • 我假设第二张图片过度绘制了第一张图片——而不是两个按钮不小心绘制了同一张图片。所以...当您单击图像按钮时,将其发送到画布的“中性”部分。例如,为了保持图像分离,您可以保留一个变量nextX,该变量确定最初放置任何新图像的 X 位置:var nextX=0drawImage(image,nextX,0)。添加图像后,nextX 会增加添加图像的宽度:nextX+=image.width。祝你的项目好运!
    • 首先我不得不说 MarkE 你是我能够自学画布的一个重要原因。我能够接受你向其他人展示的其他东西来实际学习它。是否有任何理由使用画布单击和拖动无法在 chrome 中正常工作,但在其他所有浏览器中都可以正常工作?
    【解决方案2】:

    是的,计算到每个点的最短距离,并用最近的点覆盖鼠标位置。

    我们以一个简单的点数组为例:

    var points = [10,200, 50,250, 100,100, 150,120, 200,240,
                  250,200, 300,120, 350,180, 400,150];
    

    现在,当您将项目移到上方时,您会以这种方式获得最近的点:

    让我们首先获取鼠标的 x 和 y 位置(用于演示 - 在您的代码中,您将使用项目的位置):

    var rect = canvas.getBoundingClientRect(),
        x = e.clientX - rect.left,
        y = e.clientY - rect.top,
    

    现在你可以迭代点数组,得到到鼠标x和y距离最短的点:

    var i = 0,
        pIndex = -1,
        minDist = 999999999,
        dist;
    
    /// get shortest distance
    for(; i < points.length; i+=2) {
    
        /// get distance from current point to mouse x,y
        dist = getDistance(x, y, points[i], points[i+1]);
    
        /// if less than previous distance, update
        if (dist < minDist) {
            minDist = dist;  /// current low
            pIndex = i;      /// point index
        }
    }
    
    pointX = points[pIndex];
    pointY = points[pIndex+1];
    

    计算距离的函数是简单的三角函数计算斜边:

    function getDistance(x1, y1, x2, y2) {
        var dx = x2 - x1,
            dy = y2 - y1;
        return Math.abs(Math.sqrt(dx * dx + dy * dy));
    }
    

    现在您可以在(pointX, pointY) 绘制您的项目。

    Live demo

    这里的例子使用了一个非常粗略的点数组。如果您需要更精细的分辨率,您可以使用 line smoother such as this one 或在线插入点。这是一个应用线条平滑以增加其分辨率和平滑度的版本(您可以通过将张力设置为 0 来降低平滑度,您可以通过更改段数来调整分辨率,参见以下演示):

    Here is a demo where line smoothing is used

    另外请记住,您可能需要优化检查范围,例如先进行粗略迭代,然后使用 3-4 个最近点,并在它们所代表的范围之间进行更精细的迭代。

    更新

    要仅在 held down and moved 时允许捕捉,请将这些行添加到代码中:

    var allowMove = false;
    
    canvas.onmousedown = function() {
        allowMove = true;
    }
    canvas.onmouseup = function() {
        allowMove = false;
    }
    canvas.onmousemove = function(e) {
        
        if (!allowMove) return;
    
        ... as before ...
    

    【讨论】:

    • 轻笑...JINX!我们的答案相隔不到四分之一秒。
    • 你知道在线平滑器如何只允许它在onmousedown上移动...我尝试过这样的事情,但它根本不起作用...
    • @ShawnaMacNabb 好的,要在 鼠标按下时移动它,只需在 mousedown/up 时向混合集添加一个标志并在 mousemove 中读取:jsfiddle.net/AbdiasSoftware/XfGwS/3
    • 非常感谢......这正是我所需要的,但我在将它实施到我的项目中时遇到了麻烦......当谈到我正在尝试的网络东西时,我是个菜鸟得到更好的。这是我的小提琴jsfiddle.net/smacnabb/7gZ2a
    • 我只是更新了一点,并不是我做了很多jsfiddle.net/smacnabb/7gZ2a/1
    猜你喜欢
    • 1970-01-01
    • 2011-08-20
    • 2016-11-09
    • 1970-01-01
    • 2010-10-27
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多