每次看到开发者为了给一个SVG图标加动画就拉来整个JavaScript动画库,我都觉得难受。一个旋转的加载图?一个描边渐显的标志?一个插图上的悬停动效?这些东西不需要GreenSock,不需要Anime.js,连一行JavaScript都用不上。
CSS能直接驱动SVG动画,而且比引入额外依赖更快、更轻、更直接。下面来说具体怎么做。
![]()
放在DOM里的SVG元素,说到底就是普通的页面元素——像变换、透明度这些CSS属性,还有专用于线条的描边属性,都能直接作用于这些元素。和给HTML元素做动画相比,关键区别有两个:SVG用自己的一套坐标系来处理变换,基线是SVG视图框的原点,不是元素自身的中心;另外,SVG有一套控制线条绘制的特殊属性。
最让人上瘾的SVG动画手法是描边渐显——让图案像被一支隐形的笔一笔一笔画出来。核心就是两个属性:stroke-dasharray和stroke-dashoffset。把这两个值都设成路径的总长度,然后让描边偏移量从总长度逐渐减到零。原理是这样:stroke-dasharray造一条虚线,当虚线的实线部分长度刚好等于路径总长时,这条线就是实心的。stroke-dashoffset负责把这段实线沿着路径推移。一开始偏移量等于路径总长,整段实线被推到画面之外,什么都看不见,然后逐步拉回来,线条就一笔一笔地浮现。
具体操作时,你需要知道路径的精确长度。在浏览器开发者工具的控制台里,对路径元素执行getTotalLength()就能拿到这个值,然后把stroke-dasharray和stroke-dashoffset都设成它。如果不想这么精确,取一个足够大的数值也行,只要两个属性保持一致,效果就跑得通。动画本身用CSS关键帧实现,设置一个两秒钟缓入缓出的过渡,填充模式用forward让动画结束后停在最后一帧。
SVG元素同样能对CSS变换做出响应,缩放和旋转都不在话下。但有个坑:变换的默认原点不在元素本身的中心,而在SVG视图框的(0,0)位置。一个齿轮图标的旋转动画,如果只写旋转360度而不调整原点,它会绕着整张画布的左上角转圈,而不是绕着自己的轴心转。解决方法是显式声明transform-origin。单元素的情况设成center通常管用,对于组合在一起的复杂图形组,可能需要精确指定像素坐标,直接绑定到组的包围盒中心点上。
多个元素同时动起来会显得很机械。给每个元素加上不同的animation-delay,就能制造出层叠展开的效果。比如一组标志字母,依次延迟0秒、0.1秒、0.2秒、0.3秒、0.4秒启动动画。配合animation-fill-mode设成backwards,让每个元素在各自的延迟时间到达之前保持不可见状态,最终呈现出来的就是一个干净利落的渐次揭示序列,整个过程一行JavaScript都没碰。
从性能角度比较,CSS动画和JavaScript驱动的动画走的是两条完全不同的路径。对简单的SVG动效——变换、透明度、描边属性——CSS的渲染性能可以把JavaScript甩在后面。浏览器把CSS动画交给合成器线程处理,这个线程独立于浏览器的主线程运行。主线程被大量JavaScript计算或页面回流操作堵住的时候,旋转和淡入淡出照样能流畅执行。而JavaScript动画必须跑在主线程上,一个阻塞操作就让所有动画一起卡住。这种差异在低配设备、移动端浏览器和页面负载较重的场景下尤为明显。
那么那些重量级动画库到底提供了什么?它们确实能处理更复杂的需求:复杂的时间线编排、基于物理的运动曲线、动态规划贝塞尔路径上的运动轨迹、精细的逐帧编排控制。对这些场景,JavaScript库是在正确的位置上发力。但只为一个旋转的加载图标或一段描边渐显动画引入一个几十KB的外部库,对性能来说是不必要的负担。用CSS,不仅少了网络请求的开销,浏览器还能对动画执行路径进行更深层的优化,某些情况下甚至能把整个动画完全放到合成器线程上,传输、解析和渲染的成本降到最低。即便在较老或性能较弱的设备上,页面也能保持响应。文件体积更小、执行依赖更少、渲染效率更高。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.