- 布局变化
- 用 CSS 做动画
- 性能
- FLIP
- 把所有东西放在一起
- 动画的大小
- 测量尺寸变化
- 反转尺寸变化
- 使用 position 固定大小
- 修复转换的起点
- 如果 Transform Origin 发生变化怎么办?
- 纠正子元素的变形
- 反比例公式
- 尝试
- 正确的缩放时间
- 其实不是这样的?
重现 framer 的神奇布局动画的指南。
到目前为止,我最喜欢 Framer Motion 的部分是它神奇的布局动画–将 layout prop 拍在任何运动组件上,看着该组件从页面的一个部分无缝过渡到下一个部分。
<motion.div layout />

在上面的例子中,蓝线表示父方的比例,而黄线表示子方的比例。请注意,蓝线是一条直线,而黄线则有点像曲线。这告诉我们,反比例的时间与父比例的时间是不一样的!
为了解决这个问题,我们可以这么做:
- 提前计算出正确的时间
- 每当父元素比例发生变化时,计算反比例。
(2)恰好比(1)简单得多,而且还允许我们在父元素上处理各种不同的时序。这也是 Framer Motion 使用的方法。
animate({
from: inverseTransform,
to: {
x: 0,
y: 0,
scaleX: 1,
scaleY: 1,
},
onUpdate: ({ x, y, scaleX, scaleY }) => {
parentRef.style.transform = `...`;
const inverseScaleX = 1 / scaleX;
const inverseScaleY = 1 / scaleY;
childRef.style.transform = `scaleX(${inverseScaleX}) scaleY(${inverseScaleY}) ...`;
},
});
App.js
import React from 'react'
import Motion from './Motion'
import './styles.css'
export default function App() {
const [toggled, toggle] = React.useReducer(state => !state, false)
const [corrected, toggleCorrected] = React.useReducer(state => !state, false)
return (
<div id="main">
<div>
<button onClick={toggle}>Toggle</button>
<label>
<input type="checkbox" checked={corrected} onChange={toggleCorrected} />
Corrected
</label>
</div>
<div id="wrapper" style={{ justifyContent: 'center' }}>
<Motion toggled={toggled} corrected={corrected}>Hello!</Motion>
</div>
</div>
)
}
Motion.js
const changed = (initialBox, finalBox) => {
// we just mounted, so we don't have complete data yet
if (!initialBox || !finalBox) return false;
// deep compare the two boxes
return JSON.stringify(initialBox) !== JSON.stringify(finalBox);
}
const invert = (el, from, to) => {
const { x: fromX, y: fromY, width: fromWidth, height: fromHeight } = from;
const { x, y, width, height } = to;
const transform = {
x: x - fromX - (fromWidth - width) / 2,
y: y - fromY - (fromHeight - height) / 2,
scaleX: width / fromWidth,
scaleY: height / fromHeight,
};
el.style.transform = `translate(${transform.x}px, ${transform.y}px) scaleX(${transform.scaleX}) scaleY(${transform.scaleY})`;
return transform;
}
其实不是这样的?
在这种情况下,使比例校正工作的方式是通过将子元素包裹在<div>中,并将比例校正应用于<div>中,这会有一些问题:
- 一个运动组件在 DOM 中有两个元素,从用户体验的角度来看,这可能是个问题
- 所有子组件都进行了比例校正,不可能一个子组件被校正而另一个子组件不被校正
- 如果子组件也在做动画,可能会有问题–我没有测试过,但我认为比例校正会导致问题,因为我们扭曲了子组件的坐标空间
Framer Motion 的做法有点不同,我们必须让子组件成为布局组件来选择加入比例校正。
<motion.article layout> <motion.h1 layout>Hello!</motion.h1> <-- is scale corrected <p>World!</p> <-- is not scale corrected </motion.article>
这个 API 意味着子组件需要能够 “钩住 “父组件的动画,这让实现变得更加复杂。
我选择不以这种方式实现,因为我不想脱离核心的比例校正概念。如果你有兴趣,可以看看 Framer Motion 源代码,他们使用一种叫做 “投影节点( “projection nodes”)”的东西来维护自己的类似 DOM 的运动组件树。