如何采用具有一定透明度的全白色png,并将其部分重新着色?

时间:2018-02-18 14:10:33

标签: canvas html5-canvas

我有这张png sprite云片。我完全有能力切割spritesheet,并将单个云放入画布上下文中。但是,如果我想修改该云的图像呢? 如果我想在左边用黄色着色怎么办?具有某种径向梯度。那些东西应该是可能的,对吧?径向渐变仅影响白云像素,但仅影响透明度。 这个叫什么?我需要做什么?

1 个答案:

答案 0 :(得分:1)

这称为compositing,是的,Canvas2D API非常可行,它提供了globalCompositeOperation属性,有几种不同的模式。

这是一个使用fully white + alpha sprite的简单ES6示例,我们将在其上绘制一个radialGradient。

(async() => {
  const ctx = c.getContext('2d');
  const clouds = getCloudsObjects(30);
  const sheet = await loadImage('https://i.stack.imgur.com/Gvewl.png');
  ctx.filter = 'blur(2px)';

  const cloud_grad = initGrad('yellow', 'red');
  const sky_grad = initGrad('#300c46', 'green');
  anim();

  function anim() {
    ctx.clearRect(0, 0, c.width, c.height);
    clouds.forEach(updateAndDrawSprite);
    // draw only on non transparent pixels, keeping the alpha info
    ctx.globalCompositeOperation = 'source-atop';
    drawGrad(cloud_grad);

    if (bg.checked) {
      // draw behind everything ?
      ctx.globalCompositeOperation = 'destination-over';
      drawGrad(sky_grad);
    }

    // reset gCO
    ctx.globalCompositeOperation = 'source-over';
    requestAnimationFrame(anim);
  }

  function initGrad(col1, col2) {
    const grad = ctx.createRadialGradient(0, c.height, 0, 0, c.height, c.width * 1.6);
    grad.addColorStop(0, col1);
    grad.addColorStop(0.7, col2);
    return grad;
  }

  function drawGrad(grad) {
    ctx.fillStyle = grad;
    ctx.fillRect(0, 0, c.width, c.height);
  }

  function updateAndDrawSprite(obj) {
    obj.update();
    ctx.drawImage(sheet, obj.ox, obj.oy, obj.ow, obj.oh, obj.x, obj.y, obj.w, obj.h);
  }

  function getCloudsObjects(nbOfClouds) {
    const arr = [];
    const W = 64,
      h = 16,
      w = 16,
      max = w / W;
    for (let i = 0; i < nbOfClouds; i++) {
      let s = Math.random() * (c.height / h);
      arr[i] = {
        ox: Math.floor(Math.random() * 4) * w,
        oy: 0,
        oh: h,
        ow: w,
        x: Math.random() * c.width,
        y: Math.random() * c.height,
        w: s * w,
        h: s * h,
        speed: Math.random() - .5,
        update: function() {
          this.x += this.speed;
          this.y += this.speed / 2;
          if (this.x > c.width)
            this.x = -this.w;
          if (this.y > c.height)
            this.y = -this.h;
          if (this.x < -this.w)
            this.x = c.width;
          if (this.y < -this.h)
            this.y = c.height;
        }
      };
    }
    return arr;
  }

  function loadImage(url) {
    return new Promise((res, rej) => {
      const img = new Image();
      img.onload = e => res(img);
      img.onerror = e => rej(img);
      img.src = url;
    });
  }

})();
label {
  display: block
}
<canvas id="c" width="500"></canvas>
<label>draw background <input type="checkbox" id="bg"></label>