吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1657|回复: 29
收起左侧

[其他原创] 模拟手写效果的笔触

  [复制链接]
wzs0777 发表于 2025-12-13 20:06
本帖最后由 wzs0777 于 2025-12-13 20:35 编辑

ScreenShot_2025-12-13_203432_550.png
[JavaScript] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>道林纸质感手写板</title>
<style>
  body { 
    margin: 0; 
    padding: 20px; 
    background: #e0e0e0; 
    font-family: sans-serif; 
  }
  #canvas {
    /* 道林纸米黄色背景 */
    background: #f5f5dc; 
    /* 添加细微纹理(噪声图案)模拟纸张纤维 */
    background-image: url("data:image/svg+xml,%3Csvg width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='paper'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.04' result='noise'/%3E%3CfeDiffuseLighting in='noise' lighting-color='white' surfaceScale='1'%3E%3CfeDistantLight azimuth='45' elevation='60'/%3E%3C/feDiffuseLighting%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23paper)' opacity='0.15'/%3E%3C/svg%3E");
    border: 2px solid #333;
    cursor: crosshair;
    display: block;
    touch-action: none;
  }
  .controls {
    margin: 10px 0;
    display: flex;
    gap: 15px;
    align-items: center;
    flex-wrap: wrap;
  }
  select, input, button {
    padding: 8px 12px;
    font-size: 14px;
  }
  .debug-info {
    margin-top: 10px;
    font-size: 12px;
    color: #666;
    font-family: monospace;
  }
</style>
</head>
<body>
  <h3>道林纸质感手写笔迹模拟</h3>
  <div class="controls">
    <label>笔刷:
      <select id="penType">
        <option value="fountain">钢笔</option>
        <option value="brush">毛笔</option>
        <option value="ballpoint">圆珠笔</option>
      </select>
    </label>
    <label>颜色:
      <input type="color" id="colorPicker" value="#000000">
    </label>
    <button>清空画布</button>
  </div>
  <canvas id="canvas" width="800" height="600"></canvas>
  <div class="debug-info" id="debug">状态:准备就绪 | 提示:按住鼠标拖动绘制</div>

<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let isDrawing = false;
let points = [];
let lastPoint = null;
let strokeCount = 0;

// 获取笔刷配置
function getPenConfig(type) {
  const configs = {
    fountain: { minWidth: 2, maxWidth: 6, alpha: 0.95 },
    brush: { minWidth: 8, maxWidth: 25, alpha: 0.8 },
    ballpoint: { minWidth: 2, maxWidth: 4, alpha: 1 }
  };
  return configs[type] || configs.fountain;
}

// 基于速度的压力模拟
function getPressureByVelocity(current, last) {
  if (!last) return 0.8;
  const distance = Math.sqrt(
    Math.pow(current.x - last.x, 2) + Math.pow(current.y - last.y, 2)
  );
  // 速度越快,线条越细
  return Math.min(1, Math.max(0.3, 1 - distance / 30));
}

// 笔锋模拟
function simulatePenTip(points) {
  const tipLength = Math.min(10, points.length);
  for (let i = 0; i < points.length; i++) {
    const remaining = points.length - i;
    if (remaining < tipLength) {
      points[i].pressure *= (remaining / tipLength) * 0.5 + 0.5;
    }
  }
}

// 核心绘制函数 - 道林纸质感版
function drawPath() {
  if (points.length < 2) return;
  
  const color = document.getElementById('colorPicker').value;
  const penType = document.getElementById('penType').value;
  const config = getPenConfig(penType);
  
  // 应用笔锋效果
  simulatePenTip(points);
  
  ctx.save();
  ctx.strokeStyle = color;
  ctx.globalAlpha = config.alpha;
  ctx.lineCap = 'round';
  ctx.lineJoin = 'round';
  
  // 添加墨水扩散效果
  const alphaHex = Math.round(config.alpha * 85).toString(16).padStart(2, '0');
  ctx.shadowColor = color + alphaHex;
  ctx.shadowBlur = config.maxWidth * 0.3; // 根据笔刷宽度调整模糊
  
  // 分段绘制,每段使用对应的压力宽度
  for (let i = 1; i < points.length; i++) {
    const prev = points[i - 1];
    const current = points[i];
    
    // 计算线宽
    const width = config.minWidth + (current.pressure || 0.8) * (config.maxWidth - config.minWidth);
    ctx.lineWidth = width;
    
    // 绘制线段
    ctx.beginPath();
    ctx.moveTo(prev.x, prev.y);
    ctx.lineTo(current.x, current.y);
    ctx.stroke();
  }
  
  ctx.restore();
  
  // 更新调试信息
  const currentPoint = points[points.length - 1];
  debug.textContent = `状态:绘制中 | 线段数:${points.length} | 压力:${(currentPoint.pressure || 0.8).toFixed(2)} | 颜色:${color}`;
}

// 鼠标事件处理
canvas.addEventListener('mousedown', (e) => {
  isDrawing = true;
  const rect = canvas.getBoundingClientRect();
  const newPoint = {
    x: e.clientX - rect.left,
    y: e.clientY - rect.top,
    pressure: 0.8
  };
  points = [newPoint];
  lastPoint = newPoint;
  strokeCount++;
  debug.textContent = `状态:开始绘制 | 笔画:${strokeCount}`;
});

canvas.addEventListener('mousemove', (e) => {
  if (!isDrawing) return;
  
  const rect = canvas.getBoundingClientRect();
  const currentX = e.clientX - rect.left;
  const currentY = e.clientY - rect.top;
  
  const currentPoint = {
    x: currentX,
    y: currentY,
    pressure: getPressureByVelocity({x: currentX, y: currentY}, lastPoint)
  };
  
  points.push(currentPoint);
  drawPath();
  
  lastPoint = currentPoint;
});

canvas.addEventListener('mouseup', () => {
  isDrawing = false;
  points = [];
  debug.textContent = `状态:绘制完成 | 总笔画:${strokeCount}`;
});

canvas.addEventListener('mouseleave', () => {
  if (isDrawing) {
    isDrawing = false;
    points = [];
    debug.textContent = `状态:绘制中断 | 总笔画:${strokeCount}`;
  }
});

// 清空画布并重绘纸张纹理
function clearCanvas() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  // 重绘纸张纹理
  const svgTexture = "data:image/svg+xml,%3Csvg width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='paper'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.04' result='noise'/%3E%3CfeDiffuseLighting in='noise' lighting-color='white' surfaceScale='1'%3E%3CfeDistantLight azimuth='45' elevation='60'/%3E%3C/feDiffuseLighting%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23paper)' opacity='0.15'/%3E%3C/svg%3E";
  const img = new Image();
  img.onload = () => {
    const pattern = ctx.createPattern(img, 'repeat');
    ctx.fillStyle = pattern;
    ctx.globalAlpha = 0.1;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.globalAlpha = 1;
  };
  img.src = svgTexture;
  
  points = [];
  strokeCount = 0;
  debug.textContent = '状态:画布已清空';
}

// 防止右键菜单
canvas.addEventListener('contextmenu', (e) => e.preventDefault());

// 初始化
debug.textContent = '状态:准备就绪 | 提示:按住鼠标左键拖动绘制';
</script>
</body>
</html>

免费评分

参与人数 8吾爱币 +14 热心值 +6 收起 理由
samsu + 1 谢谢@Thanks!
hrh123 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
yanglinman + 1 谢谢@Thanks!
hjxhjxjx + 1 + 1 谢谢@Thanks!
hdjkksj3366 + 1 + 1 谢谢@Thanks!
pyjiujiu + 1 + 1 用心讨论,共获提升!
zyrwl + 1 + 1 谢谢@Thanks!
w1w2w1w2 + 1 + 1 我很赞同!你这个对我来说很有用,真是个人才,牛!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| wzs0777 发表于 2025-12-13 21:18

道林纸质手写.rar (2.76 KB, 下载次数: 114)
bachelor66 发表于 2025-12-14 09:38
效果还行,就是粗细、深浅最好能有些变化            
w1w2w1w2 发表于 2025-12-13 20:44
JikeCoolTech 发表于 2025-12-13 21:06
能否给个成品呀?
hbhaoming 发表于 2025-12-13 21:25
谢谢分享。下载学习了。
zyrwl 发表于 2025-12-13 21:54
感觉好的东西真多,谢谢分享!
wqw5257 发表于 2025-12-13 23:20
这个很需要,谢谢。
mxcz227 发表于 2025-12-14 05:28
写出来的是这样的啊
hancuicao 发表于 2025-12-14 08:16
收藏了 谢谢分享
moyc 发表于 2025-12-14 08:32
清空画布似乎失效
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-12-25 14:24

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表