跳到內容

每條游來游去的小魚,都是正在瀏覽這一頁的訪客,有一條是你自己 ( ´ ▽ ` )ノ 覺得干擾閱讀的話,可以把小魚關掉。

特效轉場

transition

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

名稱描述
Anime.js輕量級 JavaScript 動畫函式庫
JS 動畫基於 JavaScript 實現的動畫,達成更複雜、精準的動畫控制,常見套件有 GSAP、anime.js 等
SVG FilterSVG 濾鏡效果,例如:模糊、扭曲、顏色調整等等
Vue Transition內建元件,可以處理單個項目的進出動畫
SVG feDisplacementMap依位移圖的色值偏移取樣來源像素,可做出扭曲、凹陷、波動等效果
SVG feColorMatrix以 4×5 矩陣重算每個像素的 RGBA,可調色、轉灰階或逐格插值動畫

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

(點擊後開始轉場)

<template>
<div class="w-full flex flex-col gap-4 example-wrap">
<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 dark:text-gray-500 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 特效。

進入特效
離開特效
<!-- #region main-code -->
<template>
<div class="example-wrap w-full flex flex-col gap-4">
<div class="example-ctrl flex flex-col gap-4">
<div class="flex items-center gap-1 px-4">
<div class="w-20">
{{ t('進入特效') }}
</div>
<div class="flex-1 border rounded">
<base-select
v-model="enterName"
:options
class="w-full"
/>
</div>
</div>
<div class="flex items-center gap-1 px-4">
<div class="w-20">
{{ t('離開特效') }}
</div>
<div class="flex-1 border rounded">
<base-select
v-model="leaveName"
:options
class="w-full"
/>
</div>
</div>
<base-checkbox
v-model="visible"
:label="t('顯示')"
class="px-4"
/>
</div>
<div
class="h-[50vh] flex cursor-pointer items-center justify-center"
@click="visible = !visible"
>
<transition-special-effects
:enter="enterName"
:leave="leaveName"
>
<div
v-if="visible"
class="flex flex-col items-center gap-4"
>
<img
src="/low/profile.webp"
alt=""
class="mb-4 h-60 w-60 overflow-hidden border-4 border-white rounded-full shadow-xl"
>
<div class="text-xl font-bold">
{{ t('鱈魚 Codfish') }}
</div>
<div>
{{ t('困擾買不到 IP69K 等級的防水電腦') }}
</div>
</div>
</transition-special-effects>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import BaseCheckbox from '../../base-checkbox.vue'
import BaseSelect from '../../base-select.vue'
import TransitionSpecialEffects from '../transition-special-effects.vue'
import { TransitionName } from '../type'
const { t } = useI18n()
const visible = ref(true)
const enterName = ref<`${TransitionName}`>('wave')
const leaveName = ref<`${TransitionName}`>('wave')
const options = Object.values(TransitionName)
</script>
<!-- #endregion main-code -->
<i18n lang="json">
{
"zh-hant": {
"進入特效": "進入特效",
"離開特效": "離開特效",
"顯示": "顯示",
"鱈魚 Codfish": "鱈魚 Codfish",
"困擾買不到 IP69K 等級的防水電腦": "困擾買不到 IP69K 等級的防水電腦 ( ´•̥̥̥ ω •̥̥̥` )"
},
"en": {
"進入特效": "Enter Effect",
"離開特效": "Leave Effect",
"顯示": "Show",
"鱈魚 Codfish": "Codfish",
"困擾買不到 IP69K 等級的防水電腦": "Troubled by not being able to buy an IP69K waterproof computer ( ´•̥̥̥ ω •̥̥̥` )"
}
}
</i18n>

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

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

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

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

實作概念為:

  1. 將 SVG Filter 內容獨立為 Vue 元件,使用 v-bind 綁定參數,配合 animejs 產生動畫。
  2. 產生唯一 ID,綁定至目標元素 style,產生濾鏡效果。
interface Props {
appear?: boolean;
enter?: EnterParams;
leave?: LeaveParams;
}
const emit = defineEmits<{
(e: 'init'): void;
(e: 'beforeEnter'): void;
(e: 'afterEnter'): void;
(e: 'beforeLeave'): void;
(e: 'afterLeave'): void;
}>()
defineSlots<{
default?: () => unknown;
}>()