Ваша основная проблема (пикселизация текста) связана с тем, что вы не очищаете холст между каждым кадром, а снова и снова рисуете в одной и той же позиции.Полупрозрачные пиксели, создаваемые сглаживанием, смешивают до все большего и большего количества непрозрачных пикселей.
Но в вашей ситуации кажется, что вы действительно хотите, чтобы хотя бы тень смешивалась следующим образом.
Для этого одним из способов будет нарисовать только один раз ваш обычный текст и иметь возможность рисовать только тень, позади текущего рисунка.
Рисование только тени фигуры.
Один прием, позволяющий рисовать только тени вашей фигуры, - это нарисовать вашу фигуру из видимого viewPort, с shadowOffsets, установленным в обратное значение этой позиции.
var text = 'foo bar';
var ctx = canvas.getContext('2d');
var original_x = 20; // the position it would have been
ctx.font = '30px sans-serif';
var targetPosition = ctx.measureText(text).width + original_x + 2;
// default shadow settings
ctx.shadowColor = 'red';
ctx.shadowBlur = 3;
// just to show what happens
var x = 0;
anim();
function anim() {
if(++x >= targetPosition) {
x=0;
return;
}
// if we weren't to show the anim, we would use 'targetPosition'
// instead of 'x'
ctx.shadowOffsetX = x;
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.fillText(text, -x + original_x, 30);
requestAnimationFrame(anim);
}
// restart the anim on click
onclick = function() {
if(x===0)anim();
};
<canvas id="canvas"></canvas>
Как только мы получим эту ясную тень без нарисованной на ней фигуры, мы можем перерисовать ее, как пожелаем.
Рисование за текущими пикселями
Параметр * destination-over *1028* делает именно это.
Таким образом, если мы соберем их вместе, мы сможем нарисовать за обычным текстом и нарисовать только нашу тень за ним в каждом кадре, избегая путаницы сглаживания.
(Обратите внимание, чтомы также можем сохранить чистую тень на неэкранном холсте для исполнения, поскольку тень - очень медленная операция.)
var text = 'foo bar';
var ctx = canvas.getContext('2d');
ctx.font = '48px sans-serif';
var x = 20;
var y = 40;
var shadow = generateTextShadow(ctx, text, x, y, 'red', 5);
ctx.globalAlpha = 0.5;
ctx.fillText(text, x, y);
// from now on we'll draw behind current content
ctx.globalCompositeOperation = 'destination-over';
var shadow_pos = 0;
anim();
// in the anim, we just draw the shadow at a different offset every frame
function anim() {
if(shadow_pos++ > 65) return;
ctx.drawImage(shadow, shadow_pos, shadow_pos);
requestAnimationFrame(anim);
}
// returns a canvas where only the shadow of the text provided is drawn
function generateTextShadow(original_ctx, text, x, y, color, blur, offsetX, offsetY) {
var canvas = original_ctx.canvas.cloneNode();
var ctx = canvas.getContext('2d');
ctx.font = original_ctx.font;
var targetPosition = ctx.measureText(text).width + 2;
// default shadow settings
ctx.shadowColor = color || 'black';
ctx.shadowBlur = blur || 0;
ctx.shadowOffsetX = targetPosition + x +(offsetX ||0);
ctx.shadowOffsetY = (offsetY || 0);
ctx.fillText(text, -targetPosition, y);
return canvas;
}
<canvas id="canvas"></canvas>