Skip to content

特效轉場 transition

效果酷炫、風格強烈的 Transition 元件!◝( •ω• )◟

注意!

此元件會占用 stylefilter 屬性,請確保沒有使用 filter,否則會導致原本的 filter 效果消失。ლ(╹◡╹ლ)

使用範例

基本用法

用法與 Vue 內建的 Transition 元件相同。

(點擊後開始轉場)

查看範例原始碼
vue
<template>
  <div class="w-full flex flex-col gap-4 border border-gray-300 p-6">
    <div class="flex flex-col items-center justify-center gap-3">
      <div
        v-for="item in list"
        :key="item.name"
        class="item h-[5rem] w-full flex items-center justify-center"
        @click="item.visible = !item.visible"
      >
        <transition-special-effects
          :enter="item.name"
          :leave="item.name"
        >
          <div
            v-if="item.visible"
            class="px-8 py-2"
            :class="item.class"
          >
            {{ item.name.toUpperCase() }}
          </div>
        </transition-special-effects>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import type { TransitionName } from '../type'
import { ref } from 'vue'
import TransitionSpecialEffects from '../transition-special-effects.vue'

const list = ref<Array<{
  visible: boolean;
  name: `${TransitionName}`;
  class: string;
}>>([{
  visible: true,
  name: 'wave',
  class: 'bg-blue-200 text-blue-900 text-2xl font-bold  rounded-full',
}, {
  visible: true,
  name: 'cyberpsychosis',
  class: 'bg-gray-200 text-gray-900 text-2xl font-bold',
}, {
  visible: true,
  name: 'melt',
  class: 'text-gray-400 border-2 border-gray-200 text-3xl rounded-xl font-bold',
}, {
  visible: true,
  name: 'glitch',
  class: 'text-red-800 border-dashed border border-red-800 text-2xl font-bold',
}, {
  visible: true,
  name: 'erode',
  class: 'bg-[#222] text-white text-2xl font-black tracking-widest',
}])
</script>

<style scoped lang="sass">
.item
  cursor: pointer
  transition-duration: 0.5s
</style>

進入與離開

可以分別指定 enter 與 leave 特效。

進入特效
離開特效
查看範例原始碼
vue
<template>
  <div class="w-full flex flex-col gap-4 border border-gray-300 p-6">
    <div class="flex flex-col gap-4 border rounded py-4">
      <div class="flex items-center gap-1 px-4">
        <div class="w-20">
          進入特效
        </div>

        <div class="flex-1 border rounded">
          <select
            v-model="enterName"
            class="w-full p-2"
          >
            <option
              v-for="option in options"
              :key="option"
              :value="option"
            >
              {{ option }}
            </option>
          </select>
        </div>
      </div>

      <div class="flex items-center gap-1 px-4">
        <div class="w-20">
          離開特效
        </div>

        <div class="flex-1 border rounded">
          <select
            v-model="leaveName"
            class="w-full p-2"
          >
            <option
              v-for="option in options"
              :key="option"
              :value="option"
            >
              {{ option }}
            </option>
          </select>
        </div>
      </div>

      <base-checkbox
        v-model="visible"
        label="顯示"
        class="px-4"
      />
    </div>

    <div class="h-[50vh] flex items-center justify-center">
      <transition-special-effects
        :enter="enterName"
        :leave="leaveName"
      >
        <div
          v-if="visible"
          class="flex flex-col items-center gap-4"
        >
          <img
            src="/profile.webp"
            class="mb-4 h-60 w-60 overflow-hidden border-4 border-white rounded-full shadow-xl"
          >

          <div class="text-xl font-bold">
            鱈魚 Codfish
          </div>

          <div>
            困擾買不到 IP69K 等級的防水電腦 ( ´•̥̥̥ ω •̥̥̥` )
          </div>
        </div>
      </transition-special-effects>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import BaseCheckbox from '../../base-checkbox.vue'
import TransitionSpecialEffects from '../transition-special-effects.vue'
import { TransitionName } from '../type'

const visible = ref(true)
const enterName = ref<`${TransitionName}`>('wave')
const leaveName = ref<`${TransitionName}`>('wave')

const options = Object.values(TransitionName)
</script>

原理

這類特殊效果已經完全超出 CSS 範疇。

一開始往 HTML to Canvas 方向研究,但是 HTML 轉換成 Canvas 會有很多誤差,成果實在不忍直視。(́⊙◞౪◟⊙‵)

最後發現 SVG Filter 最適合,因為 SVG Filter 可以直接對 HTML 元素套用濾鏡效果。

剛好趁這個機會好好研究 SVG Filter,相當複雜,但是真的很有趣。(*´∀`)~♥

實作概念為:

  1. 將 SVG Filter 內容獨立為 Vue 元件,使用 v-bind 綁定參數,配合 animejs 產生動畫。
  2. 產生唯一 ID,綁定至目標元素 style,產生濾鏡效果。

原始碼

API

Props

interface Props {
  appear?: boolean;
  enter?: EnterParams;
  leave?: LeaveParams;
}

Emits

const emit = defineEmits<{
  (e: 'init'): void;
  (e: 'beforeEnter'): void;
  (e: 'afterEnter'): void;
  (e: 'beforeLeave'): void;
  (e: 'afterLeave'): void;
}>()

Slots

defineSlots<{
  default?: () => unknown;
}>()

v0.23.1