模式 · 27
共享边界过渡
Shared bounds · shared-bounds让详情页看起来是被点击的卡片长出来的。
直接切到大面板会丢掉用户刚点的是哪张卡。FLIP 先量小卡位置,再把目标摆到最终布局,用反向位移和缩放把它暂时拉回原处,最后让变换归零。关闭时重新测量原卡,才能准确回去。
公式
First = source.getBoundingClientRect() Last = detail.getBoundingClientRect() Invert: translate(First − Last) scale(First / Last) Play: transform → none
参数
| 本页默认 | 成熟实现 | |
|---|---|---|
| duration | 450 ms | —本页参考实现的时长。 |
| easing | cubic-bezier(.2,.8,.2,1) | — |
| source rect | 每次开关时重新测量 | — |
怎样做好
- 先量 First,再改变布局量 Last。反变换要同时包含平移与宽高比,否则卡片会凭空换尺寸。Paul Lewis, “FLIP Your Animations”, 2015; www-motion/rewrite/lib/motion-kit.js:269–275
- 关闭时量当前卡片,不要沿用打开时的旧矩形:网格可能已经随视口变化。Paul Lewis, “FLIP Your Animations”, 2015
- 明确记录来源卡片,让详情返回那一张,而不是默认第一张。www-motion/rewrite/lib/motion-kit.js:269–275
在我们的案例里
18 个案例具有共享边界过渡。momentum-slider、fluid-gallery 与 morph 都把画廊项接到详情视图;位置与缩放的连续性,比单纯淡入更能交代两者的关系。
出现在 18 个案例
参考实现 · basics.js
/**
* FLIP: measure First, change the layout to the Last state, Invert with a transform, Play to identity.
* Call with the element's rect from BEFORE the layout change.
*/
export function flip(el, first, { duration = 450, easing = 'cubic-bezier(.2,.8,.2,1)' } = {}) {
const last = el.getBoundingClientRect();
const dx = first.left - last.left, dy = first.top - last.top;
const sx = first.width / last.width, sy = first.height / last.height;
return el.animate([
{ transformOrigin: '0 0', transform: `translate(${dx}px, ${dy}px) scale(${sx}, ${sy})` },
{ transformOrigin: '0 0', transform: 'none' },
], { duration, easing });
}