Print

React动效 Framer Motion 布局动画


作者: 文章来源:

本文原地址:https://www.mimiwuqi.com/webqianduan/195889.html

目录文章目录
  1. 布局变化
  2. 用 CSS 做动画
  3. 性能
  4. FLIP
  5. 把所有东西放在一起
  6. 动画的大小
  7. 测量尺寸变化
  8. 反转尺寸变化
  9. 使用 position 固定大小
  10. 修复转换的起点
  11. 如果 Transform Origin 发生变化怎么办?
  12. 纠正子元素的变形
  13. 反比例公式
  14. 尝试
  15. 正确的缩放时间
  16. 其实不是这样的?

重现 framer 的神奇布局动画的指南。

到目前为止,我最喜欢 Framer Motion 的部分是它神奇的布局动画–将 layout prop 拍在任何运动组件上,看着该组件从页面的一个部分无缝过渡到下一个部分。

<motion.div layout />

React动效 Framer Motion 布局动画 1

在上面的例子中,蓝线表示父方的比例,而黄线表示子方的比例。请注意,蓝线是一条直线,而黄线则有点像曲线。这告诉我们,反比例的时间与父比例的时间是不一样的!

为了解决这个问题,我们可以这么做:

(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>中,这会有一些问题:

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 的运动组件树。

更多 建站教程 请访问 https://www.mimiwuqi.com/webqianduan/