You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JS Canvas如何实现类似Photoshop的不同硬度画笔?

JS Canvas如何实现类似Photoshop的不同硬度画笔?

嘿,我太懂你这种困扰了——想用Canvas做个像PS那样能调硬度的画笔,结果用createRadialGradient做出来的效果总觉得不对味儿,对吧?其实问题出在径向渐变默认的线性过渡和PS画笔的边缘算法不一样,我来给你分享两个靠谱的实现方法~

方法一:改进径向渐变,模拟硬度过渡

PS里的画笔硬度,本质就是控制“实色区域”和“透明过渡区域”的比例:100%硬度时,画笔边缘完全锐利;0%时,从中心到边缘完全平滑过渡。我们可以通过自定义渐变的颜色停止点,来模拟这个逻辑:

function createBrush(size, hardness) {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  canvas.width = canvas.height = size;

  // 把0-100的硬度转成0-1的系数
  const hardnessFactor = hardness / 100;
  const radius = size / 2;
  // 计算实色区域的半径:硬度越高,实色区域越大
  const solidRadius = radius * hardnessFactor;
  // 过渡带的宽度:剩下的边缘区域
  const featherWidth = radius - solidRadius;

  // 创建从实色区域边缘到画笔外边缘的径向渐变
  const gradient = ctx.createRadialGradient(
    radius, radius, solidRadius,
    radius, radius, radius
  );
  gradient.addColorStop(0, 'rgba(0,0,0,1)');
  
  // 如果有过渡带,添加过渡的颜色停止点
  if (featherWidth > 0) {
    // 实色区域的边界,透明度保持1
    gradient.addColorStop(hardnessFactor, 'rgba(0,0,0,1)');
    // 过渡到完全透明
    gradient.addColorStop(1, 'rgba(0,0,0,0)');
  } else {
    // 100%硬度,直接从实色切到透明
    gradient.addColorStop(1, 'rgba(0,0,0,0)');
  }

  // 用渐变填充整个画笔画布
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, size, size);

  return canvas;
}

这个方法的好处是性能好、计算快,适合大部分场景。你要是想改画笔颜色,只要把rgba里的前三个数值换成你要的颜色就行。

方法二:像素级高斯计算,还原PS原生柔边效果

如果你追求和PS柔边画笔完全一致的自然过渡,那得用高斯函数来计算每个像素的透明度——毕竟PS的柔边画笔本质就是高斯模糊的边缘。这种方法会直接操作像素数据,效果更精准:

function createBrushWithGaussian(size, hardness) {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  canvas.width = canvas.height = size;
  const radius = size / 2;
  const hardnessFactor = hardness / 100;
  // 高斯函数的标准差,硬度越高,标准差越小(过渡越窄)
  const sigma = radius * (1 - hardnessFactor) / 3;

  const imageData = ctx.createImageData(size, size);
  const data = imageData.data;

  // 遍历每个像素计算透明度
  for (let y = 0; y < size; y++) {
    for (let x = 0; x < size; x++) {
      // 计算当前像素到画笔中心的距离
      const dx = x - radius;
      const dy = y - radius;
      const distance = Math.sqrt(dx*dx + dy*dy);

      // 超出画笔半径的像素直接透明
      if (distance > radius) {
        data[(y*size + x)*4 + 3] = 0;
        continue;
      }

      // 计算高斯衰减后的透明度
      let alpha = 1;
      if (sigma > 0) {
        // 把距离归一化到过渡带的范围
        const normalizedDistance = (distance - radius * hardnessFactor) / (radius * (1 - hardnessFactor));
        if (normalizedDistance > 0) {
          // 高斯函数核心计算
          alpha = Math.exp(-Math.pow(normalizedDistance * 3, 2) / 2);
        }
      }

      // 设置像素颜色(这里是黑色,可自行修改)
      data[(y*size + x)*4] = 0;
      data[(y*size + x)*4 + 1] = 0;
      data[(y*size + x)*4 + 2] = 0;
      data[(y*size + x)*4 + 3] = Math.round(alpha * 255);
    }
  }

  ctx.putImageData(imageData, 0, 0);
  return canvas;
}

最后,怎么用这个画笔?

创建好画笔画布后,你只需要在主画布上监听鼠标事件,用drawImage把画笔绘制到对应位置就行,还能配合Canvas的合成模式模拟PS的画笔混合效果:

const mainCanvas = document.getElementById('mainCanvas');
const mainCtx = mainCanvas.getContext('2d');
// 创建一个50px大小、50%硬度的画笔
const brush = createBrush(50, 50);

let isDrawing = false;

// 监听鼠标事件实现绘制
mainCanvas.addEventListener('mousedown', (e) => {
  isDrawing = true;
  const rect = mainCanvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  drawBrush(x, y);
});

mainCanvas.addEventListener('mousemove', (e) => {
  if (isDrawing) {
    const rect = mainCanvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    drawBrush(x, y);
  }
});

mainCanvas.addEventListener('mouseup', () => {
  isDrawing = false;
});

// 绘制画笔的函数
function drawBrush(x, y) {
  // 可以换成multiply、screen等合成模式,模拟不同画笔效果
  mainCtx.globalCompositeOperation = 'source-over';
  // 把画笔绘制到鼠标中心位置
  mainCtx.drawImage(brush, x - brush.width/2, y - brush.height/2);
}

小技巧补充

  • 要是想模拟压感,可以在鼠标拖拽时根据压力(或者鼠标移动速度)动态调整画笔的size或hardness;
  • 改变画笔颜色时,只要在创建画笔的代码里修改rgba的前三个通道值就行;
  • 简单的柔边也可以用shadowBlur快速实现,但精准控制硬度的话还是上面的方法更可靠。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 13:29:31