正如您在演示中所看到的,L形状从屏幕顶部被裁剪掉,应该旋转180度并与左上角齐平。我发现有两件事情没有按预期发挥作用,第一件事就是当我将ctx.translate(x, y)
更改为ctx.moveTo(x, y)
并将形状位置增加到100, 100
时,它会在翻译时移动超过100px,其中moveTo似乎准确。第二个是在ctx.stroke()
之后使用否定翻译对形状位置没有影响。
var shape = {};
function draw(shape) {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
var x = shape.position.x + 0.5;
var y = shape.position.y + 0.5;
ctx.translate(x, y);
ctx.translate(shape.width * shape.scale/2, shape.height * shape.scale/2);
ctx.rotate(shape.orientation * Math.PI/180);
ctx.beginPath();
for (var i = 0; i < shape.points.length; i++) {
x = shape.points[i].x * shape.scale + shape.position.x + 0.5;
y = shape.points[i].y * shape.scale + shape.position.y + 0.5;
ctx.lineTo(x, y);
}
ctx.strokeStyle = '#fff';
ctx.stroke();
ctx.translate(-shape.width * shape.scale/2, -shape.height * shape.scale/2);
ctx.restore();
}
}
// L Shape
shape.points = [];
shape.points.push({ x:0, y:0 });
shape.points.push({ x:0, y:3 });
shape.points.push({ x:2, y:3 });
shape.points.push({ x:2, y:2 });
shape.points.push({ x:1, y:2 });
shape.points.push({ x:1, y:0 });
shape.points.push({ x:0, y:0 });
shape.position = {x: 0, y: 0};
shape.scale = 30;
shape.width = 3;
shape.height = 2;
shape.orientation = 180;
draw(shape);
#canvas {
background: #272B34; }
<canvas id="canvas" width="400" height="600"></canvas>
答案 0 :(得分:1)
进行2D变换的最简单方法是通过setTransform函数获取6个数字,2个向量表示X和y轴的方向和比例,以及一个坐标代表新原点。
与其他依赖于当前状态的变换函数不同,setTransform在调用之前不会受到任何变换的影响。
为具有正方形(x和y比例相同)的矩阵设置变换,并且y轴与x成90度(无偏斜),旋转如下
// x,y the position of the orign
function setMatrix(x,y,scale,rotate){
var xAx = Math.cos(rotate) * scale; // the x axis x
var xAy = Math.sin(rotate) * scale; // the x axis y
ctx.setTransform(xAx, xAy, -xAy, xAx, x, y);
}
//use
setMatrix(100,100,20,Math.PI / 4);
ctx.strokeRect(-2,-2,4,4); // draw a square centered at 100,100
// scaled 20 times
// and rotate clockwise 45 deg
回应评论中的问题。
你能解释一下你为什么使用cos和sin作为轴吗?
我使用Math.sin
和Math.cos
计算X轴,从而计算Y轴(因为y与x成90度),因为它比单独添加旋转稍快一些转变。
当您使用除setTransform
之外的任何变换函数时,您正在进行矩阵乘法。下一个代码段是使用ctx.rotate
,ctx.scale
,ctx.translate
或ctx.transform
// mA represent the 2D context current transform
mA = [1,0,0,1,0,0]; // default transform
// mB represents the transform to apply
mB = [0,1,-1,0,0,0]; // Rotate 90 degree clockwise
// m is the resulting matrix
m[0] = mA[0] * mB[0] + mA[2] * mB[1];
m[1] = mA[1] * mB[0] + mA[3] * mB[1];
m[2] = mA[0] * mB[2] + mA[2] * mB[3];
m[3] = mA[1] * mB[2] + mA[3] * mB[3];
m[4] = mA[0] * mB[0] + mA[2] * mB[1] + mA[4];
m[5] = mA[1] * mB[0] + mA[3] * mB[1] + mA[5];
正如你所看到的那样,有12次乘法和6次加法以及需要内存来保存中间值,如果调用是ctx.rotation
,那么角度的sin和cos也将被完成。这一切都是在JavaScript引擎中的本机代码中完成的,因此比在JS中更快,但是通过在JavaScript中计算轴来逐步调整矩阵乘法会减少工作量。使用setTransform
只需替换当前矩阵,不需要执行矩阵乘法。
答案的setMatrix
功能的替代方案可以是
function setMatrix(x,y,scale,rotate){
ctx.setTransform(scale,0,0,scale, x, y); // set current matrix
ctx.rotate(rotate); // multiply current matrix with rotation matrix
}
做同样的事情并且看起来更干净,但速度较慢,当你想要做性能非常重要的游戏时,通常称为函数应尽可能快。
setMatrix
功能那么我如何在我的演示中将其用于自定义形状,如L?
更换绘图功能。顺便说一句,你应该在任何绘制函数之外获取上下文。
// assumes ctx is the 2D context in scope for this function.
function draw(shape) {
var i = 0;
setMatrix(shape.position.x, shape.position.y, shape.scale, shape.orientation); // orientation is in radians
ctx.strokeStyle = '#fff';
ctx.beginPath();
ctx.moveTo(shape.points[i].x, shape.points[i++].y)
while (i < shape.points.length) {
ctx.lineTo(shape.points[i].x, shape.points[i++].y);
}
ctx.closePath(); // draw line from end to start
ctx.stroke();
}
在您的代码中,您存储的行使其原点(0,0)位于左上角。定义形状时,您应该根据其局部坐标来定义它们。这将定义旋转点和缩放,并表示将在变换原点(位置x,y)处的坐标。
因此,您应该在其原点定义您的形状
function createShape(originX, originY, points){
var i;
const shape = [];
for(i = 0; i < points.length; i++){
shape.push({
x : points[i][0] - originX,
y : points[i][1] - originY,
});
}
return shape;
}
const shape = {};
shape.points = createShape(
1,1.5, // the local origin relative to the coords on next line
[[0,0],[0,3],[2,3],[2,2],[1,2],[1,0]] // shape coords
);