Javascript 如何计算在应用缩放变换的情况下绘制到画布中最近像素的偏移量?

Javascript 如何计算在应用缩放变换的情况下绘制到画布中最近像素的偏移量?,javascript,html5-canvas,pixel,matrix-multiplication,Javascript,Html5 Canvas,Pixel,Matrix Multiplication,我正在尝试在HTML画布上绘制一个矩形,该矩形具有像素完美的定位,与应用于画布变换的平移和缩放无关(假设在此场景中不使用旋转) 在这种情况下,我总是希望矩形的边框在屏幕上的宽度为1px,不考虑应用比例时矩形的缩放程度(我在下面的演示中使用了该比例),但是我也希望它在精确的像素坐标处绘制,以便不应用抗锯齿 在下面的代码中,我非常确定我必须操作ctx.rect的参数来为每个位置添加偏移量,以便将其四舍五入以精确显示在屏幕上最近的像素上。但我不确定用什么数学来达到目的 正如您在本演示中看到的,应用1.

我正在尝试在HTML画布上绘制一个矩形,该矩形具有像素完美的定位,与应用于画布变换的平移和缩放无关(假设在此场景中不使用旋转)

在这种情况下,我总是希望矩形的边框在屏幕上的宽度为1px,不考虑应用比例时矩形的缩放程度(我在下面的演示中使用了该比例),但是我也希望它在精确的像素坐标处绘制,以便不应用抗锯齿

在下面的代码中,我非常确定我必须操作ctx.rect的参数来为每个位置添加偏移量,以便将其四舍五入以精确显示在屏幕上最近的像素上。但我不确定用什么数学来达到目的

正如您在本演示中看到的,应用1.5缩放后,矩形不再绘制在像素完美坐标上

const画布=[
{
ctx:document.getElementById('canvasOriginal').getContext('2d'),
比例:1,
translateX:0,
翻译:0
},
{
ctx:document.getElementById('canvasZoomed').getContext('2d'),
比例:1.5,
translateX:-0.5,
translateY:-0.5
}
];
用于(画布的常数{ctx,scale,translateX,translateY}){
ctx.translate(translateX,translateY);
比例尺(比例尺,比例尺);
ctx.beginPath();
ctx.rect(1.5,1.5,4,4);
ctx.线宽=1/刻度;
ctx.strokeStyle=‘红色’;
ctx.stroke();
}
画布{
边框:1px实心#ccc;
图像渲染:像素化;
图像渲染:清晰的边缘;
宽度:100px;
高度:100px;
}

好的,我。。。我自己想出来的。可以使用DOMPoint类通过画布的变换矩阵变换坐标。因此,我编写了一个函数,通过矩阵变换点,将其四舍五入到最接近的半像素(因为在中心渲染1像素宽的笔划,例如像素的半点),然后通过矩阵的逆方向将其变换回来

这将导致将缩放的1px笔划渲染到屏幕上最近的像素

希望这个问题对其他浏览互联网的人有用,因为在发布这个问题之前,我花了很长时间才解决这个问题

const画布=[
{
ctx:document.getElementById('canvasOriginal').getContext('2d'),
比例:1,
translateX:0,
翻译:0
},
{
ctx:document.getElementById('canvasZoomed').getContext('2d'),
比例:1.5,
translateX:-0.5,
translateY:-0.5
}
];
const roundpointtohalf identitycoordinates=(ctx,x,y)=>{
设点=新点(x,y);
point=point.matrixTransform(ctx.getTransform());
点x=数学四舍五入(点x-0.5)+0.5;
点y=数学圆(点y-0.5)+0.5;
point=point.matrixTransform(ctx.getTransform().inverse());
返回点;
};
用于(画布的常数{ctx,scale,translateX,translateY}){
ctx.translate(translateX,translateY);
比例尺(比例尺,比例尺);
ctx.beginPath();
const topLeft=圆点到半标识坐标(ctx,1.5,1.5);
const bottomRight=圆点到半标识坐标(ctx,5.5,5.5);
ctx.rect(
左上角.x,
左上角,
右下角.x-左上角.x,
右下角。y-左上角。y
);
ctx.线宽=1/刻度;
ctx.strokeStyle=‘红色’;
ctx.stroke();
}
画布{
边框:1px实心#ccc;
图像渲染:像素化;
图像渲染:清晰的边缘;
宽度:100px;
高度:100px;
}

给定的答案有很多隐藏的开销,获取和创建DOM矩阵,创建DOM点,转换点,然后反转矩阵并转换回原来的值是100+乘法和加法(忽略内存管理开销)

由于您只进行缩放,没有平移或旋转,因此可以在一次除法、八次乘法和八次加法/减法中完成,并且至少要快一个数量级

例子
const画布=[
{ctx:document.getElementById('canvasOriginal').getContext('2d'),比例:1},
{ctx:document.getElementById('canvasZoomed').getContext('2d'),比例:1.5},
];
函数路径RectPixelAligned(ctx、比例、x、y、w、h){
const invScale=1/刻度;
x=(数学四舍五入(x*刻度)+0.5)*刻度;
y=(数学四舍五入(y*刻度)+0.5)*刻度;
w=(数学四舍五入((x+w)*比例)-0.5)*投资比例-x;
h=(数学四舍五入((y+h)*刻度)-0.5)*invScale-y;
ctx.rect(x,y,w,h);
}
用于(画布的常数{ctx,比例}){
比例尺(比例尺,比例尺);
ctx.beginPath();
pathRectPixelAligned(ctx,比例,1,1,4,4)
ctx.lineWidth=1;
ctx.strokeStyle=‘红色’;
setTransform(1,0,0,1,0,0);
ctx.stroke();
}
画布{
边框:1px实心#ccc;
图像渲染:像素化;
图像渲染:清晰的边缘;
宽度:100px;
高度:100px;
}


这是否回答了您的问题?大概我将使用它。不,这是一个有趣的技巧,但它呈现的结果与我提供的演示完全相同。感谢性能提示,但当我对变换矩阵应用非整数转换时,此示例中断。我意识到我在这个问题上是含蓄的,因为我只是说忽略旋转。我正在应用平移、旋转和缩放。我只是希望它看起来像素完美的旋转是90度的倍数。这就是为什么我说忽略旋转,因为像素完美在那个场景中并不重要。但是,是的,我从来没有说过忽略翻译。