有时候刷到那种又克制又有趣的宫格动效——
一整面卡片矩阵缓缓上下滑动,每停顿一下,又像被一只“无形的手”轻轻按了一下再弹回去。
你大脑里又会自动弹出一句:
嗯,这味儿还是有点 Apple。优雅,实在是优雅。
这种效果常见在 Apple 官网、iOS 小组件、精致 landing page:
不是“暴力左右轮播”,而是一整块宫格在视野里轻轻起伏、带着弹性感切换信息。
今天这篇就来拆一段实际在用的 “宫格弹跳” SVG 动画,
看看它是怎么只靠几条 <animateTransform>,做出这种 Apple 风格的“宫格轮播”。
本文代码基于一个现成的 SVG 编辑器组件做练习,只做技术拆解与学习。
一、先说效果:它到底在动什么?
用一句话描述这个“宫格弹跳”:
一整块 3 列 × N 行的卡片网格,沿着 Y 轴上下移动,
在关键位置会轻微缩放(scale 到 0.7 再回 1),
形成一种 “滑到某一屏 → 被按一下 → 再滑走” 的节奏感。
视觉感觉上就像:
- 一面由 App 卡片组成的长墙,缓慢往上移动
- 每当某一行来到黄金视野区域,就会被轻轻“按扁”一下再弹回
- 整个动画全程循环,看起来像一个 有节奏呼吸的长版宫格轮播
而这一切,都是用一段 SVG + SMIL 完成的。
二、整体结构:一个 <g> 扛起整面宫格
先看一下代码的骨架(我把无关属性都略掉了,只保留动效相关结构):
<svg
viewBox="-0 0 0 0"
width="100%"
style="display:block; overflow:hidden;">
<rect x="0" y="0" width="0" height="0" fill="#000000" />
<!-- 整面宫格都包在这个 g 里 -->
<g transform="translate(0 -50)">
<!-- 这里是一大堆 foreignObject,每个就是一张小卡片 -->
<foreignObject x="-30" y="0" width="0" height="0">...</foreignObject>
<foreignObject x="0" y="0" width="0" height="0">...</foreignObject>
<foreignObject x="30" y="0" width="0" height="0">...</foreignObject>
<foreignObject x="-30" y="50" width="0" height="0">...</foreignObject>
<foreignObject x="0" y="50" width="0" height="0">...</foreignObject>
<foreignObject x="30" y="50" width="0" height="0">...</foreignObject>
<!-- 下面还有很多行:y=100,150,200,250,300 ... -->
<!-- 关键的平移动画 -->
<animateTransform
type="translate"
attributeName="transform"
begin="0s"
dur="8s"
repeatCount="indefinite"
calcMode="spline"
values="0 -50; 0 -50; 0 -35; 0 -35; 0 -70; 0 -70; ...; 0 -250; 0 -50"
keyTimes="0; 0.125; 0.1625; ...; 0.999999; 1"
keySplines="0.42 0 0.58 1; ...; 0 0 1 1" />
<!-- 关键的缩放弹跳 -->
<animateTransform
type="scale"
attributeName="transform"
begin="0s"
dur="8s"
repeatCount="indefinite"
calcMode="spline"
additive="sum"
values="1; 1; 0.7; 0.7; 1; 1; 0.7; 0.7; 1; ..."
keyTimes="0; 0.125; 0.1625; ...; 0.999999; 1"
keySplines="0.42 0 0.58 1; ...; 0 0 1 1" />
</g>
</svg>
几个关键点:
-
只有一个大
<g>在动:所有小卡片都挂在这个<g>下 -
每个卡片是一个
foreignObject,里面再嵌一层<svg>,用background-image铺图 -
真正的动画只有两条:
- 一条控制 纵向 translate
- 一条控制 scale 弹跳(并且是
additive="sum"叠加在平移上)
可以理解为:
把一整面宫格丢进一个“电梯”里,
再让这个电梯沿着 Y 轴上下,
路上在不同楼层轻轻弹一下。
三、宫格是怎么排出来的?3 列 × N 行的坐标网格
中间那一长串 foreignObject,就是宫格本体。
它的排布方式非常直白:
- x 坐标只有三个值:
-30,0,30→ 三列 - y 坐标每次加 50:
0, 50, 100, 150, 200, 250, 300...→ 多行 - 每个里头再放一个内嵌
<svg>,用背景图显示一张卡片。
伪代码可以理解成:
<!-- 第 1 行 -->
<foreignObject x="-30" y="0"> [卡片1] </foreignObject>
<foreignObject x="0" y="0"> [卡片2] </foreignObject>
<foreignObject x="30" y="0"> [卡片3] </foreignObject>
<!-- 第 2 行 -->
<foreignObject x="-30" y="50"> [卡片4] </foreignObject>
<foreignObject x="0" y="50"> [卡片5] </foreignObject>
<foreignObject x="30" y="50"> [卡片6] </foreignObject>
<!-- 第 3 行 -->
<foreignObject x="-30" y="100"> [卡片7] </foreignObject>
...
每个格子内部长这样(简化版):
<foreignObject x="-30" y="0" width="0" height="0">
<svg
width="0"
height="0"
style="
display:block;
background-image:url('你的图片地址');
background-position:center center;
background-size:cover;
background-repeat:no-repeat;">
</svg>
</foreignObject>
你要做自己的宫格:
- 只需要把
background-image:url("")换成自己的图片 - 每个格子可以理解为一个 App 图标 / 分类卡片 / 产品卡片
这层完全不动,只是跟着外层 <g> 一起上下移动 + 缩放。
四、纵向平移:translate 把宫格当整块内容往上“推”
第一个 animateTransform 控制的是纵向位移:
<animateTransform
type="translate"
attributeName="transform"
begin="0s"
dur="8s"
repeatCount="indefinite"
calcMode="spline"
values="0 -50; 0 -50; 0 -35; 0 -35; 0 -70; 0 -70; 0 -100; ...; 0 -250; 0 -50"
keyTimes="0; 0.1250; 0.1625; ...; 0.999999; 1"
keySplines="0.42 0 0.58 1; ...; 0 0 1 1" />
这里有三个点很有意思:
1)values 不是简单“从 A 到 B”
而是很多个分段:
0 -500 -350 -700 -1000 -700 -1050 -1500 -1050 -1400 -2000 -1400 -1750 -2500 -50
这意味着:
宫格不会“线性一路向上”,
而是像电梯那样:在不同楼层短暂停顿、再加速、再停顿、再继续往上。
视觉上就会出现:
- 有时缓慢上移一点点
- 有时突然位移更多像跳一格
- 有时又回弹一些,再继续往上
- 最后从某个最高点瞬间回起点(实现循环)
这正是 Apple 那种网页里常见的“有层次的内容切换”的感觉,而不是简单 banner 滑动。
2)keyTimes 精细控制节点节奏
keyTimes 是一组 0~1 之间的比例,表示:
每一个 values 对应在整段动画时间轴上的位置。
比如简化理解:
- 0:起点(0%)
- 0.125:12.5% 位置
- 0.25:25% 位置
- ……
- 1:终点(100%)
通过这种方式,作者可以非常精细地控制:
- 每一段位移花多长时间
- 哪些位置停留时间更长
- 哪一段“跳得更急”
3)贝塞尔曲线:keySplines="0.42 0 0.58 1"
calcMode="spline" + keySplines="0.42 0 0.58 1" 这组合,其实就是 SVG 里的 cubic-bezier:
cubic-bezier(0.42, 0, 0.58, 1) ≈ ease-in-out
效果是:
- 起步慢一点
- 中间稍微加速
- 结束时再慢一下刹车
配合上那一堆不是很大的位移值,整块宫格动起来就变成:
没有“广告位滑动”的廉价感,
更像一组内容被轻轻推上来的“过场动画”。
五、弹跳感从哪来?第二条 scale 动画是关键
接下来这条 animateTransform 就是“弹跳感”的来源:
<animateTransform
type="scale"
attributeName="transform"
begin="0s"
dur="8s"
repeatCount="indefinite"
calcMode="spline"
additive="sum"
values="1; 1; 0.7; 0.7; 1; 1; 0.7; 0.7; 1; 1; ..."
keyTimes="0; 0.1250; 0.1625; 0.2125; 0.25; ..."
keySplines="0.42 0 0.58 1; ...; 0 0 1 1" />
几个关键信息:
1)additive="sum":叠加在前面的 translate 上
type="scale"、additive="sum" 表示:
这个缩放不是单独存在的,而是和前面的
translate叠加。
因此:
- 纵向平移负责“把宫格整体上下推进视野”
- 这个
scale负责“在特定节点把整块宫格压扁/弹回”
2)values:1 → 0.7 → 1 的周期
看 values:
1; 1; 0.7; 0.7; 1; 1; 0.7; 0.7; 1; ...
大概就是一段一段的:
正常大小 → 缩到 0.7 → 停一下 → 回到 1
在视觉上就是:
- 上滑到某个“分屏位置”
- 宫格突然“被按一下” → 缩小一点
- 再弹回原始大小 → 像是确认“对齐了一屏内容”
跟刚才 translate 的 keyTimes 一组合起来,就是:
- 某个 Y 值段:宫格刚好停住
- 同一时间段:scale 从 1 → 0.7 → 1
- 于是你看到的就是 “滑到一个位置 → 咚一下 → 再溜走” 的弹跳感
这非常符合 Apple、GQ、奈雪这类品牌喜欢用的那种 “有质量感的卡片运动” 。
六、如果你想做自己的宫格弹跳,可以改这些地方
基于上面的结构,如果你要在自己项目里做类似效果,大致可以改 4 个点:
1. 替换成你自己的图片
找到每个小格的这段:
<svg
style="
display:block;
background-image:url('https://你的图.jpg');
background-position:center;
background-size:cover;
background-repeat:no-repeat;">
</svg>
把 URL 换成你的图片地址即可。
- 建议所有图尺寸一致、风格统一
- 做产品宫格、栏目宫格、内容卡片都可以
2. 改宫格密度和间距
通过 foreignObject 的 x / y 来改:
- 想更紧凑:把
x改近一点,比如-25, 0, 25,y间距改小 - 想更松弛:把行距、列距都拉开一点
3. 调整节奏:更佛系 or 更蹦迪
节奏由这几项决定:
dur="8s":一轮多长时间values(translate):切换到哪些 Y 值keyTimes:每段停留占总时长的比例values(scale):是否要弹得更狠(比如缩放到 0.6)
例如:
-
做“禅系 / 画册感”:
dur改成 12s 或 16s- 减少弹跳次数(少一些 0.7)
-
做“节日活动页”:
dur改成 6s- 把缩放做得更明显,比如
1 → 0.6 → 1
4. 控制循环方式
目前的写法是:
repeatCount="indefinite":无限循环- 最后一个值从
0 -250回到0 -50:相当于瞬间 teleport 到初始位置,继续下一轮
如果你想只播一次,可以:
- 去掉
repeatCount或改成1 - 结尾
values停在你想要的位置即可
七、不想手写的话:E2 编辑器里有现成的“宫格弹跳”组件
如果你不想自己去排整块宫格、算 keyTimes、调贝塞尔曲线,也有一个更省事的方式:用现成组件。
比如在 E2 SVG 编辑器 里,就有一个已经封装好的交互组件,名字就叫:
「宫格弹跳」
这个组件的特性大致是:
-
基于纯 SVG 动画(无需 JS),适配公众号 / H5 / Web
-
宫格排布已经设计好,包含多行多列的图片网格
-
translate + scale 的弹跳节奏已调好,默认就是类似 Apple 风格的“轻盈起伏”
-
只需要:
- 上传自己的多张图片(尺寸统一更好)
- 调整一些基础参数(节奏、时长、整体缩放等)
- 一键导出 SVG 代码,直接嵌入到公众号或网页
更关键的是:
-
这个「宫格弹跳」组件是免费使用的
-
整个 E2 编辑器是专门做 SVG 互动动效的,有大量类似:
- 轮播 / 宫格 / 滚动动效
- 点击掉落 / 刮刮乐 / 烟花特效 / “我是谁”猜影子组件
- 适合运营人、设计师、开发者做高互动图文
对前端来说也很友好:
- 你可以先在 E2 里把“动效结构”预制好
- 导出 SVG 后,再在自己的项目里做二次封装、懒加载、按需插入
- 既保留了可视化编辑的效率,又可以保留代码层面的可控性
小结
这类 “宫格弹跳” Apple 风格轮播,本质上做的事情并不复杂:
- 把所有小卡片,用
foreignObject排成 3 列 × N 行网格 - 整体包在一个
<g>里 - 用一条
translate动画控制纵向位移,模拟滑屏/切屏 - 用一条
scale动画叠加在上面,制造“弹跳感” - 通过
keyTimes + keySplines精细控制节奏,让宫格“会呼吸”
拆开来看全是很朴素的 SVG 技巧,
组合在一起,就能做出那种:
“好像没怎么动,但越看越顺眼”的 Apple 味宫格轮播。
如果你也在折腾 SVG / 公众号 / H5 的互动动效,可以试着先用一两个宫格开始玩起,
搞明白一次 translate + scale 的组合关系之后,后面很多“高级感动效”其实都是同一套套路延伸。