Javascript 在HTML5上缩放<;帆布>;文本没有像素化?

Javascript 在HTML5上缩放<;帆布>;文本没有像素化?,javascript,html,canvas,html5-canvas,Javascript,Html,Canvas,Html5 Canvas,让我们用 var canvas = document.getElementById('myCanvas'), ctx = canvas.getContext('2d'), ctx.textBaseline = 'top'; ctx.textAlign = 'left'; ctx.font = '14px sans-serif'; ctx.fillText('Bonjour', 10, 10); 在文本上缩放画布时,可以看到像素化 有没有一种方法可以在画布上缩放而不必对文本进行像素化?在画布上填

让我们用

var canvas = document.getElementById('myCanvas'),
ctx = canvas.getContext('2d'),
ctx.textBaseline = 'top';
ctx.textAlign = 'left';
ctx.font = '14px sans-serif';
ctx.fillText('Bonjour', 10, 10);
在文本上缩放画布时,可以看到像素化


有没有一种方法可以在画布上缩放而不必对文本进行像素化?

在画布上填充文本时,它不再是字母,而是开始成为字母形状的像素集合。当你放大它时,像素会变大。画布就是这样工作的

如果希望文本缩放为基于矢量的字体而不是像素,请不要在画布上绘制它们。您可以创建
HTML元素,并使用CSS定位将它们放置在画布的顶部。这样,当您放大时,渲染引擎将以更高的分辨率渲染字体,字体将保持清晰。但是你在画布上画的任何东西都会相应地缩放

或者,您可以覆盖浏览器的缩放功能并创建自己的缩放算法,但这需要一些工作

当用户放大或缩小窗口时,将触发
window.onresize
事件处理程序。您可以使用此触发器相应地调整画布css样式的宽度和高度(而不是画布的属性。这是内部渲染分辨率。更改样式的宽度和高度属性,即它在网站上缩放到的分辨率)

现在,您有效地禁止了用户web浏览器调整画布的大小,并且还提供了一个可以对缩放输入事件作出反应的位置。您可以使用此选项调整画布的
上下文。缩放
,以更改您绘制的所有内容的大小,包括字体

以下是一个例子:

<!DOCTYPE html>
<html>
<head>

    <script type="application/javascript">

        "use strict"

        var canvas;
        var context;

        function redraw() {
            // clears the canvas and draws a text label
            context.clearRect(0, 0, context.canvas.width, context.canvas.height);
            context.font = "60pt sans-serif";
            context.fillText("Hello World!", 100, 100);
        }

        function adjustSize() {
            var width = window.innerWidth;
            var height = window.innerHeight;

            // resize the canvas to fill the whole screen
            var style = canvas.style;
            style.width = width + "px";
            style.height = height + "px";

            // backup the old current scaling factor
            context.save();
            // change the scaling according to the new zoom factor
            context.scale(1000 / width, 1000 / height);
            // redraw the canvas
            redraw();
            // restore the original scaling (important because multiple calls to scale are relative to the current scale factor)
            context.restore();
        }

        window.onload = function() {
            canvas = document.getElementById("myCanvas");
            context = canvas.getContext("2d");
            adjustSize();
        }

        window.onresize = adjustSize;
    </script>
</head>

<body>

<canvas id ="myCanvas" width = 1000 height = 1000 ></canvas>

</body>

</html>

“严格使用”
var帆布;
var语境;
函数重画(){
//清除画布并绘制文本标签
clearRect(0,0,context.canvas.width,context.canvas.height);
context.font=“60pt无衬线”;
fillText(“helloworld!”,100100);
}
函数adjustSize(){
变量宽度=window.innerWidth;
var height=window.innerHeight;
//调整画布大小以填充整个屏幕
var style=canvas.style;
style.width=宽度+px;
style.height=高度+px;
//备份旧的当前比例因子
context.save();
//根据新的缩放因子更改缩放比例
上下文。比例(1000/宽,1000/高);
//重新绘制画布
重画();
//恢复原始比例(重要的是,多次调用比例是相对于当前比例因子的)
restore();
}
window.onload=函数(){
canvas=document.getElementById(“myCanvas”);
context=canvas.getContext(“2d”);
调整大小();
}
window.onresize=调整大小;

如果只需要缩放文本,只需缩放字体大小即可

不过,需要注意的是:字体或字体并不仅仅是直接按比例缩放的,这意味着你不会取得顺利的进展。这是因为字体通常针对特定大小进行优化,所以可以说,介于两者之间的大小是上一个和下一个大小的结果。这可以使字体在放大时看起来有点移动,这是正常的和预期的

这里的方法使用一个简单的规模。如果出于动画目的需要绝对平滑比例,则必须使用非常不同的技术

简单的方法是:

ctx.font = (fontSize * scale).toFixed(0) + 'px sans-serif';
一个

出于动画目的,您需要执行以下操作:

  • 将较大的尺寸渲染到屏幕外画布,然后使用该画布绘制不同的尺寸
  • 如果差异太大,并且插值出现问题,则必须以关键帧大小渲染多个缓存的文本图像,以便在缩放因子超过某个阈值时在它们之间切换
您可以看到,在较小的尺寸下,像素会变得有点“块状”,但在其他方面比纯文本方法平滑得多

这是因为浏览器在画布上使用双线性插值而不是双立方插值(这在将来可能会改变,也可能不会改变),因此当差异变大时,浏览器无法正确插值(有关此问题的解决方案,请参见下文)

相反的情况发生在大尺寸时,因为文本也由于插值而变得模糊

在这里,我们必须切换到更小(或更大)的缓存版本,然后在再次切换之前在一定范围内进行扩展

演示被简化为只显示一个缓存版本。你可以看到一半,这很好。原则将在完整的解决方案中(尺寸仅为示例):

在缩放期间更新切换图像)

然后,在将缓存版本绘制到屏幕之前,使用一个“最佳点”(您通过实验发现了一点)在缓存版本之间切换

var ctx = canvas.getContext('2d'),
    scale = 1,             /// initial scale
    initialFactor = 6,     /// fixed reduction scale of cached image
    sweetSpot = 1,         /// threshold to switch the cached images

    /// create two off-screen canvases
    ocanvas = document.createElement('canvas'),
    octx = ocanvas.getContext('2d'),
    ocanvas2 = document.createElement('canvas'),
    octx2 = ocanvas2.getContext('2d');

ocanvas.width = 800;
ocanvas.height = 150;
ocanvas2.width = 400;  /// 50% here, but maybe 75% in your case
ocanvas2.height = 75;  /// experiment to find ideal size..

/// draw a big version of text to first off-screen canvas
octx.textBaseline = 'top';
octx.font = '140px sans-serif';
octx.fillText('Cached text on canvas', 10, 10);

/// draw a reduced version of that to second (50%)
octx2.drawImage(ocanvas, 0, 0, 400, 75);
现在,我们只需检查“最佳点”值,以确定何时在这些版本之间切换:

function draw() {

    /// calc dimensions
    var w = ocanvas.width / initialFactor * scale,
        h = ocanvas.height / initialFactor * scale;

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    if (scale >= sweetSpot) {
        ctx.drawImage(ocanvas, 10, 10, w, h);   /// use cached image 1

    } else {
        ctx.drawImage(ocanvas2, 10, 10, w, h);  /// use cached image 2
    }
}
那么,为什么不直接用字体绘制第二个缓存图像呢?您可以这样做,但随后又回到了字体针对特定大小进行优化的问题,在缩放时会产生一个小跳跃。如果你能接受它,那么就使用它,因为它会提供更好的质量(特别是小尺寸)。如果需要平滑动画,则必须减少较大的缓存版本,以保持大小100%成比例

您可以了解如何在没有插值问题的情况下调整大图像的大小


希望这有帮助。

您如何缩放画布?您是说web浏览器的内置缩放功能(Ctrl+mousewheel)?还是你自己设计了zoomi
function draw() {

    /// calc dimensions
    var w = ocanvas.width / initialFactor * scale,
        h = ocanvas.height / initialFactor * scale;

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    if (scale >= sweetSpot) {
        ctx.drawImage(ocanvas, 10, 10, w, h);   /// use cached image 1

    } else {
        ctx.drawImage(ocanvas2, 10, 10, w, h);  /// use cached image 2
    }
}