【发布时间】:2021-01-21 22:17:47
【问题描述】:
我想使用 Javascript 旋转绘制到 Canvas 的图像,但不使用 Context 的 rotate 方法(或任何 JS 库)。原因是因为这表明我在使用另一种语言时遇到的问题,我无法访问这些。
我已经创建了初稿(如下),但我的实现存在两个问题:复制的位图的逐像素旋转非常缓慢,并且在像素之间留下了间隙。
是否有更快的方法将位图数据放置在不会留下间隙的角度?请让我知道,如果你有任何问题。谢谢。
const c1 = document.getElementById("c1");
const c2 = document.getElementById("c2");
const slider = document.getElementById('slider');
const removeAllChildNodes = (parent) =>
{
while (parent.firstChild) {
parent.removeChild(parent.firstChild);
}
}
const rotatedBoundingBox = (width,height,rotation) =>
{
let rot_w = Math.abs(width * Math.cos(rotation)) + Math.abs(height * Math.sin(rotation));
let rot_h = Math.abs(width * Math.sin(rotation)) + Math.abs(height * Math.cos(rotation));
return {width:rot_w,height:rot_h};
}
const render = (rotation) => {
const canvas = document.createElement("canvas");
const canvas2 = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const ctx2 = canvas2.getContext("2d");
let txt = "Hello World";
ctx.font = '20px sans-serif';
let metrics = ctx.measureText(txt);
canvas.width = metrics.width;
canvas.height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
ctx.fillStyle = "#636674";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.font = '20px sans-serif';
ctx.textAlign = "center";
ctx.fillStyle = "#ffaefa";
ctx.fillText(txt, canvas.width/2, canvas.height);
const { width, height } = rotatedBoundingBox(canvas.width,canvas.height,rotation);
canvas2.width = width;
canvas2.height = height;
cos = Math.cos(-rotation);
sin = Math.sin(-rotation);
cx = canvas.width/2;
cy = canvas.height/2;
for (x = 0; x < canvas.width; x++)
{
for (y = 0; y < canvas.height; y++)
{
var imgd = ctx.getImageData(x, y, 1, 1);
var pix = imgd.data;
var red = pix[0];
var green = pix[1];
var blue = pix[2];
var alpha = pix[3];
nx = ((cos * (x-cx)) + (sin * (y - cy))+canvas2.width/2);
ny = ((cos * (y-cy)) - (sin * (x - cx))+canvas2.height/2);
ctx2.putImageData(imgd, nx, ny);
}
}
removeAllChildNodes(c1);
removeAllChildNodes(c2);
c1.appendChild(canvas);
c2.appendChild(canvas2);
}
slider.addEventListener('input', (e) => {
document.getElementById('currentDegree').innerHTML = e.target.value;
const rad = parseInt(e.target.value) * Math.PI / 180;
render(rad);
})
document.getElementById('currentDegree').innerHTML = slider.value;
render(parseInt(slider.value) * Math.PI / 180);
canvas
{
border: 1px solid rgba(0,0,0,0.2);
}
main
{
display: flex;
}
.contain
{
display: inline-block;
}
<div>
<input id="slider" type="range" min="0" max="360" value="66"/>
<span>rotation: <span id="currentDegree">0</span>°</span>
</div>
<main>
<div id="c1" class="contain">
</div>
<div id="c2" class="contain">
</div>
</main>
【问题讨论】:
-
你试过
transformcss吗? -
@Francisco 感谢您的回复。正如帖子中提到的,这不适用于最终会以 HTML 形式出现的内容,因此我将无法使用 CSS。我相信解决方案将是一个更有效的绘制循环。
-
每次调用 getImageData 时,位图都会从 GPU 传输到 CPU,然后再传输到 GPU。它非常慢,所以尽量少做。幸运的是,您可以获得多个像素。所以获取完整图像的图像数据,循环通过这个单一的图像数据并只组成一个新的图像数据。一旦这个新的 ImageData 完成,就将它放在你的上下文中。
标签: javascript html canvas graphics trigonometry