<canvas/>
当用户动态调整其大小时,我遇到了使我的 HTML呈现清晰的问题。我的意思是,用户可以使用两个按钮随意调整画布的大小。
从图像中可以看出,网格线是模糊的。我非常熟悉缩放 HTML 画布上下文以补偿模糊的各种方法(使用 DPR 缩放并仅从“pixel.5”点绘制等,更多内容见下文)。但是,我无法弄清楚为什么它在我的特定情况下不起作用。
这是完整的代码:
以下是我尝试过的一些注释以及有关我的系统设置方式的更多详细信息:
- 为了补偿画布通过缩放变得越来越小,我在缩放时缩放 dpr 值本身。这实际上有一个积极的影响,因为它使网格数字 (1,2,3,4,5,6,7,8,9) 在使用缩放功能时渲染得更好。如果您只有机器的原始 dpr(您可以通过取消注释
updateDPR()
函数中的中间内容并使用缩放按钮在代码框中自行检查),画布网格的渲染效果会稍微好一些,但在缩放时整个画布的缩放比例要差得多。
// in ./utils.js
export function updateDPR(zoomValue) {
let dpr = window.devicePixelRatio || 1;
let zoomValueFlipped = 100 - zoomValue;
let valueToAdd = (dpr / 100) * zoomValueFlipped;
dpr += valueToAdd;
return dpr;
}
- 画布本身嵌入在容器 div 中:
// render method of ./canvas.jsx
<div className="d-flex justify-content-center p-2">
<div ref={containerRef} style={{ width: canvasWidth }}>
<canvas ref={canvasRef} className="w-100" />
</div>
</div>
我实现画布缩放效果的方式其实就是 <div ref={containerRef}>
通过canvasWidth
状态来调整大小。这使我可以<canvas/>
更自由地操纵大小,同时确保它始终在容器中保留某个“固定”大小。你可以在这里更仔细地检查它:
// in ./canvas.jsx
// resize canvas on window resize
useEffect(() => {
if (canvasRef.current) {
window.addEventListener("resize", forceUpdate);
return () => {
window.removeEventListener("resize", forceUpdate);
};
}
}, [canvasRef]);
// Set the containerWidth when the parent DIV mounts,
// and whenever we resize the window.
useEffect(() => {
if (containerRef.current) {
setContainerWidth(containerRef.current.parentNode.clientWidth);
}
}, [containerRef, windowResizeUpdate]);
// Set new canvasWidth's if the container width changes
// or when a new zoom value is registered.
useEffect(() => {
if (containerWidth) {
const canvasW = (containerWidth / 100) * zoomValue;
const newDpr = utils.updateDPR(zoomValue);
const canvasH = canvasW;
const {
scaledCanvasWidth,
scaledCanvasHeight
} = utils.getScaledCanvasDim(newDpr, canvasW, canvasH);
setCanvasWidth(canvasW);
setScaledCanvasWidth(scaledCanvasWidth);
setScaledCanvasHeight(scaledCanvasHeight);
setNewDpr(newDpr);
}
}, [containerWidth, zoomValue]);
// ....
// ... render method below
- 正如上面简要提到的,我使用一种方法来确保每条网格线都以小数点像素点开始和结束。更具体地说,在“.5”位置。
// in ./utils.js
iexport function nrstPointZero(val, width) {
// round to nearest 0.5
let pointZero = Math.round(val - 0.5) + 0.5;
// if its near the width, floor it and round 0.5 down.
if (pointZero >= Math.floor(width)) {
pointZero = Math.floor(width) - 0.5;
}
return pointZero;
}
我不知道这是否有任何特殊效果,但它似乎确实会影响渲染质量。您可以在下面链接的“diveintohtml5.info/canvas.html”页面上阅读更多相关信息。
- 在主渲染逻辑中,我将
canvas.height
and设置canvas.width
为它们各自的 dpr 缩放值。这显然是每个人在使用 dpr 缩放值时为使画布正确渲染所做的事情。但是,如果我ctx.scale(newDpr, newDpr)
按照我应该使用的方式使用 ,那么画布会从其容器中吹出。这是不寻常的,因为设置上面的canvas.height
和 ```canvas.width`` 应该可以解决这个问题。因此,我怀疑我在容器 div 中渲染画布的方式有些可疑(在第 nr.2 点中提到)。但是,我无法弄清楚是什么..
// in canvas.jsx
// paint canvas
useLayoutEffect(() => {
if (canvasRef.current && canvasWidth) {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
const gridSize = 10;
const offset = 40;
const font = "30px Arial";
canvas.width = scaledCanvasWidth;
canvas.height = scaledCanvasHeight;
// this makes the canvas blow out of the container.
// But it shouldn't because I define the canvas.height and width stricktly above.
//ctx.scale(newDpr, newDpr);
utils.drawBackground(ctx, scaledCanvasWidth, scaledCanvasHeight);
utils.drawGrid(ctx, scaledCanvasWidth, scaledCanvasHeight, gridSize);
utils.drawNumbers(ctx, scaledCanvasWidth, gridSize, 1, offset, font);
}
}, [
canvasRef,
canvasWidth,
zoomValue,
scaledCanvasWidth,
scaledCanvasHeight,
newDpr
]);
// ...
// ... render method below
我一直在尝试解决这个问题很长时间,但没有成功,因为有这么多活动部件,可以这么说。希望那里的其他人有一些见解可以与我分享。
有用的链接: