纯css无法实现真正物理意义上的液态变形,仅能通过伪元素形变、渐变位移和贝塞尔缓动模拟流动感;真实像素扰动、粘滞拖曳或鼠标响应式扭曲必须依赖canvas或webgl。

纯 CSS 做不出真正物理意义上的液态变形(blob),所谓“液态 blob”只是视觉错觉——靠伪元素形变 + 渐变位移 + 贝塞尔缓动模拟流动感,真要像素级扰动、粘滞拖曳或鼠标响应式扭曲,必须上 Canvas 或 WebGL。
用 ::before 做液态 blob 按钮覆盖动画
这是最常用、兼容性最好、也最容易失控的方案。核心不是“画液体”,而是让一个带圆角的伪元素在按钮上“漫过去”,靠尺寸、定位和 transform 组合出浸润感。
-
::before必须比按钮宽高至少大一圈:横向推荐width: 200%; left: -50%,纵向同理;否则动画中途会露底或断层 - 父容器得设
position: relative和overflow: hidden,否则伪元素溢出不可控 - 起始状态用
transform: translateY(100%) scaleY(0.8),不是translateY(0)——液体是从下往上涌的 - 关键帧里混入
skewY(-2deg)和短暂scaleY(1.05),这是识别“液态”的视觉线索,纯translateY看起来像窗帘 - 缓动函数别用
ease-in-out,改用cubic-bezier(0.34, 1.56, 0.64, 1),前段快、中段冲、后段柔
@keyframes 里写 transform 却不动?检查这几点
动画不跑,90% 是绑定或定义漏了硬性条件。
-
@keyframes名字必须和animation-name完全一致(大小写敏感),比如定义了@keyframes blobIn,但写了animation: blobin 0.9s,就静止不动 - 没加
animation-fill-mode: forwards,动画播完立刻弹回初始态,看起来像“闪一下” - 伪元素没设
content: "",::before或::after不渲染,动画自然无效 - 目标元素是
display: none或刚插入 DOM 还没重排,requestAnimationFrame都救不了——先确保它已渲染
Canvas 实现基础 blob 扭曲(非 SVG filter)
SVG <feturbulence></feturbulence> + <fedisplacementmap></fedisplacementmap> 看似简单,但调不出液体感、移动端支持差、性能还低,真要像素扰动,Canvas 是更可控的底线选择。
- 图片必须设
crossOrigin="anonymous",否则ctx.getImageData()报SecurityError - 偏移量别用整数加减,用
Math.sin(y * 0.01 + time * 0.002) * 6这类带频率/振幅的表达式,否则抖动僵硬 - 千万别直接
putImageData(),锯齿严重;正确流程是:扰动计算 → 写入离屏 canvas →drawImage()合成到主 canvas -
requestAnimationFrame的time是毫秒值,传给三角函数前记得除以1000,否则波动快到失真
WebGL 着色器才是真 blob 变形的唯一出路
要做鼠标拖拽粘滞、多层折射、实时响应扰动的 blob,Canvas 逐帧计算顶不住,必须进 WebGL。这不是“加个滤镜”,而是把图像当纹理喂给 GLSL 片元着色器,在 GPU 上跑噪声函数生成位移场。
- 位移向量得用 2D 噪声组合,比如
vec2 noise = vec2(snoise(vec3(uv * 2.0, u_time)), snoise(vec3(uv * 2.0 + 1.0, u_time))) - UV 坐标必须归一化到
0–1范围,否则位移溢出;循环用fract(),别用% - 移动端注意
highp精度支持,部分 Android GPU 不支持sin()/cos()在 fragment shader 中,得降级为查表或简化公式 - 别误用
filter: url(#svg-filter)——它作用于整个盒模型,文字、边框、阴影全被扭曲,根本不是“blob 变形”,是“盒子糊了”
真正难的不是写出动画,而是判断该用哪一层:按钮悬停用伪元素就够了;头像波纹用 Canvas 更稳;而要做可交互、带物理反馈的 blob 场景,WebGL 是绕不开的坎——而且得自己写着色器,没有“一键液态”这种事。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











