我希望有一个平滑的动画,简单的针脚上有文字 . 这个引脚在画布周围缓慢移动,我需要流畅的动画 . 所以我的图钉由背景(填充和阴影圆圈)和顶部的文字组成 .
我为圆圈本身实现了非常平滑的运动,但 not for the text!
问:是否有可能在HTML5画布中实现流畅的文本移动以及如何实现?
我尝试了什么:
Method 0 :只绘制圆圈,上面没有文字 . 动画很流畅 .
Problem :没问题,除了没有文字 .
Method 1 :使用canvas方法fillText()在圆圈顶部绘制文本 .
Problem :垂直移动时文本抖动 . 水平移动不会产生抖动 .
Method 2 :在屏幕外画布上绘制文本,将画布复制为圆形顶部的图像 . 创建屏幕外画布并在其上绘制大小比原始大两倍的文本,然后在复制到屏幕画布时缩小 . 这将锐化文本 .
Problem :文字很清晰,但是波浪状,并且在移动过程中会出现一些闪烁 .
Method3 :将文本绘制到屏幕外的画布上,将画布复制为圆形顶部的图像 . 创建屏幕外画布并在那里绘制文本 . 画布和文本的大小与屏幕上的大小相同 .
Problem :运动足够平滑,但文字模糊,失焦 .
我的JSFIDDLE:Canvas text jitter animation
我的Javascript代码:
var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "bold 16px Helvetica";
ctx.shadowOffsetX = ctx.shadowOffsetY = 2;
ctx.shadowBlur = 6;
var bgColor="blue";
var textColor="white";
var shadowColor="rgba(0, 0, 0, 0.4)";
var radius=15;
//Draw empty plate for the pin, no text on top
//Problem: NONE, movements are smooth
function drawPinPlate(x, y)
{
var oldShadow = ctx.shadowColor;
ctx.shadowColor = shadowColor;
ctx.fillStyle = bgColor;
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2*Math.PI);
ctx.fill();
ctx.shadowColor = oldShadow;
}
//method 1: Draw pin with text directly.
//Draw text using canvas direct text rendering.
//Problem: Text vertical jittering while animating movement
function drawPin1(x, y, name)
{
drawPinPlate(x, y);
ctx.fillStyle = textColor;
ctx.fillText(name, x, y);
}
//method 2: Draw pin with text using offscreen image with resize
//Draw text using text pre-rendered to offscreen canvas.
//Offscreen canvas is twice large than the original and we do resize (shrink) to the original one
//Problem: Text is sharp but some flickering appears during image movement
function drawPin2(x, y, name)
{
drawPinPlate(x, y);
ctx.drawImage(offImage1, x - radius, y - radius, radius*2, radius*2);
}
//method 2: Draw pin with text using offscreen image
//Draw text using text pre-rendered to offscreen canvas.
//Offscreen canvas is the same size as the original.
//Problem: Text is looking fuzzy, blurry
function drawPin3(x, y, name)
{
drawPinPlate(x, y);
ctx.drawImage(offImage2, x - radius, y - radius);
}
var PIXEL_RATIO = (function ()
{
var ctx = document.createElement("canvas").getContext("2d"),
dpr = window.devicePixelRatio || 1,
bsr = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1;
return dpr / bsr;
})();
//create offscreen canvas
createHiDPICanvas = function(w, h, ratio)
{
if (!ratio) { ratio = PIXEL_RATIO; }
var can = document.createElement("canvas");
can.width = w * ratio;
can.height = h * ratio;
can.style.width = w + "px";
can.style.height = h + "px";
can.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0);
return can;
}
//create offscreen canvas with text, size of the canvas is twice larger than the original.
function createPin1(name)
{
var cnv = createHiDPICanvas(radius*2, radius*2, 2);
var ctx = cnv.getContext("2d");
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "bold 16px Helvetica";
ctx.fillStyle = textColor;
ctx.fillText(name, radius, radius);
return cnv;
}
//create offscreen canvas with text. Text becomes very blurry.
function createPin2(name)
{
var cnv = createHiDPICanvas(radius*2, radius*2, 1);
var ctx = cnv.getContext("2d");
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "bold 16px Helvetica";
ctx.fillStyle = textColor;
ctx.fillText(name, radius, radius);
return cnv;
}
var offImage1, offImage2;
offImage1 = createPin1("AB");
offImage2 = createPin2("AB");
var startTime;
var speed = 180;
//render one frame
function render(deltaTime)
{
var x = (deltaTime / speed / 2) %100;
var y = (deltaTime / speed) % 200;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(var i = 0; i<4; i++)
{
ctx.fillText(i, 20 + x + i * 50, 15 + y);
}
drawPinPlate(20 + x, 40 + y);
drawPin1(70 + x, 40 + y, "AB");
drawPin2(120 + x, 40 + y, "AB");
drawPin3(170 + x, 40 + y, "AB");
}
//Animation loop
function animate()
{
requestAnimationFrame(animate);
render(Date.now() - startTime);
}
//alert("You screen pixel ratio = " + PIXEL_RATIO);
startTime = Date.now();
animate();
2 回答
您可以使用第三种方法a.k.a屏幕外画布来实现文本的平滑移动 .
但是,实际上不可能在画布上实现平滑的文本渲染 .
这是因为文本通常使用智能平滑算法进行渲染,并且画布无法访问此类子像素级别,因此它使模糊文本成为最佳 .
当然,您可以尝试自己实现字体光栅化算法,SO用户Blindman67提供a good explanation along with a trying here,但它依赖于在移动目标上制作它的那么多参数(设备,UA,字体等)几乎是否定的-走 .
因此,如果您需要在动画内容上进行完美的文字渲染, SVG is your friend . 但即使在SVG中,文本动画看起来也有点不稳定 .
否则,对于画布,您可以获得的最佳效果是将画布的大小加倍,然后使用屏幕后画布方法使用CSS向后缩小它 .
这是因为:
当您将要创建的画布的大小增加到
createHiDPICanvas(2000,2000, ...)
时,移动会平滑 . 现在你're creating a very small resolution canvas (30px by 30px), and the text looks jittery because it'在非常小的像素范围内移动 .示例:https://jsfiddle.net/6xr4njLm/
createHiDPICanvas的更长解释:How do I fix blurry text in my HTML5 canvas?