当我为图像或 smth 设置动画时如何避免背景颜色不透明问题?

How to avoid background color opactity issue when I animate an image or smth else?

此屏幕截图和代码片段将向您展示我的麻烦。如何避免这种情况?可能是我以错误的方式这样做吗?如果可能的话,我想看一些代码示例。

let canvas = document.querySelector("canvas");
 let ctx = canvas.getContext("2d");

 canvas.width = 500;
 canvas.height = 500;

 let image = new Image();

 image.src= "https://www.html5rocks.com/static/images/identity/HTML5_Badge_256.png";

 function rand(min, max)
 {
  return Math.floor(Math.random() * (max - min + 1)) + min;
 }

 image.addEventListener("load", function()
 {

  let top = canvas.width / 2 - image.width / 2;
  let left = canvas.height / 2 - image.height / 2;

  function render()
  {

   top += rand(-2, 2);
   left += rand(-2, 2);


   ctx.save();
   ctx.globalAlpha = 0.05;
   ctx.fillStyle = "#9ea7b8";
   ctx.fillRect(0, 0, canvas.width, canvas.height);
   ctx.restore();
   ctx.drawImage(image, top, left, image.width, image.height);
   requestAnimationFrame(render);
  }

  render();
 });
<canvas></canvas>

问题

我能想到的描述此工件的最佳方式是将其称为舍入误差。人们往往会忘记的一件事是,当您绘制到 canvas 时,您实际上是在写入位图。实际上是一个图像。每个像素的 alpha 值是一个整数 x,其中 0 <= x <= 255.

所以,当你说:

ctx.save();
ctx.globalAlpha = 0.05;
ctx.fillStyle = "#9ea7b8";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
ctx.drawImage(image, top, left, image.width, image.height);

您正在绘制一个几乎透明的正方形,它不会使任何东西完全消失。例如,如果一个像素的颜色为 rgba(254,255,255,255),并且您在其上绘制一个不透明度为 0.05 的白色方块 rgba(255,255,255,12),它将把红色从 254 拉到 254.05.不过,这需要是一个整数,所以它会向下舍入,并且永远不会完全变成它接近的颜色。

现在如何解决

  1. 我认为解决这个问题的一种方法是基本上制作一条蛇。您可以使用一个圆形数组来存储每个逐渐透明的图像的位置,然后在清除屏幕后绘制它们。但这有一个问题,它会占用更多空间,并且需要更长的时间来绘制。
  2. 你可以浏览图像数据,如果一个像素足够接近,你就可以把它变成你想要的。我不会为你提供完成的代码,但你可以使用 ctx.getImageData().

像素比较应该类似于

if(abs(actualRed-targetRed) < tolerance && abs(actualGreen-targetGreen) < tolerance....){
    actualRed=targetRed; 
    actualGreen=targetGreen...
}
  1. This page has a very interesting approach. Basically, it manipulates the alpha values of the picture directly, instead of drawing a transparent picture over it. You want a background color, apparently. This starts to complicate things, but not too badly. I would suggest separating the dynamic images from the static images. You can have two canvases that are stacked on top of one another (this is best practice, actually). Or you can just set the background of the canvas.

希望这对您有所帮助。

好的,经过多次尝试,工作完成了。每一帧我都绘制完整路径。

let canvas = document.querySelector("canvas");
 let ctx = canvas.getContext("2d");
 let tail = 20;

 canvas.width = 800;
 canvas.height = 600;

 let image = new Image();

 let opacity = [];

 image.src= "https://www.html5rocks.com/static/images/identity/HTML5_Badge_256.png";

 for(let i = 1; i <= tail; i++)
 {
  opacity.push(i / tail);
 }


 function rand(min, max)
 {
  return Math.floor(Math.random() * (max - min + 1)) + min;
 }

 image.addEventListener("load", function()
 {

  let frames = [];

  frames.push({
   x: canvas.width / 2 - image.width / 2,
   y: canvas.height / 2 - image.height / 2
  });

  for (let i = 1; i < tail; i++)
  {
   frames.push({
    x: frames[i - 1].x + rand(-2, 2),
    y: frames[i - 1].y + rand(-2, 2)
   });
  }

  function render()
  {

   frames.shift();
   frames.push({
    x: frames[frames.length - 1].x + rand(-2, 2),
    y: frames[frames.length - 1].y + rand(-2, 2)
   });

   ctx.globalAlpha = 1;
   ctx.fillStyle = "#9ea7b8";
   ctx.fillRect(0, 0, canvas.width, canvas.height);

   for(let i = 0; i < tail; i++)
   {
    ctx.save();
    ctx.drawImage(image, frames[i].x, frames[i].y, image.width, image.height);
    ctx.globalAlpha = opacity[tail - 1 - i];
    ctx.fillRect(frames[i].x - tail, frames[i].y - tail, image.width + tail + tail, image.height + tail + tail);
    ctx.restore();
   }

   requestAnimationFrame(render);
  }

  render();
 });
<canvas></canvas>