v3.9.0 中新增
Flip
快速入门
CDN 链接
gsap.registerPlugin(Flip)
最简用法
// grab state
const state = Flip.getState(squares);
// Make DOM or styling changes
switchItUp();
// Animate from the initial state to the end state
Flip.from(state, {duration: 2, ease: "power1.inOut"});
Flip 涉及一种完全不同的动画思维方式。一旦掌握,你会感到惊叹。
Flip 插件让你可以无缝过渡两个状态,即使 DOM 结构发生了巨大变化,通常会导致元素跳跃,Flip 也能处理。
Flip 会记录你元素的当前位置、尺寸和旋转,你可以进行任意更改,然后 Flip 应用偏移量让它们看起来好像从未移动过……最后 Flip 会动画式地移除这些偏移!用户界面过渡变得非常简单。所有繁重的工作都由 Flip 处理。移除这些偏移!UI 转场变得异常易于编写。Flip 处理了所有繁重的工作。
人们的评价:
更深入地使用了 GSAP 的 Flip 插件,我震惊于它能如此显著加快开发速度!这才是真正的强力工具!💪"- Manoela Ilic, Codrops
“它是怎么知道该做什么的? 🤯"- @georgedoescode
“我真正被 GSAP 和 FLIP 的强大所折服。”- @Alex.Marti
“本周让我惊艳的就是 Flip 插件了。简直是天才之作。太聪明了!”- @PeHaa
视频
当然,就像所有的 GreenSock 工具一样,它拥有丰富的 API,用于精确控制效果并实现你想要的外观。
"FLIP“ 是一种动画技术,代表 "First"(第一阶段),"Last"(最后阶段),"Invert"(反转),"Play"(播放) ,这个名称是由Paul Lewis提出的。下面是一个演示来展示其工作原理:
加载中...
特性
- 嵌套变换?没问题!大多数 FLIP 库仅计算基础偏移,假设没有超出 x/y 的变换,因此缩放的父元素会使问题加剧。旋转就更不允许了。GSAP 的 Flip 插件就是正常工作!
- 设置
absolute: true
设置为让元素在翻转期间使用position: absolute
在 flip 期间。这解决了 flexbox、grid 等布局难题。你甚至可以定义目标子集,例如:absolute: ".box"
- 一次 flip 可以处理多个元素甚至支持交错动画。
- 通过 width/height 属性调整大小(默认)或 scaleX/scaleY (
scale: true
) - 你将获得背后完整的 GSAP 功能,所以你可以使用任何
ease
定义如onComplete
,onUpdate
,repeat
,yoyo
等特殊属性,add()
以及对时间等其他动画进行完全控制。
阅读更多...
- 使用
toggleClass
在翻转期间应用一个 CSS 类。它会在翻转结束时被移除。 - Flip.fit()将某个元素重新定位/调整大小以完美贴合另一个元素(甚至是同一元素之前的状态).
- 补偿嵌套偏移如果一个容器元素与其某些子元素一同执行 flip,请设置
nested: true
以防止偏移累加。 - 平滑处理中断.
- 将一个元素翻转到另一个元素;甚至可以交叉淡入淡出(
fade: true
)。只要为它们指定相同的data-flip-id
属性使它们相关联。 onEnter
和onLeave
回调函数用于元素进入或离开时(比如当 flip 检测到切换但没有对应的交换目标时),可以轻松优雅地动画显示或隐藏元素。display: none
toggle and there's no matching target to swap), making it easy to elegantly animate on/off elements.- 批量处理多个 Flip 动画互不干扰,比如在需要协作工作的多个独立 React 组件中。
Flip 是如何工作的?
“FLIP” 动画通常包括以下三个步骤:
-
获取当前状态
// returns a state object containing data about the elements' current position/size/rotation in the viewport
const state = Flip.getState(".targets");这只是捕获有关当前状态的一些数据。可以使用选择器字符串、Element、元素数组或 NodeList。Flip.getState()不会修改任何内容(除非有活跃的 flip 动画影响其中任何一个目标,在这种情况下它会强制完成该动画以便准确捕捉最终状态)。默认情况下,Flip 只关注位置、尺寸、旋转和倾斜。如果你想让 Flip 动画影响其他 CSS 属性,你可以定义一个配置对象,并列出逗号分隔的
props
列表,例如:// record some extra properties (optional)
const state = Flip.getState(".targets", { props: "backgroundColor,color" }); -
做出你的状态更改
执行 DOM 编辑、样式更新、添加/删除类,或任何必要的操作以达到最终状态。无需通过插件来做这件事(除非你正在批量处理)。例如,我们可以切换一个类:
// make state changes. We'll toggle a class, for example:
element.classList.toggle("full-screen"); -
调用
Flip.from(state, options)
Flip 会查看
state
对象,比较记录的位置/尺寸和当前的位置/尺寸,立即重新定位/调整它们大小以看起来像是在上一状态中的样子,然后动画式地移除移除这些偏移。你可以指定几乎任何标准 tween 的特殊属性,例如duration
,ease
,onComplete
,等等。Flip.from()返回一个时间轴,你可以向其添加add()
内容或以任何方式控制它:// animate from the previous state to the current one:
Flip.from(state, {
duration: 1,
ease: "power1.inOut",
absolute: true,
onComplete: myFunc,
});就是这样!
简单示例
加载中...
弹性布局(Flex)示例
加载中...
高级示例
加载中...
配置对象
这个Flip.from()参数对象(第二个参数)可以包含以下任选属性另外也可以包含任何标准 tween 属性,例如duration
, ease
, onComplete
等。详细说明见此处:
- Boolean | String | Array | NodeList | Element- 指定哪些目标应在 FLIP 动画过程中应用
position: absolute
。如果true
, 所有所有目标都被影响,或者使用类似".box"
的选择器文本(或元素数组/NodeList,甚至单个元素)来指定目标的一个子集。例如,这可以解决 flex 和 grid 布局中的布局挑战。如果表现不符预期,尝试设置absolute: true
。请注意,position: absolute
它会使元素脱离文档流,导致下方内容塌陷。在这种情况下,只需指定一个不包含容器元素的子集,让它保持布局空间。(添加于 3.9.0) - Boolean- 如果设置为
true
,任何“离开”的元素(传递给onLeave()
的那些)将在翻转动画期间被设置为position: absolute
。当你将元素设置为display: none
来在最终状态中隐藏它们,但你希望它们可以淡出、缩放等时,这会非常有用。关键是这些元素不能影响布局,但在动画期间仍然可见。(添加于 3.9.0) - Boolean- 默认情况下,如果与某个特定
data-flip-id
在前一个状态相关联的目标元素是不同的元素而不是在最终状态中具有相同data-flip-id
的那个元素,则它将立即被交换;但如果你更希望它们交叉淡入淡出,请设置fade: true
。同样,这只适用于元素交换的情况。如果“交换出去”(离开)的元素是display: none
(CSS),显然它不会对淡出可见,但如果你将 Flip 设置为absolute: true
,它会强制该元素恢复到之前的显示状态在翻转过程中以便它可以交叉淡出。在这种情况下需要absolute: true
的原因是,否则该元素会影响文档流并打乱其他元素的位置,但如果它是position: absolute
(CSS),则它会从文档流中移除,不会干扰定位。 - Boolean- 如果 Flip 有嵌套的目标(比如父元素和子元素都在
targets
中),请设置nested: true
以让 Flip 执行额外计算来防止这些移动叠加。父级的移动会影响其子元素,因此如果两者都被映射到最终位置距离初始位置 200px,并且 Flip 让它们都移动了 200px,则子元素最终会移动 400px,除非设置了nested: true
。 - 函数- 如果某个目标在原始
state
中找不到,或者它在原始状态中不在文档流中(比如display: none
),但它确实存在于最终状态的IS文档流中,会调用此回调函数。由于在原始状态中没有可比较的位置/尺寸数据,它不会包含在翻转动画中,但回调函数接收一个进入元素的数组作为参数,这样你可以根据需要对它们进行动画处理(例如淡入)。此回调返回的任何动画都会添加到翻转时间轴中,以便在竞争的翻转中断时强制完成。例如:end state. Since there is no position/size data to compare to in the original state, it won't be included in the flip animation, but the callback receives an Array of the entering elements as a parameter so that you can animate them as you please (like fade them in). Any animation returned by this callback will get added to the flip timeline so that it gets forced to completion if a competing flip interrupts it. For example:onEnter: elements => gsap.fromTo(elements, {opacity: 0}, {opacity: 1})
- 函数- 如果某个目标存在于原始
state
状态中但不存在于结束状态中,或者在结束状态中不在文档流中(比如display: none
),则会调用此回调函数。由于在结束状态中没有可比较的位置/尺寸数据,它不会包含在翻转动画中,但回调函数接收一个离开元素的数组作为参数,这样你可以根据需要对它们进行动画处理(例如淡出)。重要提示:这些元素不可见,除非你也设置了absolute: true
(否则会破坏文档流)。如果设置了absolute: true
,它会强制display
恢复到之前的状态,然后在翻转结束时再恢复回来。此回调返回的任何动画都会添加到翻转时间轴中,以便在竞争的翻转中断时强制完成。例如:onLeave: elements => gsap.fromTo(elements, {opacity: 1}, {opacity: 0})
- 字符串- 一个逗号分隔的驼峰式CSS 属性列表,除了标准的定位/尺寸/旋转/倾斜属性之外,还应包括这些属性在翻转动画中。例如,
"backgroundColor,color"
。然而,这只有在state
对象(第一个参数)中存在这些属性的情况下才有效,否则就没有对应的数据可供提取。默认情况下,Flip 将使用props
在调用Flip.getState(targets, props)时捕获的状态数据,因此很少需要在props
,只在你想将其限制为状态中捕获的部分属性时才有用。Flip.from()中定义LIMIT them to a subset of the ones captured in the state. - Boolean- 如果设置为
true
,Flip 将从动画中移除与前一个状态(位置/尺寸)匹配的任何目标,以节省资源。这需要一些前期更多的处理,但在动画期间当移除多个目标时可能提高性能,此外也可以使交错动画更加直观,因为你可能不希望非动画目标影响交错计算。(添加于 3.9.0) - Boolean- 默认情况下,Flip 会通过修改
width
和height
CSS 属性来改变元素大小,但如果你想改为缩放元素(通常性能更好),请设置scale: true
. - Boolean- 如果设置为
true
,Flip 将跳过确定位置时为了支持旋转/缩放/倾斜所需的额外计算。这就像是告诉 Flip“我保证翻转元素的容器没有旋转/缩放/倾斜”这样会使处理更快faster。大多数情况下性能差异并不明显,但如果你正在翻转大量元素,这有助于保持流畅性。 - Boolean | Number | Function -
如果
true
,元素将在翻转动画期间额外旋转 360 度,从而看起来更有趣。你也可以定义数值完整旋转圈数,包括负数,因此-1
会朝相反方向旋转一圈。如果你提供一个函数,它将为每个目标调用一次,以便你可以为每个单独元素返回任意旋转值。这样你就可以让某些目标朝一个方向旋转,其他元素朝另一个方向旋转,或者返回 0 表示完全不旋转。示例代码:...Flip.from(state, {
spin: (index, target) => {
if (target.classList.contains("clockwise")) {
return 1;
} else if (target.classList.contains("counter-clockwise")) {
return -1;
} else {
return 0;
}
},
}) - String | Element | Array | NodeList- 默认情况下,Flip 将使用来自
state
对象(第一个参数)的目标,但你可以指定其中的一个子集作为选择器文本(".class, #id"
)、一个元素、一组元素或一个NodeList。如果提供的任何目标在对象中未找到,则它将被传递给state
included in the flip animation because there's no previous state from which to pull position/size data.onEnter
和有在翻转动画期间添加一个CSS类到目标上。例如 - 字符串- 在整个翻转动画过程中立即将zIndex CSS属性设置为此值,然后在结束时恢复。这使得确保您的翻转元素在动画期间位于其他元素之上变得简单,例如。
"flipping"
. - 数字- 立即将zIndex CSS属性设置为此值,并在整个翻转动画期间保持该值,最后再恢复。这样就能轻松确保翻转元素在动画期间位于其他元素之上,比如。
属性
描述
如何在两个不同的元素之间进行翻转动画?
Flip会在每个与之交互的元素上查找一个data-flip-id(通过Flip.getState()data-flip-id
属性,如果找不到,Flip会自动分配一个递增的ID(例如"auto-1"、"auto-2"等)。它允许你关联目标("from"状态中具有特定data-flip-id的目标会与end状态中具有相同data-flip-id的目标匹配)。这个data-flip-id可以是任意字符串,不仅仅是数字。Flip.getState()或者Flip.from()等等)并且如果没有找到,Flip会自动分配一个递增的ID(比如“auto-1”,“auto-2”等)。它让你可以关联目标(在“from”状态中有data-flip-id的目标会被与end状态中有相同data-flip-id的目标匹配)。data-flip-id可以是任意字符串,不只是数字。data-flip-id
id 为"5"
data-flip-iddata-flip-id
id 为"5"
设置为true时,请记住坐标将基于当前视口进行计算,因此如果在翻转过程中视口大小发生变化或用户滚动页面,可能会影响定位(但在翻转完成后且偏移量被移除之后,元素将会回到正确的位置)。换句话说,进行中的翻转并不总是响应式的。data-flip-id
data-flip-id
因此如果你想要在两个不同的目标之间翻转,请确保end状态中的data-flip-id属性与"from"状态中的一致。当Flip看到from/end状态中有两个相同值的data-flip-id时,它会自动判断哪一个正在消失(通常带有display: none),并以此为基础进行“交换”元素操作。如果你希望它们交叉淡入淡出,只需设置crossfade: true,否则它们会立即交换。通常最好设置absolute: true,以便Flip修改position值时不会影响文档流。display: none
display: nonefade: true
crossfade: trueabsolute: true
absolute: truedisplay
position
加载中...
批处理
如果你需要创建多个协调一致的Flip动画(可能在不同的React组件中)怎么办?它们都需要先调用getState(),然后再进行DOM/样式更改,因为那样做可能会改变其他元素的位置和大小。请参阅Flip.batch()的文档。多个协调一致的Flip动画(可能在不同的React组件中)?.getState() 在他们中的任何一个都对DOM/样式进行了更改,因为这样做可能会改变其他元素的位置/大小。请参阅Flip.batch()的文档。Flip.batch()了解详情。
注意事项与技巧
- Flip不支持3D变换(如rotationX、rotationY或z)。
- 强烈建议你在元素上使用will-change属性以确保准确计算宽度和高度。
box-sizing: border-box
will-change - 当
absolute: true
fixedPositioning - Set any transform-related values (x, y, scale, rotation, etc.) directly via GSAP whenever possible (instead of just in CSS classes or inline) because GSAP caches transform-related data to supercharge performance and maximize accuracy. To clear GSAP's cache on a particular element (which you'd never need to do if you're making all your changes via GSAP),
gsap.set(element, {clearProps: "transform"});
深入了解 - 使用像Vue、React或Angular这样的框架?
注意:框架通常不会立即渲染更改,因此你应该等待渲染完成后再触发Flip.from()。批处理可能是一个很好的选择,因为你可以在React中使用useLayoutEffect()来等待渲染完成。另一种方法是使用requestAnimationFrame()来等待一帧:不渲染更改立即生效,因此应在渲染发生后之前发起Flip.from(). 批处理可能是一个极好的选择,因为你可以batch.getState(true)
使用useLayoutEffectuseLayoutEffect(() => batch.run(true), [...])
的React中。另一个技巧是使用requestAnimationFrame()等待一帧:requestAnimationFrame(() => Flip.from(...));
当你Flip.getState(".your-class")
,它会在那时记录".your-class"元素的位置/尺寸数据,并记住这些特定元素及其data-flip-id属性值。然后,如果你调用Flip.from()但没有指定任何targetthose particular elements和它们的data-flip-id
data-flip-idFlip.from(yourState)
调用Flip.from()targets
targets“使用这些新目标并在状态对象中搜索匹配的ID...”。所以请确保你定义targets的方式如下:targets
像这样:
// BAD
Flip.from(state, {
duration: 1,
});
// GOOD
Flip.from(state, {
targets: ".your-class", // <-- BINGO!
duration: 1,
});
并且不要忘记你可能需要在这些元素上设置data-flip-id,以确保Flip知道哪个目标与捕获的状态中的哪一个相匹配。data-flip-id
data-flip-id
方法
Flip.batch(id:String):FlipBatch | 协调创建多个Flip动画,按正确的步骤顺序执行以避免相互干扰。 |
Flip.fit(targetToResize:String | Element, destinationTargetOrState:String | Element | FlipState, vars:Object); | 重新定位/调整一个元素的大小,使其看起来完全适配到另一个元素的区域中。使用resizeChildren特殊属性,你甚至可以让它的某个子元素用于适配计算!默认情况下,它会更改变换(x、y、rotation和skewX)以及元素的宽度和高度,但如果你设置{useScale: true},它将改用scaleX和scaleY代替宽度和高度。 |
Flip.from(状态:FlipState,参数:Object):Timeline | 立刻移动/调整目标以匹配提供的state对象,然后反向动画以移除这些偏移,最终达到当前状态。默认情况下,使用width和height属性进行调整,但你可以设置useScale: true改为使用缩放(变换)。它返回一个时间轴动画,因此你可以控制它或add()其他动画。 |
Flip.getState(targets:String | Element | Array, vars:Object):Object | 捕获有关targets当前状态的信息,以便稍后进行Flip操作。默认情况下,这些信息包括维度、旋转、倾斜、透明度以及目标在视口中的位置。可以通过配置来捕获其他属性。 |
Flip.isFlipping(目标:String | Element):Boolean | 返回 |
Flip.killFlipsOf(目标:String | Array | NodeList | Element,complete:boolean); | 立即终止与提供的任何目标相关联的Flip翻转动画。 |
Flip.makeAbsolute(目标:Element | Array | NodeList | FlipState):Array | 将所有提供的目标元素设置为 |
Flip.to(状态:FlipState,参数:Object):Timeline | 与Flip.from()相同,但方向相反,因此它会从当前状态设置为过渡到所提供的状态。 |
示例演示
查看完整系列的使用演示和我们最喜欢的社区灵感演示在 CodePen 上。
Flip 演示示例