调整形状宽度时,旋转形状在y轴上移动

时间:2016-08-08 11:14:05

标签: javascript html5-canvas

我正在尝试在画布上调整旋转的形状。我的问题是,当我调用渲染功能时,形状会根据形状角度开始“漂移”。我该如何防止这种情况?

我做了一个简化的小提示演示了这个问题,当点击画布时,形状会长大,并且由于某种原因它会向上漂移。

这是小提琴:https://jsfiddle.net/x5gxo1p7/

@POST("/save")
    public void CreateAccount(
            @Body Map<String, String> data,
             Callback<Response> callback);

在“真实”代码中,单击并移动调整大小句柄时会调整形状的大小,但我认为此示例充分展示了该问题。

编辑:更新小提琴以澄清问题:

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

4 个答案:

答案 0 :(得分:1)

始终使用局部坐标来定义形状。

在渲染要转换的内容时,内容应位于其自己的(本地)坐标系中。想象一下图像。无论您在何处渲染,左上角的像素始终为0。像素位于其局部坐标处,在渲染时,它们通过当前变换移动到(世界)画布坐标。

因此,如果您将坐标设置为局部的形状,使旋转点位于其本地原点(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
    }
};

现在你不必乱用前后翻译......等等等等。

只需

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>

答案 1 :(得分: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>

答案 2 :(得分: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

答案 3 :(得分: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();