startViewTransition的簡單示例

热饭班长發表於2024-10-29

用途

實現檢視過渡效果

示例1

實現兩張圖片之間的過渡效果
chrome-capture-2024-10-29 (1).gif

<script setup lang="ts">
import { ref } from "vue"

const visible = ref(false)

const toggle = () => {
  // 關鍵程式碼,把扭轉狀態的程式碼用startViewTransition包起來
  document.startViewTransition(() => {
    visible.value = !visible.value
  })
}
</script>

<template>
  <img v-if="!visible"
    class="small-img"
    src="https://live.staticflickr.com/65535/50187927333_12dc192ab6_b.jpg"
    @click="toggle">
  <img v-if="visible"
    class="big-img"
    src="https://live.staticflickr.com/65535/50187927333_12dc192ab6_b.jpg"
    @click="toggle">
</template>

<style>
.small-img {
  width: 500px;
}

.big-img {
  width: 1000px;
}
</style>

startViewTransition的預設效果是對新舊檢視進行fade-in和fade-out動畫過渡,也就是opacity的0和1過渡。

示例2

縮放過渡
chrome-capture-2024-10-29.gif

<script setup lang="ts">
import { ref } from "vue"

const visible = ref(false)
const toggle = () => {
  document.startViewTransition(() => {
    visible.value = !visible.value
  })
}
</script>

<template>
  <img v-if="!visible"
    class="small-img"
    src="https://live.staticflickr.com/65535/50187927333_12dc192ab6_b.jpg"
    @click="toggle">
  <img v-if="visible"
    class="big-img"
    src="https://live.staticflickr.com/65535/50187927333_12dc192ab6_b.jpg"
    @click="toggle">
</template>

<style>
.small-img {
  width: 500px;

  // 關鍵程式碼 定義過渡名稱
  view-transition-name: img;
}

.big-img {
  width: 1000px;

  // 關鍵程式碼 定義過渡名稱
  view-transition-name: img;
}
</style>

實現該效果的關鍵程式碼是.small-img和.big-img上定義的view-transition-name: img,這裡的程式碼是告訴瀏覽器,幫我對view-transition-name為img的元素進行動畫過渡。然後瀏覽器會對兩個設定為同樣view-transition-name的元素進行動畫過渡。

需要注意的是,在同一個dom樹中,不能同時存在兩個view-transition-name相同的元素,所以你會看到上面的示例程式碼中使用visible進行了判斷。

示例3

路由過渡
chrome-capture-2024-10-30 (1).gif

路由配置如下:

const routes = [
  {
    path: "/before",
    component: before,
  },
  {
    path: "/after",
    component: after,
  },
]

/before路由內容

<script setup lang="ts">
import { useRouter } from "vue-router"

const router = useRouter()

const handleClick = () => {
  document.startViewTransition(() => {
    router.push('/photos/1')
  })
}
</script>

<template>
  <img
    class="photo"
    src="https://live.staticflickr.com/65535/50187927333_12dc192ab6_b.jpg"
    alt="photo"
    @click="handleClick"
  />
</template>

<style scoped>
.photo {
  width: 400px;
  // 關鍵程式碼 定義過渡名稱
  view-transition-name: photo;
}
</style>

/after路由內容

<script setup lang="ts">
import { useRouter } from "vue-router"

const router = useRouter()

const handleClick = () => {
  document.startViewTransition(() => {
    router.push('/before')
  })
}
</script>

<template>
  <img
    class="photo"
    src="https://live.staticflickr.com/65535/50187927333_12dc192ab6_b.jpg"
    alt="photo"
    @click="handleClick"
  />
</template>

<style scoped>
.photo {
  max-width: 100%;
  // 關鍵程式碼 定義過渡名稱
  view-transition-name: photo;
}
</style>

兩個路由中的img元素宣告瞭相同的view-transition-name,然後瀏覽器就對其進行了動畫過渡。

示例4

列表與詳情路由的過渡
chrome-capture-2024-10-30 (2).gif

/articles路由

<script setup lang="ts">
import { ref } from "vue"
import { useRouter } from "vue-router";

import { photos as photosData } from "@/api";

const router = useRouter()
const photos = ref(photosData)

const handleClick = (id: number) => {
  document.startViewTransition(() => {
    router.push(`/articles/${id}`)
  })
}
</script>

<template>
  <div class="container">
    <div class="items">
      <div
        class="item"
        v-for="photo in photos"
        :key="photo.id"
        @click="handleClick(photo.id)"
      >
        <img
          class="img"
          :src="photo.src"
          :alt="photo.alt"
          :style="`view-transition-name: photo-${photo.id}`" />
        <div
          :style="`view-transition-name: title-${photo.id}`"
        >{{ photo.alt }}</div>
      </div>
    </div>
  </div>
</template>

<style scoped>
body {
  background: #f7f7f8;
}

.container {
  display: flex;
  justify-content: center;
  align-items: center;
}

.items {
  display: flex;
  flex-direction: column;
  margin-top: 20px;
}

.item {
  display: block;
  margin-bottom: 20px;
  width: 400px;
  padding: 15px;
  border-radius: 3px;
  background: #fff;
}

.img {
  width: 100%;
  margin-bottom: 5px;
}
</style>

/articles/:id路由

<script setup lang="ts">
import { computed } from "vue";
import { useRouter } from "vue-router";
import { useRoute } from "vue-router"
import { photos } from "@/api";

const router = useRouter()
const route = useRoute()

const id = route.params.id as string
const data = computed(() => photos.find((photo) => photo.id === Number(id)))

const handleBack = () => {
  document.startViewTransition(() => {
    router.push("/articles")
  })
}
</script>

<template>
  <div class="container" @click.self="handleBack">
    <img
      class="img"
      :src="data?.src"
      :style="`view-transition-name: photo-${id}`" />
    <div
      class="title"
      :style="`view-transition-name: title-${id}`"
    >{{ data?.alt }}</div>
    <button @click="handleBack">返回</button>
  </div>
</template>

<style scoped>
.container {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.img {
  width: 1000px;
}

.title {
  padding: 20px 0;
  font-size: 20px;
  font-weight: bold;
}
</style>

實現該效果的關鍵程式碼是.img元素上的view-transition-name: photo-${id}和.title元素上的view-transition-name: title-${id},由於同一個頁面不能有相同的view-transition-name存在,所以在這種列表過渡的情況下,我們用id給每個元素作區分。

相關文章