searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

react-spring 弹簧动画

2023-06-02 06:19:19
21
0

react-spring 是什么

spring 这里是 弹簧 的意思

react-spring 是 基于弹簧物理学(spring-physics based) 的动画库,官网自称能覆盖绝大多数的动画需求。

对比之下,css 是 基于时间(time-based) 的动画体系。

为什么基于弹簧而不基于时间

react-spring 有个弹簧的规则在里面,并不是 css 中的通过 curve 或者 duration 来创建动画。这两者有很大的不同。 我们认为动画是由 time(duration)和 curve 组成的,这本身会带来很大的挑战当我们使 element 在屏幕上自然运动的时候,因为现实世界中没有什么东西是这样运动的。

第一个例子

import { useSpring, animated } from "react-spring";
const Demo1 = () => {
  const spring = useSpring({ to: { height: 100 }, from: { height: 0 } });
  return (
    <animated.div className="bg-blue-300 overflow-hidden" style={spring}>
      Demo1 -- 单个高度动画
    </animated.div>
  );
};

第一步,导入

import { useSpring, animated } from "react-spring";

本动画库在 React 之外进行动画(为了性能的考虑),所以我们的 view 必须怎么处理传给它的animated props,这正是animated的作用,它将扩展原生 element 以支持animated values

第二步,定义一个弹簧

const spring = useSpring({ to: { height: 100 }, from: { height: 0 } });

一个弹簧简单的把values从一个状态推向另一个状态。更新是累计的,弹簧会记忆你传给他的所有属性。这些值会自动累计更新,这正是正常的标签不能处理的原因,必须使用animated.div

最后,绑定弹簧和 view

return (
  <animated.div className="bg-blue-300 overflow-hidden" style={spring}>
    Demo1 -- 单个高度动画
  </animated.div>
);

把定义的spring作为 style 的值传给animated.div即可

不仅仅对 styles 进行动画

可以把弹簧应用在任何地方,只要你喜欢

对 innerText 动画

function Number() {
  const [flip, set] = useState(false);
  const { number } = useSpring({
    reset: true,
    reverse: flip,
    from: { number: 0 },
    // to: { number: 1 },
    number: 1, // 和to: { number: 1 }效果一样
    delay: 200,
    config: config.molasses,
    onRest: () => set(!flip),
  });

  return <animated.div>{number.to((n) => n.toFixed(2))}</animated.div>;
}

对 scrollTo 动画

function Scrolling() {
  const [flip, set] = useState(false)

  const words = ['We', 'came.', 'We', 'saw.', 'We', 'kicked', 'its', 'ass.']

  const { scroll } = useSpring({
    scroll: (words.length - 1) * 50,
    from: { scroll: 0 },
    reset: true,
    reverse: flip,
    delay: 200,
    config: config.molasses,
    onRest: () => set(!flip),
  })

  return (
    <animated.div
      style={{
        position: 'relative',
        width: '100%',
        height: 60,
        overflow: 'auto',
        fontSize: '0.5em',
      }}
      scrollTop={scroll}>
      {words.map((word, i) => (
        <div
          key={`${word}_${i}`}
          style={{ width: '100%', height: 50, textAlign: 'center' }}>
          {word}
        </div>
      ))}
    </animated.div>
  )
}

不仅仅对数字进行动画

以下这些统统可以进行动画

  • Colors (names, rgb, rgba, hsl, hsla, gradients)
  • CSS Variables (declared on :root) and their fallbacks
  • Absolute lengths (cm, mm, in, px, pt, pc)
  • Relative lengths (em, ex, ch, rem, vw, vh, vmin, vmax, %)
  • Angles (deg, rad, grad, turn)
  • Flex and grid units (fr, etc)
  • All HTML attributes
  • SVG paths (as long as the number of points matches, otherwise use custom interpolation)
  • Arrays
  • String patterns (transform, border, boxShadow, etc)
  • Non-animatable string values (visibility, pointerEvents, etc)
  • ScrollTop/scrollLeft

对color进行动画

const Color = () => {
  const [flip, set] = useState(false);

  const { color } = useSpring({
    from: { color: "blue" },
    color: "green",
    reset: true,
    reverse: flip,
    delay: 200,
    config: config.molasses,
    onRest: () => set(!flip),
  });

  return (
    <div>
      <animated.div
        style={{ background: color, color: "white" }}
        className="p-4"
      >
        hello
      </animated.div>
    </div>
  );
};

对border进行动画

const Border = () => {
  const [flip, set] = useState(false);
  const { o, xyz, color } = useSpring({
    from: { o: 0, xyz: [0, 0, 0], color: "red" },
    o: 1,
    xyz: [10, 20, 5],
    reset: true,
    delay: 200,
    reverse: flip,
    config: config.molasses,
    color: "green",
    onRest: () => set(!flip),
  });

  return (
    <animated.div
      style={{
        border: to([o, color], (o, c) => `${o * 10}px solid ${c}`),
      }}
    >
      {o.to((n) => n.toFixed(2)) /* innerText interpolation ... */}
    </animated.div>
  );
};

几个重要概念

interpolate

interpolate可以理解为 插值 ,在 react-string 主要通过to方法或者spring.to方法来完成。比如下面就是一个interpolate的例子

style={{
  border: to([o, color], (o, c) => `${o * 10}px solid ${c}`),
}}

range

range是范围的意思,在 react-string 中就是将从fromto之间的值进行一个分段,也就是说将动画过程分段,可以理解为 css 中的keyframes

上例子

const Padding = () => {
  const [flip, set] = useState(false);

  const { color, o, xyz } = useSpring({
    from: { color: "blue", o: 0, xyz: [0, 0, 0] },
    o: 1,
    xyz: [10, 20, 5],
    color: "green",
    reset: true,
    reverse: flip,
    delay: 200,
    config: config.molasses,
    onRest: () => set(!flip),
  });

  return (
    <div>
      <animated.div
        style={{
          // You can also form ranges, even chain multiple interpolations
          padding: o
            .to({ range: [0, 0.5, 1], output: [0, 0, 10] })
            .to((o) => `${o}%`),
        }}
        className="p-4"
      >
        {o.to((n) => n.toFixed(2))}
      </animated.div>
    </div>
  );
};

output就很好理解了,就是到达分界点的时候的to值应该是多少。

如何对auto进行动画

react-string 不能对auto进行动画,不过别灰心,我们可以迂回一下,比如借助 react-use-measure 来获取元素的宽高,然后对其进行动画。

const Auto = () => {
  const [flip, set] = useState(false);
  const [ref, bounds] = useMeasure();

  const { height } = useSpring({
    from: { height: 0 },
    height: bounds.height,
    reset: true,
    reverse: flip,
    delay: 200,
    config: config.molasses,
    onRest: () => set(!flip),
  });

  return (
    <animated.div
      className="bg-lime-500 "
      style={{ overflow: "hidden", height }}
    >
      <div ref={ref} className="p-4" style={{ overflow: "hidden" }}>
        豇豆角搜集
        <br />
        掉绝对是
        <br />
        d的简欧is解耦司机端
        <br />
        d的简欧is解耦司机端222
      </div>
    </animated.div>
  );
};

注意ref的位置必须在animated.div的下一个层级,如果放在animated.div上,height是一直变化的,这就没法玩了。

交互与动画

如何将交互和动画结合起来呢?react-with-gesture应该是最好的选择了。

import { useGesture } from "react-with-gesture";

const Gesture = (props) => {
  const [{ xy, scale }, set] = useSpring(() => ({ xy: [0, 0], scale: 1 }));
  const bind = useGesture(({ down, delta }) => {
    set({
      xy: down ? delta : [0, 0],
      scale: down ? 2 : 1,
    });
  });
  return (
    <animated.div
      {...bind()}
      style={{
        transform: to(
          [xy, scale],
          (xy, s) => `translate3d(${xy[0]}px, ${xy[1]}px, 0) scale(${s})`
        ),
      }}
      className="bg-blue-500 w-10 h-10"
    ></animated.div>
  );
};

注意to的用法,是直接将scalexy的值传入,而不是用scale.to(...)xy.to(...),因为当transform有多个值的时候采用后者是做不到的。

参考

0条评论
0 / 1000
尹****勇
2文章数
0粉丝数
尹****勇
2 文章 | 0 粉丝
尹****勇
2文章数
0粉丝数
尹****勇
2 文章 | 0 粉丝
原创

react-spring 弹簧动画

2023-06-02 06:19:19
21
0

react-spring 是什么

spring 这里是 弹簧 的意思

react-spring 是 基于弹簧物理学(spring-physics based) 的动画库,官网自称能覆盖绝大多数的动画需求。

对比之下,css 是 基于时间(time-based) 的动画体系。

为什么基于弹簧而不基于时间

react-spring 有个弹簧的规则在里面,并不是 css 中的通过 curve 或者 duration 来创建动画。这两者有很大的不同。 我们认为动画是由 time(duration)和 curve 组成的,这本身会带来很大的挑战当我们使 element 在屏幕上自然运动的时候,因为现实世界中没有什么东西是这样运动的。

第一个例子

import { useSpring, animated } from "react-spring";
const Demo1 = () => {
  const spring = useSpring({ to: { height: 100 }, from: { height: 0 } });
  return (
    <animated.div className="bg-blue-300 overflow-hidden" style={spring}>
      Demo1 -- 单个高度动画
    </animated.div>
  );
};

第一步,导入

import { useSpring, animated } from "react-spring";

本动画库在 React 之外进行动画(为了性能的考虑),所以我们的 view 必须怎么处理传给它的animated props,这正是animated的作用,它将扩展原生 element 以支持animated values

第二步,定义一个弹簧

const spring = useSpring({ to: { height: 100 }, from: { height: 0 } });

一个弹簧简单的把values从一个状态推向另一个状态。更新是累计的,弹簧会记忆你传给他的所有属性。这些值会自动累计更新,这正是正常的标签不能处理的原因,必须使用animated.div

最后,绑定弹簧和 view

return (
  <animated.div className="bg-blue-300 overflow-hidden" style={spring}>
    Demo1 -- 单个高度动画
  </animated.div>
);

把定义的spring作为 style 的值传给animated.div即可

不仅仅对 styles 进行动画

可以把弹簧应用在任何地方,只要你喜欢

对 innerText 动画

function Number() {
  const [flip, set] = useState(false);
  const { number } = useSpring({
    reset: true,
    reverse: flip,
    from: { number: 0 },
    // to: { number: 1 },
    number: 1, // 和to: { number: 1 }效果一样
    delay: 200,
    config: config.molasses,
    onRest: () => set(!flip),
  });

  return <animated.div>{number.to((n) => n.toFixed(2))}</animated.div>;
}

对 scrollTo 动画

function Scrolling() {
  const [flip, set] = useState(false)

  const words = ['We', 'came.', 'We', 'saw.', 'We', 'kicked', 'its', 'ass.']

  const { scroll } = useSpring({
    scroll: (words.length - 1) * 50,
    from: { scroll: 0 },
    reset: true,
    reverse: flip,
    delay: 200,
    config: config.molasses,
    onRest: () => set(!flip),
  })

  return (
    <animated.div
      style={{
        position: 'relative',
        width: '100%',
        height: 60,
        overflow: 'auto',
        fontSize: '0.5em',
      }}
      scrollTop={scroll}>
      {words.map((word, i) => (
        <div
          key={`${word}_${i}`}
          style={{ width: '100%', height: 50, textAlign: 'center' }}>
          {word}
        </div>
      ))}
    </animated.div>
  )
}

不仅仅对数字进行动画

以下这些统统可以进行动画

  • Colors (names, rgb, rgba, hsl, hsla, gradients)
  • CSS Variables (declared on :root) and their fallbacks
  • Absolute lengths (cm, mm, in, px, pt, pc)
  • Relative lengths (em, ex, ch, rem, vw, vh, vmin, vmax, %)
  • Angles (deg, rad, grad, turn)
  • Flex and grid units (fr, etc)
  • All HTML attributes
  • SVG paths (as long as the number of points matches, otherwise use custom interpolation)
  • Arrays
  • String patterns (transform, border, boxShadow, etc)
  • Non-animatable string values (visibility, pointerEvents, etc)
  • ScrollTop/scrollLeft

对color进行动画

const Color = () => {
  const [flip, set] = useState(false);

  const { color } = useSpring({
    from: { color: "blue" },
    color: "green",
    reset: true,
    reverse: flip,
    delay: 200,
    config: config.molasses,
    onRest: () => set(!flip),
  });

  return (
    <div>
      <animated.div
        style={{ background: color, color: "white" }}
        className="p-4"
      >
        hello
      </animated.div>
    </div>
  );
};

对border进行动画

const Border = () => {
  const [flip, set] = useState(false);
  const { o, xyz, color } = useSpring({
    from: { o: 0, xyz: [0, 0, 0], color: "red" },
    o: 1,
    xyz: [10, 20, 5],
    reset: true,
    delay: 200,
    reverse: flip,
    config: config.molasses,
    color: "green",
    onRest: () => set(!flip),
  });

  return (
    <animated.div
      style={{
        border: to([o, color], (o, c) => `${o * 10}px solid ${c}`),
      }}
    >
      {o.to((n) => n.toFixed(2)) /* innerText interpolation ... */}
    </animated.div>
  );
};

几个重要概念

interpolate

interpolate可以理解为 插值 ,在 react-string 主要通过to方法或者spring.to方法来完成。比如下面就是一个interpolate的例子

style={{
  border: to([o, color], (o, c) => `${o * 10}px solid ${c}`),
}}

range

range是范围的意思,在 react-string 中就是将从fromto之间的值进行一个分段,也就是说将动画过程分段,可以理解为 css 中的keyframes

上例子

const Padding = () => {
  const [flip, set] = useState(false);

  const { color, o, xyz } = useSpring({
    from: { color: "blue", o: 0, xyz: [0, 0, 0] },
    o: 1,
    xyz: [10, 20, 5],
    color: "green",
    reset: true,
    reverse: flip,
    delay: 200,
    config: config.molasses,
    onRest: () => set(!flip),
  });

  return (
    <div>
      <animated.div
        style={{
          // You can also form ranges, even chain multiple interpolations
          padding: o
            .to({ range: [0, 0.5, 1], output: [0, 0, 10] })
            .to((o) => `${o}%`),
        }}
        className="p-4"
      >
        {o.to((n) => n.toFixed(2))}
      </animated.div>
    </div>
  );
};

output就很好理解了,就是到达分界点的时候的to值应该是多少。

如何对auto进行动画

react-string 不能对auto进行动画,不过别灰心,我们可以迂回一下,比如借助 react-use-measure 来获取元素的宽高,然后对其进行动画。

const Auto = () => {
  const [flip, set] = useState(false);
  const [ref, bounds] = useMeasure();

  const { height } = useSpring({
    from: { height: 0 },
    height: bounds.height,
    reset: true,
    reverse: flip,
    delay: 200,
    config: config.molasses,
    onRest: () => set(!flip),
  });

  return (
    <animated.div
      className="bg-lime-500 "
      style={{ overflow: "hidden", height }}
    >
      <div ref={ref} className="p-4" style={{ overflow: "hidden" }}>
        豇豆角搜集
        <br />
        掉绝对是
        <br />
        d的简欧is解耦司机端
        <br />
        d的简欧is解耦司机端222
      </div>
    </animated.div>
  );
};

注意ref的位置必须在animated.div的下一个层级,如果放在animated.div上,height是一直变化的,这就没法玩了。

交互与动画

如何将交互和动画结合起来呢?react-with-gesture应该是最好的选择了。

import { useGesture } from "react-with-gesture";

const Gesture = (props) => {
  const [{ xy, scale }, set] = useSpring(() => ({ xy: [0, 0], scale: 1 }));
  const bind = useGesture(({ down, delta }) => {
    set({
      xy: down ? delta : [0, 0],
      scale: down ? 2 : 1,
    });
  });
  return (
    <animated.div
      {...bind()}
      style={{
        transform: to(
          [xy, scale],
          (xy, s) => `translate3d(${xy[0]}px, ${xy[1]}px, 0) scale(${s})`
        ),
      }}
      className="bg-blue-500 w-10 h-10"
    ></animated.div>
  );
};

注意to的用法,是直接将scalexy的值传入,而不是用scale.to(...)xy.to(...),因为当transform有多个值的时候采用后者是做不到的。

参考

文章来自个人专栏
前端开发-react
1 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0