高级概念
共享元素过渡
共享元素过渡允许您自动动画从一个页面到下一个页面的共享元素,从而提供流畅的导航体验。
API 可用性
共享元素过渡 API 在 @nativescript/core
版本 8.5.0
或更高版本中可用。
当您寻找提升应用程序用户体验的方法时,共享元素过渡可以帮助在整个应用程序中创建关联的视觉移动。它们甚至可以用来在两个组件之间创建变形效果。
本质上,您可以在不同页面上的组件上声明 sharedTransitionTag
属性,并传入自定义 SharedTransition
来创建引人入胜的视觉效果。
共享元素过渡类型 | iOS | Android |
---|---|---|
页面导航 | ✅ | ✅ |
模态导航 | ✅ | 不可用 |
使用共享元素过渡
使用共享元素过渡需要在过渡的两个“端点”上对视图进行标记,并使用 SharedTransition
类对其进行配置。
标记要共享的视图
您可以使用包含 **唯一值** 的 sharedTransitionTag
属性标记任何页面上的任何 View 组件。
页面 A
在您 **即将离开** 的页面上标记一个视图
<!-- Page A content -->
<Image sharedTransitionTag="hero" />
页面 B
在您 **即将到达** 的页面上标记一个视图,使用相同的匹配 sharedTransitionTag
。当过渡开始时,该视图将从在 **页面 A** 上呈现的起始状态过渡到在 **页面 B** 上呈现的目标状态。
<!-- Page B content -->
<Image sharedTransitionTag="hero" />
使用默认的 SharedTransition
您可以使用 SharedTransition.custom()
API 以多种方式自定义过渡。
例如,使用默认值设置共享元素页面过渡
import { SharedTransition, PageTransition } from '@nativescript/core'
page.frame.navigate({
moduleName: `views/detail`,
transition: SharedTransition.custom(new PageTransition()),
})
或者在打开模态时(*仅限 iOS*)
import { SharedTransition, ModalTransition } from '@nativescript/core'
page.showModal('views/modal', {
transition: SharedTransition.custom(new ModalTransition()),
closeCallback(args) {
// ... modal closed
},
})
独立共享元素
在某些情况下,您可能在两个“端点”上都没有元素,或者需要在过渡期间动画其他元素。这就是“独立”共享元素解决的问题。
考虑以下示例
SharedTransition.custom(new PageTransition(), {
pageEnd: {
opacity: 1,
sharedTransitionTags: {
spaceman: {
opacity: 0,
y: 20,
scale: {
x: 6,
y: 6,
},
},
title: {
opacity: 0,
x: -200,
},
},
},
// ...
})
标记为 spaceman
的视图将在 x 和 y 轴上分别向下移动 20dips 并向上缩放 6 倍。
标记为 title
的视图将淡出并向左移动 200dips。
注意
- 独立共享元素目前仅在 iOS 上受支持。
- 不建议将“独立”元素与交互式过渡一起使用(并且很可能会导致意外行为)。
共享元素过渡的工作原理
为了提供开箱即用的灵活页面和模态过渡处理,@nativescript/core
提供了 PageTransition
和 ModalTransition
,它们预先配置了适用于大多数流行用例,但 SharedTransition
包含许多高级选项,可以自定义默认行为。
共享元素过渡会进行以下步骤
- 查找起始页和结束页之间具有匹配
sharedTransitionTag
值的视图。 - 当过渡开始时,在两个页面上都找到的标记有
sharedTransitionTag
的视图将在其起始状态和结束状态之间进行动画。 - 同时,传入页面将从配置中提供的
pageStart
状态动画到pageEnd
状态。 - 在返回导航中,传出页面将从其当前状态动画到通过
pageReturn
提供的状态。
sharedTransitionTag
值可以动态绑定,以实现更多独特的功能。
sharedTransitionIgnore
可用于选择性地将视图包含或排除在共享过渡中。
属性
sharedTransitionTag
<View sharedTransitionTag="hero" />
用于标识有资格进行共享元素过渡的视图的字符串值。
重要!
所有标签必须在每个给定页面上都是唯一的。重复的值可能会导致意外行为。
注意:
- 在声明带有
sharedTransitionTag
的图像时,请确保在图像上声明了有效的尺寸(宽度/高度)。 - 虽然可以标记
Label
组件,但不建议这样做,因为文本大小和样式在两种状态之间可能会有所不同。
sharedTransitionIgnore
<View sharedTransitionIgnore="{{ someCondition }}" />
一个布尔值,指示在共享元素过渡期间是否应该忽略一个视图。
注意
此属性最常与动态绑定一起使用,否则它与一开始不标记视图没有什么区别。
API
SharedTransition
一个类,它公开各种 static
方法,用于配置共享元素过渡。
custom
SharedTransition.custom(
transition: Transition,
options?: SharedTransitionConfig
)
设置共享元素过渡的主要 API。返回的对象可以直接作为 transition
传递给核心页面和模态导航 API。
第一个参数通常是 PageTransition
或 ModalTransition
,具体取决于导航类型。第二个可选参数允许配置过渡的各个方面。该函数返回配置的过渡实例以用于视觉过渡,并为内部状态跟踪进行设置。
interface SharedTransitionConfig {
/**
* State applied to the incoming page transition on start
*/
pageStart?: SharedTransitionPageProperties
/**
* State that the incoming page transitions to.
*/
pageEnd?: SharedTransitionPageProperties
/**
* State that the outgoing page transitions to.
* (from it's current state)
*/
pageReturn?: SharedTransitionPageProperties & {
/**
* In some cases you may want the returning animation to start with the original opacity,
* instead of beginning where it ended up on pageEnd.
* Note: you can try enabling this property in cases where your return animation doesn't appear correct.
*/
useStartOpacity?: boolean;
};
/** @ios - only supported on iOS */
interactive?: {
dismiss?: {
/**
* A threshold (percentage) that if exceeded by the pan gesture
* will finish the transition once the touch is released.
*
* Default: 0.5
*/
finishThreshold?: number
/**
* You can create your own percent formula used for determing
* the interactive value. By default, we handle this with the
* following formula:
*
* eventData.deltaX / (eventData.ios.view.bounds.size.width / 2)
*
* @param eventData PanGestureEventData
* @returns The percentage value to be used as the finish/cancel threshold
*/
percentFormula?: (eventData: PanGestureEventData) => number
}
}
}
interface SharedProperties {
x?: number
y?: number
width?: number
height?: number
opacity?: number
scale?: {
x?: number
y?: number
}
}
interface SharedTransitionTagProperties = SharedProperties & {
/**
* (iOS only) The visual stacking order where 0 is at the bottom.
* Shared elements are stacked one on top of the other during each transition.
* By default they are not ordered in any particular fashion.
*/
zIndex?: number;
/**
* (iOS only) Collection of properties to match and animate on each shared element.
*
* Defaults to: {
* view: ['backgroundColor'],
* layer: ['cornerRadius', 'borderWidth', 'borderColor']
* }
*
* Tip: Using an empty array, [], for view or layer will avoid copying any properties if desired.
*/
propertiesToMatch?: {
/**
* View related properties
*/
view?: Array<string>;
/**
* specific CALayer related properties
*/
layer?: Array<string>;
};
/**
* (iOS only) Ability to modify other visuals while handling the transition.
* Callback will be fired before the shared element is positioned and added to the transition.
* For example: scroll a CollectionView based on the element's position
*/
callback?: (view: View, action: SharedTransitionEventAction) => Promise<void>;
};
type SharedTransitionPageProperties = SharedProperties & {
/**
* @ios - only supported on iOS
* Allow "independent" elements found only on one of the
* pages to take part in the animation.
*/
sharedTransitionTags?: Record<string, SharedTransitionTagProperties>
/**
* Spring animation settings.
* Defaults:
* tension: 140
* friction: 10
*/
spring?: {
tension?: number
friction?: number
mass?: number
delay?: number
velocity?: number
/** @ios - UIViewAnimationOptions */
animateOptions?: any
}
}
事件
SharedTransition.events(): Observable
公开一个 Ovservable,用于监听各种共享元素过渡事件。
SharedTransition.startedEvent
当过渡开始时发出。
SharedTransition.finishedEvent
当过渡结束时发出。
SharedTransition.interactiveCancelledEvent
当交互式过渡取消时发出。
SharedTransition.interactiveUpdateEvent
当交互式过渡使用百分比值更新时发出。
interface SharedTransitionEventData {
id: number // transition instance id
type: 'page' | 'modal'
action?: 'present' | 'dismiss' | 'interactiveStart' | 'interactiveFinish'
percent?: number
}
例如
SharedTransition.events().on(
SharedTransition.finishedEvent,
(data: SharedTransitionEventData) => {
//
}
)
getSharedElements
SharedTransition.getSharedElements(fromPage: View, toPage: View): {
sharedElements: Array<View>
presented: Array<View>
presenting: Array<View>
}
主要由 PageTransition
和 ModalTransition
在内部使用。
也可以用作实用程序来获取两个视图之间的共享元素,以及获取所有声明的 sharedTransitionTag
元素列表(*即使在两个视图之间没有共享时也是如此*)。
getState
SharedTransition.getState(id: number): SharedTransitionState
通过 id 获取过渡的当前状态。
interface SharedTransitionState extends SharedTransitionConfig {
/**
* @internal the preconfigured transition instance
*/
instance?: Transition
/**
* the Page which will start the transition.
*/
page?: ViewBase
activeType?: SharedTransitionAnimationType
toPage?: ViewBase
/**
* Whether interactive transition has began.
*/
interactiveBegan?: boolean
/**
* Whether interactive transition was cancelled.
*/
interactiveCancelled?: boolean
}
enum SharedTransitionAnimationType {
present,
dismiss,
}
updateState
SharedTransition.updateState(id: number, state: SharedTransitionState): void
在内部使用,以在过渡进行时更新状态。如果出于任何原因您需要更新内部状态,请提供此方法。
finishState
SharedTransition.finishState(id: number): void
在内部使用,以在过渡完成后完成状态。如果出于任何原因您需要提前完成状态,请提供此方法。
疑难解答
- 在两个不同页面之间意外提供不匹配的
sharedTransitionTag
值很常见。在遇到共享元素过渡问题时,请始终检查匹配的标签。 - 尝试避免在 Label 上使用
sharedTransitionTag
,因为它们通常不会表现出预期的行为。
致谢
共享元素过渡的 API 灵感来自 React Native Reanimated。