2016-08-08 9 views
6

キャンバスに回転した形状のサイズを変更しようとしています。私の問題は、レンダリング関数を呼び出すと、シェイプの角度に応じてシェイプが「ドリフト」してしまうことです。どうすればこれを防ぐことができますか?形状の幅を変更すると、回転した形状がy軸上を移動しています

私はキャンバスをクリックすると形状が大きくなり何らかの理由で上にドリフトするという問題を示す簡単なフィドルを作った。

はここでフィドルだ:「本物」のコードでhttps://jsfiddle.net/x5gxo1p7/

<style> 
    canvas { 
     position: absolute; 
     box-sizing: border-box; 
     border: 1px solid red; 
    } 
</style> 
<body> 
<div> 
    <canvas id="canvas"></canvas> 
</div> 
</body> 

<script type="text/javascript"> 
    var canvas = document.getElementById('canvas'); 
    canvas.width = 300; 
    canvas.height= 150; 
    var ctx = canvas.getContext('2d'); 
    var counter = 0; 

    var shape = { 
     top: 120, 
     left: 120, 
     width: 120, 
     height: 60, 
     rotation: Math.PI/180 * 15 
    }; 



    function draw() { 
     var h2 = shape.height/2; 
     var w2 = shape.width/2; 

     var x = w2; 
     var y = h2; 


     ctx.save(); 
     ctx.clearRect(0, 0, canvas.width, canvas.height); 
     ctx.translate(75,37.5) 
     ctx.translate(x, y); 

     ctx.rotate(Math.PI/180 * 15); 
     ctx.translate(-x, -y); 

     ctx.fillStyle = '#000'; 
     ctx.fillRect(0, 0, shape.width, shape.height); 
     ctx.restore(); 
} 


canvas.addEventListener('click', function() { 

    shape.width = shape.width + 15; 
    window.requestAnimationFrame(draw.bind(this)); 
}); 

window.requestAnimationFrame(draw.bind(this)); 
</script> 

形状がサイズ変更ハンドルをクリックするとリサイズと移動させたが、私は、この例では、十分に問題を示し思います。

EDIT:問題を明確にする更新フィドル:

https://jsfiddle.net/x5gxo1p7/9/

+0

それが原因ctx.translate(x、y)は 'にドリフトします;'と 'ctx.translate(-x、-y);':

溶液で新しいフィドル。コメントアウトしてみてください! - https://jsfiddle.net/Pugazh/x5gxo1p7/3/ – Pugazh

+0

その中心を中心にして形状を回転させるには、平行移動が必要です。 – Jomppe

答えて

0

を行う必要があり多くの作業を簡素化するには、解決策、問題は、私は新しい中心点座標を計算していなかったことがわかりました。 https://jsfiddle.net/HTxGb/151/

var canvas = document.getElementById('canvas'); 
var ctx = canvas.getContext('2d'); 

canvas.width =500; 
canvas.height = 500; 

var x = canvas.width/2; 
var y = canvas.height/2; 
var rectw = 20; 
var recth = 20; 
var rectx = -rectw/2; 
var recty = -recth/2; 
var rotation = 0; 
var addedRotation = Math.PI/12; 
var addedWidth = 20; 
var addedHeight = 10; 

var draw = function() { 
    ctx.save(); 
    ctx.clearRect(0, 0, canvas.width, canvas.height); 
    ctx.translate(x, y); 
    ctx.rotate(rotation); 
    ctx.fillRect(rectx, recty, rectw, recth); 
    ctx.restore(); 
} 


document.getElementById('growXRight').addEventListener('click', function() { 
    rectx -= addedWidth/2; 
    x += addedWidth/2 * Math.cos(rotation); 
    y -= addedWidth/2 * Math.sin(-rotation); 
    rectw += addedWidth; 
    draw(); 
}) 

document.getElementById('growXLeft').addEventListener('click', function() { 
    rectx -= addedWidth/2; 
    x -= addedWidth/2 * Math.cos(rotation); 
    y += addedWidth/2 * Math.sin(-rotation); 
    rectw += addedWidth; 
    draw(); 
}) 

document.getElementById('growYTop').addEventListener('click', function() { 
    recty -= addedHeight/2; 
    x += addedHeight/2 * Math.sin(rotation); 
    y -= addedHeight/2 * Math.cos(-rotation); 
    recth += addedHeight; 
    draw(); 
}) 

document.getElementById('growYBottom').addEventListener('click', function() { 
    recty -= addedHeight/2; 
    x -= addedHeight/2 * Math.sin(rotation); 
    y += addedHeight/2 * Math.cos(-rotation); 
    recth += addedHeight; 
    draw(); 
}) 

document.getElementById('rotatePlus').addEventListener('click', function() { 
    rotation += addedRotation; 
    rotation = rotation % (Math.PI*2); 
    if(rotation % Math.PI*2 < 0) { 
    rotation += Math.PI*2; 
    } 
    draw(); 
}) 

document.getElementById('rotateMinus').addEventListener('click', function() { 
    rotation -= addedRotation; 
    rotation = rotation % (Math.PI*2); 
    if(rotation % Math.PI*2 < 0) { 
    rotation += Math.PI*2; 
    } 
    draw(); 
}) 

draw(); 
0

はこれを試してみてください。まったく必要でない場所でctx.translate()を使用しました。それは問題を引き起こした。

<script type="text/javascript"> 
var canvas = document.getElementById('canvas'); 
canvas.width = 300; 
canvas.height= 150; 
var ctx = canvas.getContext('2d'); 
var counter = 0; 

var shape = { 
    top: 120, 
    left: 120, 
    width: 120, 
    height: 60, 
    rotation: Math.PI/180 * 15 
}; 

function draw() { 

    ctx.save(); 
    ctx.clearRect(0, 0, canvas.width, canvas.height); 
    ctx.translate(75,37.5) 

    ctx.rotate(Math.PI/180 * 15); 

    ctx.fillStyle = '#000'; 
    ctx.fillRect(0, 0, shape.width, shape.height); 
    ctx.restore(); 
} 

canvas.addEventListener('click', function() { 

    shape.width = shape.width + 15; 
    window.requestAnimationFrame(draw.bind(this)); 
}); 

window.requestAnimationFrame(draw.bind(this)); 
</script> 
+0

シェイプを中心の周りに回転させる必要があるため、シェイプの中心点に変換する必要があります。 – Jomppe

0

xyが完全にその位置を変化させる形状サイズの半分の値として設定されているので、これが起こっています。

とにかく、図形の中央の点を設定する必要があります。私はこの点をキャンバスの半分であるctx.canvas.[width or height]/2と設定しました。

var h2 = shape.height/2; 
var w2 = shape.width/2; 

var x = (ctx.canvas.width/2) - w2; 
var y = (ctx.canvas.height/2) - h2; 

ctx.save(); 

ctx.clearRect(0, 0, canvas.width, canvas.height); 
ctx.translate(x + (shape.width/2), y + (shape.height/2)); 

ctx.rotate(((shape.rotation * Math.PI)/180) * 15); 

ctx.fillStyle = '#000'; 
ctx.fillRect(-shape.width/2, -shape.height/2, shape.width, shape.height); 
ctx.restore(); 

Fiddle

1

常にローカル座標を使用して図形を定義します。

変換するコンテンツをレンダリングするとき、コンテンツは独自の(ローカル)座標系である必要があります。イメージを考える。左上のピクセルは、レンダリングした場所に関係なく、常に0,0になります。ピクセルはローカル座標にあり、レンダリングされると現在の変換を介して(ワールド)キャンバス座標に移動されます。世界が

var shape = { 
    top: -30, // local coordinates with rotation origin 
    left: -60, // at 0,0 
    width: 120, 
    height: 60, 
    world : { 
     x : canvas.width/2, 
     y : canvas.height/2, 
     rot : Math.PI/12, // 15deg clockwise 
    } 
}; 

を座標として

ですから、そのローカル原点に回転ポイントを作り、そのローカルに設定された座標を使って形を作る場合には(0,0)、表示座標が別々に保存されている今、あなたは「ドン前方と後方を翻訳することで混乱しなければなりません。変換が翻訳を配置する場所

だけ

ctx.save(); 
ctx.translate(shape.world.x,shape.world.y); 
ctx.rotate(shape.world.rot); 
ctx.fillRect(shape.left, shape.top, shape.width, shape.height) 
ctx.restore(); 

やイベント迅速かつ

ctx.setTransform(1,0,0,1,shape.world.x,shape.world.y); 
ctx.rotate(shape.world.rot); 
ctx.fillRect(shape.left, shape.top, shape.width, shape.height); 

局所形状の原点(0,0)を保存し、復元に使用する必要をなくすです。

これは非常

var canvas = document.getElementById('canvas'); 
 
canvas.width = 300; 
 
canvas.height= 150; 
 
var ctx = canvas.getContext('2d'); 
 
ctx.fillStyle = "black"; 
 
ctx.strokeStyle = "red"; 
 
ctx.lineWidth = 2; 
 

 

 
var shape = { 
 
    top: -30, // local coordinates with rotation origin 
 
    left: -60, // at 0,0 
 
    width: 120, 
 
    height: 60, 
 
    world : { 
 
     x : canvas.width/2, 
 
     y : canvas.height/2, 
 
     rot : Math.PI/12, // 15deg clockwise 
 
    } 
 
}; 
 

 
function draw() { 
 
    ctx.setTransform(1,0,0,1,0,0); // to clear use default transform 
 
    ctx.clearRect(0, 0, canvas.width, canvas.height); 
 

 
    // you were scaling the shape, that can be done via a transform 
 
    // once you have moved the shape to the world coordinates. 
 
    ctx.setTransform(1,0,0,1,shape.world.x,shape.world.y); 
 
    ctx.rotate(shape.world.rot); 
 
    
 
    // after the transformations have moved the local to the world 
 
    // you can ignore the canvas coordinates and work within the objects 
 
    // local. In this case showing the unscaled box 
 
    ctx.strokeRect(shape.left, shape.top, shape.width, shape.height); 
 
    // and a line above the box 
 
    ctx.strokeRect(shape.left, shape.top - 5, shape.width, 1); 
 
    
 
    ctx.scale(0.5,0.5); // the scaling you were doing 
 
    ctx.fillRect(shape.left, shape.top, shape.width, shape.height); 
 
} 
 

 
canvas.addEventListener('click', function() { 
 
    shape.width += 15; 
 
    shape.left -= 15/2; 
 
    shape.world.rot += Math.PI/45; // rotate to illustrate location 
 
            // of local origin 
 
    var distToMove = 15/2; 
 
    shape.world.x += Math.cos(shape.world.rot) * distToMove; 
 
    shape.world.y += Math.sin(shape.world.rot) * distToMove; 
 
    draw(); 
 
}); 
 
// no need to use requestAnimationFrame (RAF) if you are not animation 
 
// but its not wrong. Nor do you need to bind this (in this case 
 
// this = window) to the callback RAF does not bind a context 
 
// to the callback 
 
/*window.requestAnimationFrame(draw.bind(this));*/ 
 
requestAnimationFrame(draw); // functionaly identical 
 
// or just 
 
/*draw()*/ //will work
body { font-family : Arial,"Helvetica Neue",Helvetica,sans-serif; font-size : 12px; color : #242729;} /* SO font currently being used */ 
 

 
canvas { border: 1px solid red; }
<canvas id="canvas"></canvas> 
 
<p>Click to grow "and rotate" (I add that to illustrate the local origin)</p> 
 
<p>I have added a red box and a line above the box, showing how using the local coordinates to define a shape makes it a lot easier to then manipulate that shape when rendering "see code comments".</p>

+0

非常にいい答えですが、それでも問題は解決しません。私は一方向にしか形を成長させる必要はなく、 "新しい"中心の周りを回転する必要があります。あなたの例では、shape.left - = 15/2を削除すると、シェイプは古い中心点を中心に回転します。 – Jomppe

+0

@Jomppeそれを成長させてその方向に沿って移動したい場合は、 'world.x + = Math.cos(world.rot)* dist;でworld coordsに移動するための距離を追加します。 – Blindman67

+0

@Jomppe回答スニペットを更新して、形のx軸に沿った世界空間の動きを表示しました。 – Blindman67

関連する問題