放大HTML5 <canvas>并且没有文本像素化?</canvas>

时间:2014-01-14 20:20:31

标签: javascript html5 canvas html5-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);

在文本上缩放画布时,可以看到像素化

有没有办法在画布上放大而不会在文字上出现像素化?

2 个答案:

答案 0 :(得分:1)

当您在画布上fillText时,它将停止为字母并开始成为字母形状的像素集合。当您放大它时,像素会变大。这就是画布的工作方式。

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

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

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

现在,您已经有效地禁用了用户Web浏览器调整画布的大小,并且还有一个可以对缩放输入事件做出反应的位置。您可以使用它来调整画布的context.scale以更改您绘制的所有内容的大小,包括字体。

以下是一个例子:

<!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>

答案 1 :(得分:1)

如果您只需缩放文字,只需缩放字体即可。

然而,关于这一点的几点注意事项:字体或字体不仅仅是直接扩展意味着你将无法顺利进展。这是因为字体通常针对特定尺寸进行了优化,因此两者之间的尺寸可以说是前一尺寸和下一尺寸的结果。这可以使字体看起来像是在放大时移动一点并且是正常的和预期的。

此处的方法使用简单的大小比例。如果你需要一个绝对平滑的比例用于动画目的,你将不得不使用一种非常不同的技术。

简单的方法是:

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

online demo here

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

  • 将更大的尺寸渲染到离屏画布,然后用于绘制不同尺寸
  • 当差异太大而您遇到插值问题时,您必须以密钥大小渲染其中几个缓存的文本图像,以便在缩放系数超过某个阈值时可以在它们之间切换。

In this demo 你可以看到,在较小的尺寸下,像素会变得有点“块状”,但除此之外比纯文本方法更平滑。

这是因为浏览器使用双线性插值而不是双立方与画布(将来可能会或可能不会改变),因此当差异变大时,它无法正确插值(请参阅下面的解决方案)这个问题)。

相反的情况发生在大尺寸,因为插值会使文字变得模糊。

这是我们必须切换到更小(或更大)的缓存版本的地方,然后我们会在再次切换之前在一定范围内进行缩放。

该演示简化为仅显示单个缓存版本。你可以看到中途工作正常。原则将是一个完整的解决方案(尺寸只是示例):

(缩放时,更新Here is a demo 切换后的图片。)

-- Cached image (100px)
   -- Draw cached image above scaled based on zoom between 51-100 pixels

-- Cached image (50px) generated from 100px version / 2
   -- Draw cached image above scaled based on zoom between 26-50 pixels

-- Cached image (25px) generated from 50px version / 2
   -- Draw cached image above scaled based on zoom between 1-25 pixels

然后使用“甜蜜点”(通过实验稍微找到)在缓存版本之间切换,然后再将它们绘制到屏幕上。

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%的比例。

您可以看到this answer如何在没有插值问题的情况下调整大图像的大小。

希望这有帮助。