我已经编写了一个用于创建等轴测平面的绘图函数。该函数接受以下参数。
widthIso
—平面的等轴测(倾斜)侧宽度heightIso
—平面的等轴测高度imgObj
-包含图案图像的对象(在本问题中不重要)
我制作了一个JSFiddle以使其更加清晰:https://jsfiddle.net/Le45oo71/10/
我的draw
函数创建了一个画布,因此我可以使用该画布在主画布上绘制它。(这是预呈现)。
该功能执行以下操作:
- 创建一个屏幕外画布
- 计算并设置画布大小
- 将上下文转换为等轴测投影
- 用
imgObj
中的图像绘制一个矩形
当然,由于上下文的转换,期望等轴测平面的斜边具有所需的长度有点天真。我试了很多,但我不知道如何计算一个数字,所以在变换之后,得到的平面具有所需的边长。
我希望在绘制矩形时,平面(未变换)矩形的边一定要长一点。这样在变换之后,平面边就有了所需的长度。
变换矩阵
简单的答案在底部。如果你想知道如何阅读其余部分。
集变换和疯狂的abc的
您可以使用ctx.setTransform(a, b, c, d, e, f);
来解决此问题。使用它而不是ctx.transform
、ctx.rotate
、ctx.scale
或ctx.translate
意味着您不必担心任何以前的变换,因为它会替换现有变换,而不是与现有变换相乘。
魔术参数可以最好地理解为x和y轴的方向和比例以及原点的屏幕坐标。
所有值都与像素坐标有关。轴描述单个像素的方向和大小,a,b表示像素的顶部,c,d表示左侧。e、 f是屏幕上显示变换坐标0,0的坐标,称为原点。
默认情况下,a = 1
上的像素为一个像素,b = 0
下的像素为零。该像素在c = 0
上为零像素,在d = 1
下为一像素。原点位于左上角的e = 0
、f = 0
;
从而设置默认变换ctx.setTransform(1,0,0,1,0,0);
让我们把这些变量称为更有意义的变量。它们是向量,对于x轴,它有一个x和y分量a = xAX
(xa轴y分量)和b = xAY
(xay轴)。对于y轴CCD_ 20和CCD_。(我更喜欢xAx
、xAy
、yAx
、yAy
与惯例略有不同),对于来源ox
、oy
因此ctx.setTransform(xAx, xAy, yAx, yAy, ox, oy)
使整个事情稍微清晰(不那么像泥)。
两个轴是独立的,可以指向任何方向,也可以是任何长度,这使我们能够创建等轴测投影等。比例由轴的长度决定。x刻度是x轴的长度,y刻度是y轴的长度。
为了创建轴的矢量,我们使用cos和sin,因为它们返回角度的x和y分量。因此,如果我们希望x轴为45度(Math.PI/4,弧度),我们可以通过获得矢量
var direction = Math.PI/4; // 45 deg down and right
var xAx = Math.cos(direction);
var xAy = Math.sin(direction);
如果我们想要135度的y轴(Math.PI*(3/4)弧度)
var direction = Math.PI * (3/4); // 135 deg down and left
var yAx = Math.cos(direction);
var yAy = Math.sin(direction);
Cos和sin总是创建一个单位向量(一个1单位长的向量),这意味着向量的比例是1(实际上没有比例)
若要更改矢量的比例,请将x和y分量相乘。因此,在上面的基础上,让我们缩放两个轴。
var scale = 2; // zoom by 2
direction = Math.PI/4; // 45 deg down and right
xAx = Math.cos(direction) * scale;
xAy = Math.sin(direction) * scale;
direction = Math.PI * (3/4); // 135 deg down and left
yAx = Math.cos(direction) * scale;
yAy = Math.sin(direction) * scale;
现在我们可以将原点设置为画布的中心
var ox = ctx.canvas.width/2;
var oy = ctx.canvas.height/2;
所以用它来创建转换
ctx.setTransform(xAx, xAy, yAx, yAy, ox, oy); // set the transform
ctx.strokeRect(0,0,20,10); // draws a rectangle 40 pixels ( scale 2) at 45 deg
// and 20 pixels at 135 deg centered on the canvas
希望你能看到这是多么简单。
答案
如果你有一张需要缩放的图像,以便侧面达到所需的尺寸,我们需要找到缩放比例。假设图像宽度为100像素,我们需要以300像素显示它。为了得到将屏幕大小除以图像大小300/100 = 3
的比例,比例为3。
比例与轴方向无关。
我们可以把这些放在一起作为一个函数,它取x轴和y轴的角度,图像宽度和高度的大小,显示宽度和高度以及原点的位置。然后我们计算比例,创建轴并设置变换
function createTransform(angX, angY, imgWidth, imgHeight, displayWidth, displayHeight, originX, originY){
var scaleX = displayWidth / imgWidth; // get x scale
var scaleY = displayHeight / imgHeight; // get y scale
// create the x axis
var xAx = Math.cos(angX) * scaleX;
var xAy = Math.sin(angX) * scaleX;
// create the y axis
var yAx = Math.cos(angY) * scaleY;
var yAy = Math.sin(angY) * scaleY;
// set the transform
ctx.setTransform(xAx, xAy, yAx, yAy, originX, originY);
}
现在你可以绘制图像
// assume image is a loaded image
// axis at 45 and 135 deg make the display width and height 200,300 at the canvas center
createTransform(Math.PI / 4, Math.PI * (3 / 4), image.width, image.height, 200, 300, ctx.canvas.width / 2, ctx.canvas.height / 2)
ctx.drawImage(image,0,0); // drawn to fit requirements.
复制粘贴简单答案
上面的内容与你想要的不匹配,因为你使用的是图案,角度也有点不同,但如果我只是为你做了解决方案,除了立即剪切粘贴解决方案外,你不会从中得到太多。
function draw(widthIso, heightIso, imgObj) {
// rather than the messing around with the and rotations hand code the axis
// x axis
var xAx = 1;
var xAy = 0.5;
// y axis
var yAx = -1;
var yAy = 0.5;
// need the length of the axis so we can calculate a scale adjustment
var scaleX = Math.sqrt(xAx * xAx + xAy * xAy); // pythagoras to get the length of x axis
var scaleY = Math.sqrt(yAx * yAx + yAy * yAy); // pythagoras to get the length of y axis
// now we know how big a pixel is in isometric projection
// assuming you don't want to change the image pixel size from what you have in the fiddle
// I will workout the draw size
var drawWidth = widthIso / scaleX;
var drawHeight = heightIso / scaleY;
// get the width as sum of the product of axis x & y, x components
var canvasWidth = xAx * widthIso + yAx * heightIso;
// get the width as sum of the product of axis x & y, y components
var canvasHeight = xAy * widthIso + yAy * heightIso;
// create the canvas rounding up for size so we don't lose any pixels
var ca = document.createElement('canvas');
ca.width = Math.ceil(canvasWidth);
ca.height = Math.ceil(canvasHeight);
var ctx = ca.getContext('2d');
// now get the origin
var originY = 0; // always at the top
var originX = yAx * heightIso; // in by x component of height
// now just set the transform
ctx.setTransform(xAx, xAy, yAx, yAy, originX, originY);
// create pattern
var pattern = ctx.createPattern(imgObj.img, 'repeat');
ctx.fillStyle = pattern;
// draw with the corrected width and height
ctx.fillRect(0, 0, drawWidth, drawHeight);
// return the canvas image
return {canvas: ca, ctx: ctx, toLeft: originX};
}
这就完成了。像素完美创建画布图像时不要忘记四舍五入,否则边缘可能会丢失一个像素
这是我用来测试它的代码。将红线和蓝线移动到中心,使它们的长度分别为200和300像素。
// to visualise with red and blue lines.
var x = Math.cos(Math.atan2(0.5,-1))*150 + 230;
var y = Math.sin(Math.atan2(0.5,-1))*150 + 50;
ctx.strokeStyle = 'red';
ctx.beginPath();
ctx.moveTo(x, y); // no guessing
ctx.lineTo(x + Math.cos(Math.atan2(0.5,1))*200, y + Math.sin(Math.atan2(0.5,1))*200); //
ctx.stroke();
var x = Math.cos(Math.atan2(0.5,1))*100 + 230;
var y = Math.sin(Math.atan2(0.5,1))*100 + 50;
ctx.strokeStyle = 'blue';
ctx.beginPath();
ctx.moveTo(x, y); //
ctx.lineTo(x + Math.cos(Math.atan2(0.5,-1))*300, y + Math.sin(Math.atan2(0.5,-1))*300); //
ctx.stroke();