2014-07-22 201 views
4

我对JavaScript和画布很陌生,我有一个程序,它应该检测在椭圆路径上动画的元素。它后来去形成一棵树..但这是我连接到jsfiddle的基本结构。 它工作正常,没有缩放或平移,但只要我尝试缩放或平移,鼠标坐标就会失灵。 我尝试了以下markE的建议HTML5 canvas get coordinates after zoom and translate 但是我肯定是做错了事,我很清楚不了解画布和变换矩阵发生了什么。我花了大约3天试图改变这一切我能想到的组合,但我似乎无法推测出来:■缩放和平移画布后鼠标坐标不匹配

解决: 这里是我的代码以缩放和平移鼠标和动画和在椭圆检测元件: http://jsfiddle.net/metalloyd/A8hgz/

  theCanvas = document.getElementById("canvasOne"); 
      context = theCanvas.getContext("2d"); 
      var status = document.getElementById('status'); 
      var $canvas = $("#canvasOne"); 
      var canvasOffset = $canvas.offset(); 
      var offsetX = canvasOffset.left; 
      var offsetY = canvasOffset.top; 
      var scrollX = $canvas.scrollLeft(); 
      var scrollY = $canvas.scrollTop(); 
      var cw = theCanvas.width; 
      var ch = theCanvas.height; 
      var scaleFactor = 1.00; 
      var panX = 0; 
      var panY = 0; 

      var mainX = 250; 
      // setting the middle point position X value 
      var mainY = 100; 
      // setting the middle point position Y value 
      var mainR = 125; 
      // main ellipse radius R 
      var no = 5; 
      // number of nodes to display 
      var div_angle = 360/no; 

      var circle = { 
       centerX: mainX, 
       centerY: mainY + 100, 
       radius: mainR, 
       angle: .9 
      }; 

      var ball = { 
       x: 0, 
       y: 0, 
       speed: .1 
      }; 
      var a = 1.8; 
      //Ellipse width 
      var b = .5; 
      //Ellipse height 

      //Scale and Pan variables 
      var translatePos = { 
       x: 1, 
       y: 1 
      }; 
      var startDragOffset = {}; 
      var mouseDown = false; 

      var elements = [{}]; 

      // Animate 
      var animateInterval = setInterval(drawScreen, 1); 

      //Animation 
      function drawScreen() { 
       context.clearRect(0, 0, cw, ch); 
       // Background box 
       context.beginPath(); 
       context.fillStyle = '#EEEEEE'; 
       context.fillRect(0, 0, theCanvas.width, theCanvas.height); 
       context.strokeRect(1, 1, theCanvas.width - 2, theCanvas.height - 2); 
       context.closePath(); 

       context.save(); 
       context.translate(panX, panY); 
       context.scale(scaleFactor, scaleFactor); 

       ball.speed = ball.speed + 0.001; 

       for (var i = 1; i <= no; i++) { 
        // male 
        new_angle = div_angle * i; 
        //Starting positions for ball 1 at different points on the ellipse 
        circle.angle = (new_angle * (0.0174532925)) + ball.speed; 
        //elliptical x position and y position for animation for the first ball 
        //xx and yy records the first balls coordinates 
        xx = ball.x = circle.centerX - (a * Math.cos(circle.angle)) * (circle.radius); 
        yy = ball.y = circle.centerY + (b * Math.sin(circle.angle)) * (circle.radius); 
        //Draw the first ball with position x and y 
        context.fillStyle = "#000000"; 
        context.beginPath(); 
        context.arc(ball.x, ball.y, 10, 0, Math.PI * 2, true); 
        context.fill(); 
        context.closePath(); 

        //alert("male Positions "+"X: "+ball.x+ " Y: "+ball.y); 

        // female 
        new_angle = div_angle * i + 4; 
        //Starting positions for ball 2 at different points on the ellipse 
        circle.angle = (new_angle * (0.0174532925)) + ball.speed; 
        //elliptical x position and y position for animation for the second ball 
        //ball.x and ball.y record the second balls positions 
        ball.x = circle.centerX - (a * Math.cos(circle.angle)) * (circle.radius); 
        ball.y = circle.centerY + (b * Math.sin(circle.angle)) * (circle.radius); 
        context.fillStyle = "#000000"; 
        context.beginPath(); 
        context.arc(ball.x, ball.y, 10, 0, Math.PI * 2, true); 
        context.fill(); 
        context.closePath(); 

        //alert("female Positions "+"X: "+ball.x+ " Y: "+ball.y); 

        //Record the ball positions in elements array for locating positions with mouse coordinates. 
        elements[i] = { 
         id: i, 
         femaleX: ball.x, 
         femaleY: ball.y, 
         maleX: xx, 
         maleY: yy, 
         w: 10 //radius of the ball to draw while locating the positions 
        }; 
        //Text Numbering 
        context.beginPath(); 
        context.fillStyle = "blue"; 
        context.font = "bold 16px Arial"; 
        context.fillText(elements[i].id, ball.x - 20, ball.y + 20); 
        context.closePath(); 
        // line drawing--Connecting lines to the balls from the center. 
        context.moveTo(mainX, mainY); 
        context.lineTo((ball.x + xx)/2, (ball.y + yy)/2); 
        //Draw line till the middle point between ball1 and ball2 
        context.stroke(); 
        context.fill(); 
        context.closePath(); 
       } 
       // center point 
       context.fillStyle = "#000000"; 
       context.beginPath(); 
       context.arc(mainX, mainY, 15, 0, Math.PI * 2, true); 
       context.fill(); 
       context.closePath(); 

       context.restore(); 
      } 

      // Event Listeners 
      // Mouse move event to alert the position of the ball on screen 


      document.getElementById("plus").addEventListener("click", function() { 
       scaleFactor *= 1.1; 
       drawScreen(); 
      }, false); 

      document.getElementById("minus").addEventListener("click", function() { 
       scaleFactor /= 1.1; 
       drawScreen(); 
      }, false); 

      // Event listeners to handle screen panning 
      context.canvas.addEventListener("mousedown", function (evt) { 
       mouseDown = true; 
       startDragOffset.x = evt.clientX - translatePos.x; 
       startDragOffset.y = evt.clientY - translatePos.y; 
      }); 

      context.canvas.addEventListener("mouseup", function (evt) { 
       mouseDown = false; 
      }); 

      context.canvas.addEventListener("mouseover", function (evt) { 
       mouseDown = false; 
      }); 

      context.canvas.addEventListener("mouseout", function (evt) { 
       mouseDown = false; 
      }); 

      context.canvas.addEventListener("mousemove", function (evt) { 
       if (mouseDown) { 
        translatePos.x = evt.clientX - startDragOffset.x; 
        translatePos.y = evt.clientY - startDragOffset.y; 

        panX = translatePos.x; 
        panY = translatePos.y; 

        drawScreen(); 
       } 

       evt.preventDefault(); 
       evt.stopPropagation(); 

       var mouseX = parseInt(evt.clientX - offsetX); 
       var mouseY = parseInt(evt.clientY - offsetY); 

       var mouseXT = parseInt((mouseX - panX)/scaleFactor); 
       var mouseYT = parseInt((mouseY - panY)/scaleFactor); 

       status.innerHTML = mouseXT + " | " + mouseYT; 

       for (var i = 1; i < elements.length; i++) { 
        var b = elements[i]; 
        context.closePath(); 
        context.beginPath(); 
        context.arc(b.femaleX, b.femaleY, 10, 0, Math.PI * 2); 
        context.arc(b.maleX, b.maleY, 10, 0, Math.PI * 2); 

        if (context.isPointInPath(mouseXT, mouseYT)) { 
         theCanvas.style.cursor = 'pointer'; 
         alert(b.id + " female.x: " + b.femaleX + " female.y: " + b.femaleY + " ball.x: " + ball.x + " ball.y: " + ball.y); 
         return; 
        } else theCanvas.style.cursor = 'default'; 
        context.closePath(); 
       } 

      });` 
+3

你代码变得太大了。首先要把它分解成小部分,你可以简单地进行单元测试。例如,有一个矩阵处理的单独部分,并检查它工作正常。编写一些小的绘图函数来简化(drawBackground,drawBall,...),并将你的事件处理从你的代码中解除关联(你的mousemove就像60行!!)。用小测试的积木,你很快就会看到捕获物的位置。 – GameAlchemist

+0

哈哈!试图让它作为一个整体工作,我陷入了一段迷雾。但我确实将其分解为基础知识。它确实得到解决。谢谢你的建议:) 我真的不知道为什么我以前没有这么想过。 – MetalloyD

+0

这是一个常见的错误,不会看到复杂性已经达到“关键”的门槛,不用担心。当我得到一个错误时,我总是怀疑它是否只是一个错误或我需要重构我的代码的标志。顺便说一下,我没有看到你发布的编辑有太多的变化,给一个重构的例子,我做了drawScreen()的这个小提琴的重构(http://jsfiddle.net/gamealchemist/A8hgz/2/ – GameAlchemist

回答

11

使用变换矩阵在这些情况下是有用的或者甚至是必要的:

  • 如果深度嵌套转换。
  • 如果用不同的变换改变不同的图纸。
  • 如果您需要临时转换坐标。
  • 如果您正在进行涉及歪斜的转换。
  • 如果您正在进行涉及旋转的转换。

但是对于简单的平移和缩放整个画布的情况,有一个更简单的方法。

首先,设置变量来保存缩放和平移的电流量:

var scaleFactor=1.00; 
var panX=0; 
var panY=0; 

然后用这些锅&比例变量做所有的图纸。

  • 清除画布。
  • 保存未转换的画布状态。
  • 做的翻译与panX变量。
  • 使用scaleFactor变量进行缩放。
  • 绘制所有元素,就好像它们处于未被格式化的空间一样。
  • 将上下文恢复到未转换状态。

实施例的代码:现在

function drawTranslated(){ 

    ctx.clearRect(0,0,cw,ch); 

    ctx.save(); 
    ctx.translate(panX,panY); 
    ctx.scale(scaleFactor,scaleFactor); 

    ctx.beginPath(); 
    ctx.arc(circleX,circleY,15,0,Math.PI*2); 
    ctx.closePath(); 
    ctx.fillStyle=randomColor(); 
    ctx.fill(); 

    ctx.restore(); 

} 

,约鼠标坐标:

浏览器总是返回在未变换的坐标的鼠标位置。您的绘图已经在变换后的空间中完成。如果你想知道你的鼠标在转换后的空间,你可以将未转换的鼠标坐标转换后的坐标如下:

var mouseXTransformed = (mouseX-panX)/scaleFactor; 
var mouseYTransformed = (mouseY-panY)/scaleFactor; 

下面是示例代码和演示:http://jsfiddle.net/m1erickson/HwNp3/

<!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 cw=canvas.width; 
    var ch=canvas.height; 

    var scaleFactor=1.00; 
    var panX=0; 
    var panY=0; 

    var circleX=150; 
    var circleY=150; 

    var $screen=$("#screen"); 
    var $transformed=$("#transformed"); 
    var $trx=$("#trx"); 

    drawTranslated(); 

    $("#canvas").mousemove(function(e){handleMouseMove(e);}); 
    $("#scaledown").click(function(){ scaleFactor/=1.1; drawTranslated(); }); 
    $("#scaleup").click(function(){ scaleFactor*=1.1; drawTranslated(); }); 
    $("#panleft").click(function(){ panX-=10; drawTranslated(); }); 
    $("#panright").click(function(){ panX+=10; drawTranslated(); }); 


    function drawTranslated(){ 
     ctx.clearRect(0,0,cw,ch); 

     ctx.save(); 
     ctx.translate(panX,panY); 
     ctx.scale(scaleFactor,scaleFactor); 

     ctx.beginPath(); 
     ctx.arc(circleX,circleY,15,0,Math.PI*2); 
     ctx.closePath(); 
     ctx.fillStyle=randomColor(); 
     ctx.fill(); 

     ctx.restore(); 

     $trx.text("Pan: "+panX+", Scale: "+scaleFactor); 
    } 

    function handleMouseMove(e){ 
     e.preventDefault(); 
     e.stopPropagation(); 

     var mouseX=parseInt(e.clientX-offsetX); 
     var mouseY=parseInt(e.clientY-offsetY); 

     var mouseXT=parseInt((mouseX-panX)/scaleFactor); 
     var mouseYT=parseInt((mouseY-panY)/scaleFactor); 

     $screen.text("Screen Coordinates: "+mouseX+"/"+mouseY); 

     $transformed.text("Transformed Coordinates: "+mouseXT+"/"+mouseYT); 
    } 

    function randomColor(){ 
     return('#'+Math.floor(Math.random()*16777215).toString(16)); 
    } 

}); // end $(function(){}); 
</script> 
</head> 
<body> 
    <h3>Transformed coordinates are mouseXY in transformed space.<br>The circles center is always at translated [150,150]</h3> 
    <h4 id=screen>Screen Coordinates:</h4> 
    <h4 id=transformed>Transformed Coordinates:</h4> 
    <h4 id=trx>Pan & Scale</h4> 
    <button id=scaledown>Scale Down</button> 
    <button id=scaleup>Scale Up</button> 
    <button id=panleft>Pan Left</button> 
    <button id=panright>Pan Right</button><br> 
    <canvas id="canvas" width=350 height=400></canvas> 
</body> 
</html> 
+0

谢谢markE !这完全解决了它! :D – MetalloyD

+0

非常感谢。解决了我的问题! –