Canvas布料模拟必须用质点-弹簧系统实时解算物理,因CSS仅支持整体变换,无法控制网格中各质点独立位置、张力传递与局部形变;需建模M×N质点阵,通过结构、剪切、弯曲三类弹簧施加弹力,并每帧依次累加重力、风力、弹簧力及阻尼后统一积分更新位置,再以贝塞尔曲线或三角形带渲染平滑曲面。

Canvas 布料模拟不是加个 CSS 动画就能出来的,必须用质点-弹簧系统实时解算物理——否则旗帜只会僵硬摆动或直接飞出屏幕。
为什么不能用 requestAnimationFrame 直接驱动 CSS transform
因为布料形变是局部、非刚性的:旗面不同区域弯曲程度不同,顶点之间有相对位移和张力传递。CSS 只能整体平移/旋转/缩放一个 DOM 元素,没法控制内部上百个点的独立位置与连接关系。
- 试图用
transform: matrix()模拟褶皱,很快会遇到插值断裂、透视失真、性能爆炸等问题 - Canvas 的优势在于你能精确控制每个像素对应的顶点坐标,而 DOM 渲染层根本不暴露这些中间状态
- 所有“看起来像布料”的纯 CSS 效果,本质都是预渲染动画帧或遮罩蒙版,无法响应风向变化或鼠标拖拽
怎么建模质点-弹簧系统(M×N 网格)
把旗帜拆成可计算的二维点阵,比如 12 列 × 8 行共 96 个 Point 实例,每个点存 x、y、vx、vy,用 Float32Array 打包存储比对象数组快 3–5 倍。
- 结构弹簧:连接上下/左右邻点,
k设为 0.2~0.4,维持基本形状 - 剪切弹簧:连接对角点(如 (i,j) ↔ (i+1,j+1)),
k设为 0.05~0.15,防网格菱形畸变 - 弯曲弹簧:连接隔行点(如 (i,j) ↔ (i+2,j)),
k设为 0.01~0.03,抑制高频抖动 - 旗杆端的点设
pin_x/pin_y锚定,不参与位置更新
每帧怎么更新?别漏掉这四步顺序
物理计算必须严格按序执行,否则会出现穿模、振荡发散或能量凭空增加:
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
- 先累加重力:
vy += g(g = 0.15左右,Canvas y 向下为正) - 再叠加风力:用
Math.sin(Date.now() * 0.001)或 Perlin 噪声生成水平扰动力,避免机械重复 - 然后解弹簧:遍历所有约束,按胡克定律
F = k * (L - L0)计算弹力并累加到两端点的vx/vy - 最后加阻尼:
vx *= 0.99、vy *= 0.995,否则小球越弹越快
注意:所有力更新完,才统一执行位置积分:x += vx; y += vy;,千万别边算边改位置。
Canvas 渲染时怎么让质点变成平滑布面
直接用 ctx.fillRect() 画点毫无意义;用户看到的是连续曲面,不是离散点阵。
- 边缘拟合:按行取首尾点,用
ctx.bezierCurveTo()连接,形成自然弧线轮廓 - 填充主体:把质点网格转为三角形带(triangle strip),调用
ctx.fill()一次性绘制,比逐条画线快得多 - 防抖关键:连线前加距离阈值判断,比如
if (dist 才连,否则远点误连导致画面撕裂 - 若需纹理感,可在 canvas 上叠加半透明噪点图层,或根据顶点法向量(邻点叉积估算)混合渐变色
真正卡顿的从来不是渲染,而是每帧对 O(N²) 邻域做距离判断——所以别盲目堆质点数,12×8 是平衡真实感与 60fps 的实用起点。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










