Skip to content

缩放和共享元素 ​

点击一张卡片,让页面从其中延伸出来。在Back上,它缩小为相同的元素。这是 UIKit 的原生缩放,当运行时公开 UIViewControllerTransition 时,可在 iOS 18+ 上使用。 Android 和没有该 API 的运行时使用正常转换或显式回退。

卡以 UIKit 缩放打开消息并接收返回转换。 iOS 26模拟器。

另存为Inbox.vue。 RouterLink 的单个子元素是源元素;不需要 ref 或 JavaScript 动画。

vue
<script setup lang="ts">
import { RouterLink } from 'vue-router-ns'
</script>

<template>
  <Page>
    <ActionBar title="Inbox" />
    <StackLayout padding="24">
      <RouterLink :to="{ name: 'message', params: { id: 1 } }" zoom>
        <StackLayout padding="24" borderRadius="16" backgroundColor="#dbeafe">
          <Label text="Ana" color="#1e3a8a" fontSize="24" />
          <Label text="Lunch on Friday?" color="#1e3a8a" textWrap="true" />
        </StackLayout>
      </RouterLink>
    </StackLayout>
  </Page>
</template>

目的地和设置 ​

将目的地保存为 Message.vue:

vue
<script setup lang="ts">
const props = defineProps<{ id: string }>()
const subjects: Record<string, string> = {
  '1': 'Lunch on Friday?',
  '2': 'Release notes',
}
</script>

<template>
  <Page>
    <ActionBar title="Message" />
    <StackLayout padding="24">
      <Label
        :text="subjects[props.id] ?? 'Message'"
        :sharedTransitionTag="`message-${props.id}`"
        fontSize="28"
        textWrap="true"
      />
      <Label
        text="This is a native Page. Use Back to return to the inbox."
        textWrap="true"
      />
    </StackLayout>
  </Page>
</template>

注册两个页面并在应用程序中安装路由器,如安装指南中所示:

ts
import { createRouter } from 'vue-router-ns'

export const router = createRouter({
  routes: [
    { path: '/', component: () => import('./Inbox.vue') },
    {
      path: '/messages/:id',
      name: 'message',
      component: () => import('./Message.vue'),
      props: true,
    },
  ],
})

在应用程序根目录中使用 <RouterView />。首先尝试单个 导航栈:展开的拆分中的第一个详细信息出现在另一个窗格中,而不从列表中缩放。

包含 zoomFrom 的列表行 ​

将 Inbox.vue 替换为该版本,以使用 itemTap 提供的视图。上面的目标还具有用作后备的共享转换所需的标记。

vue
<script setup lang="ts">
import { PageTransition, SharedTransition, type ItemEventData } from '@nativescript/core'
import { useRouter } from 'vue-router-ns'

const router = useRouter()
const messages = [
  { id: 1, subject: 'Lunch on Friday?' },
  { id: 2, subject: 'Release notes' },
]
function open({ index, view }: ItemEventData) {
  const message = messages[index]
  if (!message) return
  router.push(
    { name: 'message', params: { id: message.id } },
    {
      zoomFrom: view,
      transition: SharedTransition.custom(new PageTransition()),
    },
  )
}
</script>

<template>
  <Page>
    <ActionBar title="Inbox" />
    <ListView :items="messages" @itemTap="open">
      <template #default="{ item }">
        <Label
          :text="item.subject"
          :sharedTransitionTag="`message-${item.id}`"
          padding="24"
        />
      </template>
    </ListView>
  </Page>
</template>

寻找什么 ​

  • 点击一行:消息应在同一个 导航栈 中打开。
  • 当缩放可用时,页面从行开始增长。 Back 应该返回源。
  • 启用减少运动后,路由器请求无动画转换。
  • 在Android上,具有相同sharedTransitionTag的标签参与共享转换。
  • 正常的横向动画不演示缩放。检查运行时 API 可用性并避免在此框架上出现 Frame.defaultTransition。

demo Inbox 使用带有后备功能的 zoomFrom。要重现视频,请从演示中的卡打开“设置”→“缩放”。本页面使用单个导航栈; 参见其来源。

转换参考 ​

transition 采用 NativeScript 的 SharedTransition,它将在两个平台上共享 sharedTransitionTag 的视图从一个页面更改为下一个页面:

vue
<!-- in the list -->
<Image :src="photo.thumb" :sharedTransitionTag="`photo-${photo.id}`" />

<!-- in the photo page -->
<Image :src="photo.full" :sharedTransitionTag="`photo-${photo.id}`" />
ts
import { PageTransition, SharedTransition } from '@nativescript/core'

router.push(`/photos/${photo.id}`, {
  transition: SharedTransition.custom(new PageTransition()),
})

返回倒转播放。它的交互式解雇(仅限 interactive: { dismiss }、iOS)是一种原生弹出,就像向后滑动一样:路由器跟随它,但离开 导航守卫 无法阻止它。

在 iOS 18 及更高版本上,zoomFrom 将动画交给 UIKit 的缩放:页面从视图中拉出,并在关闭时通过后退按钮、边缘滑动或向下拖动缩小回视图中。当离开 导航守卫 处于活动状态时,拖动会被保持,就像向后滑动一样。

ts
function open({ index, view }: ItemEventData) {
  router.push(`/photos/${photos[index].id}`, { zoomFrom: view })
}

zoomFrom 采用视图、模板 ref 的元素或组件,并且 <RouterLink zoom> 缩小其自己的子级。如果页面关闭时视图已消失,则页面将关闭而不缩放。在不适用缩放的情况下(18 之前的 Android、iOS、animated: false),页面将获得通常的过渡,因此传递这两个过渡会为每个平台提供自己的过渡:

ts
router.push(to, {
  zoomFrom: view, // iOS 18
  transition: SharedTransition.custom(new PageTransition()), // everywhere else
})

帧 (Frame.defaultTransition) 上设置的默认过渡将从 UIKit 取回动画,因此在缩放位置不要设置它。

在设计自定义手势适配器之前,请参阅 交互式转换所有权和限制。

基于 MIT 许可证发布。