You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

带透明通道的纯白PNG局部重色及Canvas云精灵图渐变着色方法问询

Alpha遮罩渐变着色:给透明PNG云元素局部加色的实现

你说的这种操作叫做Alpha遮罩渐变着色(或者更准确的说是「基于Alpha通道的颜色叠加+遮罩」),核心是利用原图像的透明通道(Alpha)作为“蒙版”,让渐变颜色只显示在云的实体区域,同时保留原像素的透明度层级。

结合你已经能切片精灵图并绘制到Canvas的基础,这里提供两种高效的实现方案:

方案一:用Canvas合成模式快速实现(推荐)

这种方法利用Canvas的globalCompositeOperation属性,无需手动操作像素,代码简洁高效,适合大多数场景。

步骤:

  1. 准备画布与云图像:假设你已经拿到切片后的云图像cloudImage,以及Canvas上下文ctx,先确定云元素的尺寸和位置。
  2. 创建径向渐变:以云的左侧区域为中心,创建从黄色到白色的径向渐变(你可以根据需求调整渐变的位置、半径和颜色)。
  3. 先画渐变,再用云图做遮罩:通过合成模式destination-in,让渐变只保留云图不透明的区域,同时继承云图的透明度。
// 假设云元素在Canvas上的位置是(x=50, y=50),尺寸是(w=200, h=100)
const x = 50;
const y = 50;
const w = cloudImage.width;
const h = cloudImage.height;

// 1. 创建径向渐变:左侧中心为起点,向外过渡到白色
const gradient = ctx.createRadialGradient(
  x + w * 0.2,  // 渐变中心X:云左侧1/5处
  y + h * 0.5,  // 渐变中心Y:云垂直居中
  0,            // 渐变起始半径
  x + w * 0.2,
  y + h * 0.5,
  w * 0.8       // 渐变结束半径:覆盖云的大部分区域
);
gradient.addColorStop(0, '#ffd700');  // 中心黄色
gradient.addColorStop(1, '#ffffff');  // 边缘过渡到白色

// 2. 填充渐变到云元素所在区域
ctx.fillStyle = gradient;
ctx.fillRect(x, y, w, h);

// 3. 设置合成模式:只保留原有内容(渐变)与新绘制内容(云)的重叠区域,且继承云的透明度
ctx.globalCompositeOperation = 'destination-in';

// 4. 绘制云图像,此时渐变只会显示在云的实体区域
ctx.drawImage(cloudImage, x, y, w, h);

// 5. 恢复默认合成模式,避免影响后续绘制
ctx.globalCompositeOperation = 'source-over';

方案二:手动操作像素(灵活定制)

如果你需要更精细的颜色控制(比如原云不是纯白色的情况),可以直接操作图像像素数据,这种方式自由度更高。

步骤:

  1. 用离屏Canvas绘制云图和渐变:分别获取两者的像素数据。
  2. 遍历像素,将渐变颜色与云的Alpha通道结合:替换云的RGB颜色为渐变颜色,保留原Alpha值。
  3. 将修改后的像素放回画布
// 1. 创建离屏Canvas处理云图像
const offscreenCanvas = document.createElement('canvas');
const offscreenCtx = offscreenCanvas.getContext('2d');
offscreenCanvas.width = cloudImage.width;
offscreenCanvas.height = cloudImage.height;
offscreenCtx.drawImage(cloudImage, 0, 0);
const imageData = offscreenCtx.getImageData(0, 0, cloudImage.width, cloudImage.height);
const pixelData = imageData.data;

// 2. 创建渐变的像素数据
const gradientCanvas = document.createElement('canvas');
const gradientCtx = gradientCanvas.getContext('2d');
gradientCanvas.width = cloudImage.width;
gradientCanvas.height = cloudImage.height;
const gradient = gradientCtx.createRadialGradient(
  cloudImage.width * 0.2,
  cloudImage.height * 0.5,
  0,
  cloudImage.width * 0.2,
  cloudImage.height * 0.5,
  cloudImage.width * 0.8
);
gradient.addColorStop(0, '#ffd700');
gradient.addColorStop(1, '#ffffff');
gradientCtx.fillStyle = gradient;
gradientCtx.fillRect(0, 0, cloudImage.width, cloudImage.height);
const gradientPixelData = gradientCtx.getImageData(0, 0, cloudImage.width, cloudImage.height).data;

// 3. 遍历每个像素,替换颜色并保留原Alpha
for (let i = 0; i < pixelData.length; i += 4) {
  const alpha = pixelData[i + 3];
  if (alpha === 0) continue; // 透明像素跳过处理

  // 替换RGB为渐变颜色,保留原Alpha
  pixelData[i] = gradientPixelData[i];     // R通道
  pixelData[i + 1] = gradientPixelData[i + 1]; // G通道
  pixelData[i + 2] = gradientPixelData[i + 2]; // B通道
  pixelData[i + 3] = alpha;               // 保留原Alpha
}

// 4. 将修改后的图像放回离屏Canvas,再绘制到主画布
offscreenCtx.putImageData(imageData, 0, 0);
ctx.drawImage(offscreenCanvas, 50, 50); // 绘制到主画布的目标位置

两种方案都能实现你要的效果:黄色径向渐变只作用在云的白色像素上,且原云的半透明区域会呈现半透明的黄色渐变。

内容的提问来源于stack exchange,提问作者Sixophrenia

火山引擎 最新活动