一张图片被切成二十乘二十共四百个方块。初始状态下,这些方块散落在画面之外,各自带着随机的旋转角;拖动底部的滑块,它们会一边归位一边转正,图片随之从零碎逐渐拼完整。滑块拖到底时,裁剪整个关掉,露出原始图片。技术上是 SVG 的 clipPath 配原生 JavaScript:方块本身不是图片碎片,而是裁剪路径里的四百个矩形,图片始终只有一张。
这个效果的三个关键点
只有一张图。所有方块都是 clipPath 里的 rect,图片套上这个裁剪路径后,矩形移到哪里,图片就在哪里露出来,不需要复制四百份图片。
出发点在一个大圆上。每个方块的起始位置是在一个半径超过屏幕对角线的圆周上随机取的,所以初始状态必定在视野之外,从任何方向飞入。
进度由滑块直接映射。滑块的百分比被当作动画的「当前时间」喂给缓动函数,因此进度可以来回拖,不是只能单向播放。
把时间交给滑块的好处
常见做法是启动一个计时器让动画自己跑完。这份源码反过来,把滑块值换算成时间再去求每个方块的当前位置:
const time = sliderControlPercent * duration;
缓动用的是五次方出场曲线,前段快、末段缓,所以拼合的最后一点位移会显得格外细腻。这种「进度驱动」而非「时间驱动」的写法,好处是可以随意绑定到别的输入上——滚动位置、鼠标横坐标、音频强度都能直接接进来,动画逻辑一行不用改。
能改的地方
横竖各切几块是脚本开头的两个常量,改成 40 会更细碎但元素数量上升到一千六。缓动函数在文件末尾的工具类里,换成匀速或回弹会得到完全不同的手感。换图片只需替换 img 的地址,网格尺寸会按图片实际宽高自动重算。想改成自动播放,把读滑块的那一行换成按时间递增的变量即可。
常见问题
方块数量调大后很卡?
每个方块都是一个真实的 SVG 元素,每帧都要改属性,数量上到一千以上时低端设备会明显掉帧。可以减少切分数,或者只在拖动时更新、静止时停止重绘。
换成自己的图片后碎片对不齐?
网格尺寸是按图片渲染后的宽高算的,如果图片是异步加载的,脚本可能在尺寸还是 0 的时候就建好了网格。给图片加一个 load 回调,在其中再调用一次建网格即可。
能用在视频或整个页面上吗?
可以。裁剪路径能作用在任意元素上,把它套到视频、容器甚至整页包裹层上都成立,脚本只负责移动矩形。
适合用在哪
首屏图片的进场动画、产品发布页的揭晓效果、图集的切换过渡,也适合做成随滚动推进的叙事动画。裁剪路径构建、圆周起点取样与缓动实现都在脚本里,下载后可查看。