javascript - Rotated shape is moving on y-axis when resizing shape width -
i'm trying resize rotated shape on canvas. problem when call rendering function, shape starts "drifting" depending on shape angle. how can prevent this?
i've made simplified fiddle demonstrating problem, when canvas clicked, shape grown , reason drifts upwards.
here's fiddle: 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>
in "real" code shape resized when resize-handle clicked , moved think example demonstrates problem sufficiently.
edit: updated fiddle clarify issue:
always use local coordinates define shapes.
when rendering content intended transformed content should in own (local) coordinate system. think of image. top left pixel @ 0,0 on image no matter render it. pixels @ local coordinates, when rendered moved (world) canvas coordinates via current transformation.
so if make shape coordinates set local, making rotation point @ local origin (0,0) display coordinates stored separately world coordinates
var shape = { top: -30, // local coordinates rotation origin left: -60, // @ 0,0 width: 120, height: 60, world : { x : canvas.width / 2, y : canvas.height / 2, rot : math.pi / 12, // 15deg clockwise } };
now don't have mess translating forward , back... blah blah total pain.
just
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();
or event quicker , eliminating need use save , 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);
the local shape origin (0,0) transformation places translation.
this simplifies lot of work has done
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 rotation origin left: -60, // @ 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); // clear use default transform ctx.clearrect(0, 0, canvas.width, canvas.height); // scaling shape, can done via transform // once have moved shape world coordinates. ctx.settransform(1,0,0,1,shape.world.x,shape.world.y); ctx.rotate(shape.world.rot); // after transformations have moved local world // can ignore canvas coordinates , work within objects // local. in case showing unscaled box ctx.strokerect(shape.left, shape.top, shape.width, shape.height); // , line above box ctx.strokerect(shape.left, shape.top - 5, shape.width, 1); ctx.scale(0.5,0.5); // scaling 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 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 use requestanimationframe (raf) if not animation // not wrong. nor need bind (in case // = window) callback raf not bind context // callback /*window.requestanimationframe(draw.bind(this));*/ requestanimationframe(draw); // functionaly identical // or /*draw()*/ //will work
body { font-family : arial,"helvetica neue",helvetica,sans-serif; font-size : 12px; color : #242729;} /* font being used */ canvas { border: 1px solid red; }
<canvas id="canvas"></canvas> <p>click grow "and rotate" (i add illustrate local origin)</p> <p>i have added red box , line above box, showing how using local coordinates define shape makes lot easier manipulate shape when rendering "see code comments".</p>
Comments
Post a Comment