2014-07-22 197 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

非常感謝。解決了我的問題! –