本文翻译自 FLIP animation in react
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a4YHrSjn-1604052655260)(https://github.com/aholachek/react-flip-toolkit/raw/master/example-assets/listanimations.gif)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-21L53RZZ-1604052655262)(https://github.com/aholachek/react-flip-toolkit/raw/master/example-assets/list-transition.gif)]
背景
动画将用户界面带入生活。有意设计动画也有助于可用性、减少了用户的认知负荷,并引导用户关注重要的东西。
问题与解决方案
你可能已经尝试过了改变元素的 height、width、top、left 或者除也 transform 和 opacity 之外的其他属性来实现一个动画效果。或许你已经在以往的经历(做的动效)中发现自己的实现的动效有点粗糙甚至卡顿,其实这是有一定原因的。任何触发布局变化的属性(比如 height),浏览器都会递归检查布局中的其他元素是否也因此改变,这一个过程花销是很贵的,如果这个计算比一个动画帧(16.7ms)更长,那么动画就会丢帧。
我们需要在更改 DOM 元素位置和尺寸后浏览器渲染预前计算绘制需要发生的转换,然后让它平稳运行。为了实现上述过渡,我们需要通过boundingClintRect
获取到元素的初始和最终状态,并使用 CSS transform
让过渡
步骤
对上述过程进行简短说明:
first and Last
Fist是我们转换之前的元素,Last是我们转换之后想要得到的。
我们需要存储元素的getboundingClientRect
并调整大小并其重新放置到目标位置。
Invert
这个阶段在我们需要在浏览器布局之后,浏览器绘制之前。我们可以根据getboundingClientRect
计算出差值。
Play
最后,我们让元素移动到 0,0
位置即可。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hqxqBt6E-1604052655268)(https://makersden.io/d6ac541c409723ebb01771cd8ca95d57/play.gif)]
此时我们可以采用如下技术实现: - 使用 CSS transition - 使用 CSS keyframes + animation - 使用 Web Animation API - 使用 Anime.js
代码实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z7C04Z8W-1604052655269)(https://makersden.io/c02746907fe0da4cdb1836084899fbaf/nolib.gif)]
整体代码如下:
import React, { useLayoutEffect, useRef, useState } from 'react';
interface ViewProps {
items: any[];
}
function List({ items }: ViewProps) {
const listRef = React.createRef<HTMLUListElement>();
useFlip({
root: listRef,
invert,
play,
});
return (
<ul ref={listRef}>
{items.map((item) => (
<li data-key={item} key={item}>
{item}
</li>
))}
</ul>
);
}
interface FlipProps {
root: React.RefObject<HTMLUListElement>;
invert: any;
play: any;
}
interface Rect {
top: number;
left: