大家好!今天我们要深入探讨一下我在 NativeScript 中最喜欢的功能之一------共享元素转场(Shared Element Transitions)。如果你曾想过要创建那种丝滑、精致的动画效果,让一个元素看起来从一个屏幕流畅地"流动"到另一个屏幕,那么你就来对地方了。
什么是共享元素转场?
你可以将共享元素转场理解为一种在不同屏幕之间保持视觉连续性的方法。它不是让元素简单地出现或消失,而是让它们从一个屏幕上的位置平滑地动画过渡到下一个屏幕上的新位置。
视频中的这个图书应用就是一个绝佳的例子------当你点击列表中的书籍封面时,这张封面图片会非常平滑地缩放和移动到详情页中的更大尺寸的相应位置上。正是这些精妙的小细节,才使得应用程序显得高端且精致。
本项目的完整源代码可以在以下地址找到:github.com/NewbieScrip...
准备
在开始之前,确保你已经准备好了以下环境:
- NativeScript-Vue 项目(版本3以上)
@nativescript/core版本8.5.0或更高(这里是实现共享转场动画的关键)- 已安装 NativeScript CLI (可以通过
npm install -g nativescript安装)
开始编程~
准备好让应用运行了吗?执行下列命令之一:
bash
# 对于 iOS
ns run ios
# 对于 Android
ns run android
步骤1:设置我们的书籍数据
首先,让我们为我们的书籍创建一些示例数据。创建一个新的文件 src/data/books.ts:
typescript
export interface Book {
id: number;
title: string;
author: string;
description: string;
coverImage: string;
}
export const books: Book[] = [
// 示例书籍数据...
];
这里定义了书籍的数据结构以及几个书籍实例。
步骤2:基本样式设计
让我们添加一些基本的样式,使我们的应用看起来不错。更新你的 src/app.css 文件:
css
ActionBar {
background-color: #65adf1;
color: white;
}
步骤3:创建书籍列表屏幕
现在,让我们构建显示所有书籍的主要屏幕。创建一个新组件 src/components/Home.vue:
html
<script lang="ts" setup>
// ... imports and navigation function ...
</script>
<template>
<Frame>
<Page>
<ActionBar>
<Label text="Book Library" class="font-bold text-lg" />
</ActionBar>
<ScrollView>
<StackLayout class="p-4">
<GridLayout
v-for="book in books"
:key="book.id"
rows="auto"
columns="auto, *"
class="p-2 mb-4 bg-white rounded-lg border-b"
@tap="navigateToBookDetail(book)"
>
<!-- Book cover with shared transition tag -->
<Image
:src="book.coverImage"
:sharedTransitionTag="`cover-image-${book.id}`"
class="w-16 h-24 rounded-lg mr-4"
stretch="aspectFill"
/>
<StackLayout col="1" class="justify-center">
<Label :text="book.title" class="text-lg font-bold" />
<Label :text="book.author" class="text-gray-600" />
</StackLayout>
</GridLayout>
</StackLayout>
</ScrollView>
</Page>
</Frame>
</template>
在这个组件中,我们通过 <GridLayout> 循环遍历书籍数组,并使用 <Image> 标签展示每本书的封面。关键在于每个图像元素上都设置了 sharedTransitionTag 属性,这使得当点击某本书时,NativeScript 能够识别要动画过渡的具体元素。比如,对于第 i 本书,其标签是 cover-image-${i},这里的 ${i} 是书的 id,确保每个标签都是唯一的。
这样,我们就完成了基础的书籍列表页面的创建,包括如何展示书籍封面和作者等信息,同时也为实现共享元素转场做好了准备。接下来,你可以继续完成详情页的开发,以实现完整的转场效果。
步骤4:实现页面跳转
我们需要在 Home.vue 的脚本部分添加导航逻辑:
typescript
import { SharedTransition, PageTransition } from "@nativescript/core";
function navigateToBookDetail(book: Book) {
$navigateTo(BookDetail, {
props: {
book: book,
},
transition: SharedTransition.custom(new PageTransition(500)),
});
}
这就是魔法发生的地方!我们告诉 NativeScript 在跳转到详情页时使用共享转场。SharedTransition.custom(new PageTransition(500)) 这一部分启用了持续时间为500毫秒的平滑动画。请注意,duration 参数是可选的,你可以省略它来使用平台默认值。有趣的是,Android的默认持续时间已经是500毫秒,而iOS的默认值是CORE_ANIMATION_DEFAULTS.duration,即350毫秒。所以指定500毫秒可以确保转场在两个平台上具有相同的持续时间!
步骤5:构建详情页面
为我们书籍的详细信息创建一个新组件 src/components/BookDetail.vue:
html
<script lang="ts" setup>
// ... imports and goBack function ...
</script>
<template>
<Page actionBarHidden="true">
<GridLayout rows="auto, *" class="bg-gray-100">
<!-- Back button -->
<Label
text="← Back"
@tap="goBack"
class="text-lg p-4 font-bold text-blue-500"
/>
<!-- Book detail card -->
<ScrollView row="1">
<StackLayout class="p-4">
<GridLayout rows="auto, auto, auto" class="bg-white rounded-2xl p-6">
<!-- Book cover with shared transition tag -->
<Image
:src="book.coverImage"
:sharedTransitionTag="`cover-image-${book.id}`"
class="w-48 rounded-xl mb-6 self-center"
stretch="aspectFill"
/>
<StackLayout row="1" class="mb-6">
<Label
:text="book.title"
class="text-2xl font-bold text-center mb-2"
/>
<Label
:text="`by ${book.author}`"
class="text-gray-600 text-center text-lg"
/>
</StackLayout>
<StackLayout row="2">
<Label
:text="book.description"
class="text-gray-700"
textWrap="true"
/>
</StackLayout>
</GridLayout>
</StackLayout>
</ScrollView>
</GridLayout>
</Page>
</template>
在这个模板中,我们同样给 <Image> 组件设置了 sharedTransitionTag 属性,注意我们在这里使用了完全相同的标签值 (cover-image-${book.id})。这一点至关重要,它告诉 NativeScript 哪些元素应该在转场期间一起进行动画。
步骤6:添加返回导航
别忘了在 BookDetail.vue 的脚本部分添加返回导航功能:
typescript
import { SharedTransition, PageTransition } from "@nativescript/core";
function goBack() {
$navigateBack({
transition: SharedTransition.custom(new PageTransition()),
});
}
工作原理
当你点击一本书时,会发生以下过程:
- NativeScript 发现具有匹配
sharedTransitionTag值的<Image>元素。 - 它会计算该元素在两个屏幕上的位置、大小和其他属性。
- 它创建一个平滑的动画,将该元素从列表视图中的位置转换到详情视图中的位置。
- 当你返回时,同样的过程会反向执行。
值得一提的是,与原生实现相比,NativeScript 实现共享元素转场要容易得多。在 Android 中你需要编写复杂的转场代码(例如 SharedElementCallback)并处理多种边界情况。在 iOS 中你需要与 UINavigationControllerDelegate 配合,并创建自定义的转场动画。而在 NativeScript 中,只需添加一个 sharedTransitionTag 属性并使用 SharedTransition.custom() 即可,框架会为你处理所有复杂的动画逻辑!
好的,这是最后一段内容的中文翻译:
需要牢记的重要事项
- 标签唯一性 :在一个页面内,每一个共享转场标签(shared transition tag)都必须是唯一的。这就是为什么我们使用
cover-image-${book.id}而不是简单的cover-image。 - 标签需匹配:想要产生转场效果的元素,在两个页面上必须使用完全相同的标签。
- 图片尺寸:请确保你的图片有有效的尺寸,以获得最佳效果。
- 支持的元素 :共享转场最适合用于
<Image>和其他视觉组件(应避免在<Label>组件上使用)。
NativeScript 实现方式的优点在于,它能自动处理所有复杂的坐标计算和动画同步。在原生开发中,你需要手动协调两个视图之间的位置和变换,但 NativeScript 将所有这些复杂性为你抽象掉了。
故障排除小贴士
如果你的转场效果没有按预期工作:
- 确保两个元素拥有完全相同的
sharedTransitionTag值(例如,第一本书都应该是 "cover-image-1")。 - 确保你在页面跳转时使用了
SharedTransition.custom(new PageTransition())。 - 检查你的
@nativescript/core版本是否为 8.5.0 或更高。 - 确认两个元素都是可见的,并且具有有效的尺寸。
- 确认每个页面内的每个标签都是唯一的。
总结
共享元素转场是一个能够真正提升你应用用户体验的功能。只需几行代码,你就可以创建出平滑、吸引人的动画,让你的应用感觉精致且专业。
其中的要点是:使用 sharedTransitionTag 属性来标记那些需要一起进行动画的元素,并在页面间跳转时使用 SharedTransition.custom(new PageTransition())。
你可以在 NativeScript Snacks 上找到更多使用示例,那里有很多关于 SharedTransition 的代码片段供你探索。试着在你自己的项目中实现它,看看它如何改变你应用的感觉。一旦你开始使用共享转场,你就会疑惑以前没有它时是怎么过来的!