HTML5画布缩放后字体大小小于1的问题

3
我正在使用 HTML5 画布 2D 上下文构建一个正方形网格。我进行平移和缩放,以便网格方块的大小为一个单位,但在未变换时大小为30px。这个10x10的网格的例子在这里
这样做看起来很好,但有一个问题:文本。
为了在正方形内指定适合的字体,我需要指定小于1的字体大小。
例如,“.5px sans-serif”应该是正方形大小的一半(即30px未变换正方形的“15px sans-serif”)。但这会失败,至少在 Chrome 中会如此,画布会恢复到较大的默认值。
我唯一找到的解决方法是在绘制文本时撤消画布缩放,真是丑陋!
是否有一种通过亚像素字体规范解决这个问题的方法? (自然地,在变换后,字体将成为合理的大字体。亚像素字体大小仅与画布变换相关)
3个回答

2

在认为这将很难之后,我意识到使用变换有一个简单的解决方案。

考虑到当前上下文以复杂方式进行了转换,并且我想在给定位置(tx,ty)绘制文本。你唯一需要知道的是当前比例(scaleX,scaleY)(无论如何你都需要知道它来计算分数字体大小)。然后,您可以在转换后的坐标系中进行平移,然后取消缩放并绘制文本:

ctx.save();
ctx.translate(tx, ty);
ctx.scale(1/scaleX, 1/scaleY);
ctx.fillText("Some String", 0, 0);
ctx.restore();

如果您需要进行与文本大小相关的任何数学计算,可以在“未缩放”坐标中完成。

2

将字体大小设置为0.5px意味着字体是半像素不变形。它不是一个比例值,而是一个绝对值。

因此,需要将字体大小指定为未缩放的大小,缩放上下文,绘制文本和网格,然后可以选择性地删除缩放。

这个演示将展示它的实际效果:
缩放演示

关键部分:

ctx.font = '15px sans-serif';
ctx.scale(1/30, 1/30);
ctx.fillText('TEXT', 5, 5);
ctx.strokeRect(0, 0, 30, 30);

示例演示

这个示例将在左上角产生一个微小的点。


你可能更喜欢在下面使用 save() 和 ctx.setTransform(1, 0, 0, 1, 0, 0),而不是使用缩放等效的方法。 - backspaces
@backspaces 保存/恢复在性能方面相当昂贵(您需要两者,因为它们使用推送/弹出堆栈方法并保存/恢复整个上下文的设置)。setTransform是(reset)缩放的良好替代品,但scale只是一个包装器,用于设置转换矩阵值,就像setTransform一样,但您需要setTransform来完全重置缩放,因为scale()会累加。希望这个答案有所帮助! - user1693593
啊,我错过了那个..避免保存/恢复对肯定是一个好主意。 - backspaces
这个答案暗地里做了另外一件有用的事情:它通过偏移文本5,5,使得文本显示在画布上的位置为(5,5):ctx.fillText('TEXT', 5, 5); 这个偏移很重要,因为 fillText 相对于变换使用了一个奇怪的基线,如果不偏移,则文本将被渲染到方块之上太高而看不见。你可以通过改变基线来将5,5偏移改为0,0: ctx.textBaseline = "top"; - DoomGoober

-2

说实话,子像素字体大小相当无意义。你可能更想要完全删除文本,或者用小直线“希腊化”文本行。使用measureText()在某个已知的标准大小上获取每行适当的相对长度,然后只需绘制一条直线来指示(不可读)文本的存在。


字体随画布变换而缩放,因此如果ctx.scale为20,则“.5px serif”字体将呈现为10px字体。这就是我的问题所在...我想在画布中指定一个微小的字体,以便在缩放后变成一个合理的字体。 - backspaces
我不确定为什么,但自己尝试过零次。也许是因为字距应用在未缩放的大小上。相反,我最终将比例设置为1.0的文本放置在可以缩放的地图上,并在JS中确定缩放后的平移值。 - bjorke
2
我遇到了同样的问题。我的数据已经归一化(0-1),并且还有任意的转换,我使用上下文转换来应用它们。似乎我只是无法绘制文本,因为小于0.5的任何东西都会消失,而0.5很大(因为它被缩放了)。这只是Canvas规范中的一个错误或其他什么问题吗?我很想看到解决方案。此外,我的转换过于复杂,难以在JavaScript中轻松重现。 - Yona Appletree
我放弃了使用其他方法,而是使用恒等矩阵进行缩放,然后恢复之前的比例。即:ctx.save();ctx.setTransform(1, 0, 0, 1, 0, 0);<绘制文本>; ctx.restore() - backspaces

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接