2016-08-08 8 views
6

Próbuję zmienić rozmiar obróconego kształtu na płótnie. Mój problem polega na tym, że gdy wywołuję funkcję renderowania, kształt zaczyna "dryfować" w zależności od kąta kształtu. Jak mogę temu zapobiec?Obracany kształt porusza się na osi Y podczas zmiany szerokości kształtu

Zrobiłem uproszczone skrzypce demonstrujące problem, po kliknięciu płótno, kształt rośnie i z jakiegoś powodu dryfuje w górę.

Oto skrzypce: 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> 

W „prawdziwym” kodu kształt jest zmieniany podczas zmiany rozmiaru rękojeść kliknięciu i przeniósł ale myślę, że ten przykład pokazuje problem w wystarczającym stopniu.

EDIT: zaktualizowane skrzypce w celu wyjaśnienia problemu:

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

+0

To płynął z powodu 'ctx.translate (x, y);' i 'ctx.translate (-x, -y); '. Spróbuj je skomentować! - https://jsfiddle.net/Pugazh/x5gxo1p7/3/ – Pugazh

+0

Potrzebuję przekładów, aby obrócić kształt wokół jego środka. – Jomppe

Odpowiedz

0

znalazł rozwiązanie problemów było to, że nie było obliczenie współrzędnych nowy punkt środkowy.

Nowy skrzypacz roztworem: 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

Spróbuj tego. Użyto ctx.translate(), gdy nie było to konieczne. To spowodowało problemy.

<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

Muszę obrócić kształt wokół jego środka, więc muszę przetłumaczyć na środkowy punkt kształtu. – Jomppe

0

Dzieje się tak dlatego, że x i y są ustawione jako połowa wartości wielkości kształtu, który całkowicie zmienia swoje położenie.

W każdym razie należy ustawić punkt dla środka kształtu. Ustawiłem ten punkt jako ctx.canvas.[width or height]/2, połowa płótna.

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

Zawsze używaj lokalnych współrzędnych do definiowania kształtów.

Podczas renderowania treści, które mają zostać przekształcone, zawartość powinna znajdować się we własnym (lokalnym) układzie współrzędnych. Pomyśl o obrazie. górny lewy piksel ma zawsze 0,0 na obrazie bez względu na to, gdzie go renderujesz. Piksele znajdują się w ich lokalnych współrzędnych, gdy są renderowane, są przenoszone do współrzędnych (świat) płócien poprzez bieżącą transformację.

Więc jeśli uczynić swój kształt ze współrzędnymi ustawiony na swój lokalny, dzięki czemu punkt obrotu na swoim lokalnym pochodzenia (0,0) współrzędne wyświetlane są przechowywane oddzielnie jako świata współrzędne

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 
    } 
}; 

Teraz don” Trzeba zadzierać z tłumaczeniem do przodu i do tyłu ... blah blah całkowity ból.

Wystarczy

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(); 

lub wydarzenie szybciej i eliminuje potrzebę stosowania zapisać i przywrócić

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); 

Lokalne pochodzenie kształt (0,0), gdzie jest transformacja umieszcza tłumaczenie.

To znacznie upraszcza wiele prac, które ma być wykonane

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

Bardzo ładna odpowiedź, ale nie rozwiązuje problemu. Muszę wyhodować kształt tylko w jednym kierunku, ale obracać wokół jego "nowego" centrum. W twoim przykładzie kształt obraca się wokół starego punktu środkowego, gdy usuwam shape.left - = 15/2. – Jomppe

+0

@Jomppe Jeśli chcesz go wyhodować i poruszać się wzdłuż jego kierunku. Dodaj odległość, aby przejść do świata współrzędnych z 'world.x + = Math.cos (world.rot) * dist; world.y + = Math.sin (world.rot) * dist; ' – Blindman67

+0

@ Jomppe Mam zaktualizowany fragment odpowiedzi, aby pokazać ruch w przestrzeni świata wzdłuż osi x kształtów. – Blindman67

Powiązane problemy