画笔跟随效果

114 阅读2分钟

效果说明

本案例实现了一个平滑的画笔跟随效果。鼠标或触摸移动时会生成一条彩色的轨迹,并添加了渐变色和渐隐效果。

Snipaste_2024-12-23_23-23-30.png

核心实现

1. 画笔状态管理

使用一个配置对象统一管理画笔的所有状态:

const brush = {
    points: [],           // 存储轨迹点数组
    maxPoints: 50,        // 轨迹最大点数(影响轨迹长度)
    minDistance: 10,      // 相邻点最小距离(影响轨迹平滑度)
    lastX: 0,            // 上一个点的X坐标
    lastY: 0,            // 上一个点的Y坐标
    isDrawing: false,     // 是否正在绘制
    hue: 0               // HSL色相值(0-360)
};

2. 轨迹点控制

通过计算点间距离来控制轨迹的平滑度:

function addPoint(x, y) {
    // 计算与上一个点的距离
    const dx = x - brush.lastX;
    const dy = y - brush.lastY;
    const distance = Math.sqrt(dx * dx + dy * dy);
    
    // 只有距离大于最小距离时才添加新点
    if (distance >= brush.minDistance) {
        brush.points.push({ x, y });
        brush.lastX = x;
        brush.lastY = y;
        
        // 限制轨迹点数,移除最老的点
        if (brush.points.length > brush.maxPoints) {
            brush.points.shift();
        }
    }
}

3. 平滑轨迹绘制

使用线性插值连接点,实现平滑效果:

function drawTrail() {
    if (brush.points.length < 2) return;
    
    ctx.beginPath();
    ctx.moveTo(brush.points[0].x, brush.points[0].y);
    
    // 连接所有点
    for (let i = 1; i < brush.points.length - 1; i++) {
        ctx.lineTo(brush.points[i].x, brush.points[i].y);
    }
    
    // 设置渐变色和线条样式
    brush.hue = (brush.hue + 1) % 360;           // 循环改变色相
    ctx.strokeStyle = `hsl(${brush.hue}, 100%, 50%)`;
    ctx.lineWidth = 2;
    ctx.lineCap = 'round';
    ctx.lineJoin = 'round';
    ctx.stroke();
}

4. 事件处理

统一处理鼠标和触摸事件:

function mousedown(e) {
    brush.isDrawing = true;
    brush.points = [];           // 清空之前的点
    brush.lastX = e.clientX;     // 记录起始位置
    brush.lastY = e.clientY;
}

// 添加事件监听
canvas.addEventListener('mousedown', mousedown);
canvas.addEventListener('mousemove', mousemove);
canvas.addEventListener('mouseup', mouseup);

参数调优

  1. 轨迹长度(maxPoints)
    • 值越大: 轨迹越长,但内存占用更大
    • 值越小: 轨迹更短,性能更好
    • 50是一个比较平衡的值
  2. 点间距(minDistance)
    • 值越大: 轨迹更粗糙,但性能更好
    • 值越小: 轨迹更平滑,但点更密集
    • 10像素是一个比较合适的值
  3. 渐隐速度(透明度)
    • 值越大: 消失更快
    • 值越小: 停留更久
    • 0.05是一个比较理想的值

扩展思路

  1. 添加速度感知
  2. 添加压感支持

Demo