每條游來游去的小魚,都是正在瀏覽這一頁的訪客,有一條是你自己 ( ´ ▽ ` )ノ 覺得干擾閱讀的話,可以把小魚關掉。
蓄力按鈕
button需要長按才能觸發的按鈕,按住時會出現進度條,進度滿了放開即觸發點擊。
進度滿後仍不放開,會有驚喜喔。(「・ω・)「
| 名稱 | 描述 |
|---|---|
| Pointer 事件 | 偵測滑鼠或觸控點移動、點擊、懸停等等事件,取得座標、目標等等資訊 |
| SVG | 可在 DOM 中使用的圖形格式,適合複雜圖形和動畫,配合 Vue v-bind 可以實現基於資料的動態效果 |
| CSS 動畫 | 基於 CSS transition 和 animation 實現 |
| Anime.js | 輕量級 JavaScript 動畫函式庫 |
| JS 動畫 | 基於 JavaScript 實現的動畫,達成更複雜、精準的動畫控制,常見套件有 GSAP、anime.js 等 |
按住按鈕直到進度滿再放開,即可觸發 click 事件。
長按按鈕直到進度滿再放開
<template> <div class="flex flex-col items-center gap-6"> <btn-charge :label="t('label')" @click="handleClick" @explode="handleExplode" @reset="handleReset" />
<div class="text-sm opacity-60"> <transition mode="out-in" enter-active-class="transition-opacity duration-300" leave-active-class="transition-opacity duration-150" enter-from-class="!opacity-0" leave-to-class="!opacity-0" > <span :key="message"> {{ message }} </span> </transition> </div> </div></template>
<script setup lang="ts">import { ref } from 'vue'import { useI18n } from 'vue-i18n'import BtnCharge from '../btn-charge.vue'
const { t } = useI18n()
const message = ref(t('hint'))
function handleClick() { message.value = t('clicked')}
function handleExplode() { message.value = t('exploded')}
function handleReset() { message.value = t('hint')}</script>評分高低與按鈕堅固程度成正比。乁( ◔ ௰◔)「
選擇星級後長按送出
<template> <div class="flex flex-col items-center gap-6"> <div class="flex gap-1"> <button v-for="index in 5" :key="index" class="star-btn" :class="{ active: index <= rating }" @click="rating = index" > <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="32" height="32" :fill="index <= rating ? 'currentColor' : 'none'" stroke="currentColor" stroke-width="1.5" > <path d="M12 2l3.09 6.26L22 9.27l-5 4.87L18.18 22 12 18.56 5.82 22 7 14.14l-5-4.87 6.91-1.01z" /> </svg> </button> </div>
<btn-charge :label="t('label')" :shake-duration="shakeDurationMap[rating]" :auto-reset-delay="3000" :press-duration="456" @click="handleClick" @explode="handleExplode" @reset="handleReset" />
<div class="text-sm opacity-60"> <transition mode="out-in" enter-active-class="transition-opacity duration-300" leave-active-class="transition-opacity duration-150" enter-from-class="!opacity-0" leave-to-class="!opacity-0" > <span :key="message"> {{ message }} </span> </transition> </div> </div></template>
<script setup lang="ts">import { ref } from 'vue'import { useI18n } from 'vue-i18n'import BtnCharge from '../btn-charge.vue'
const { t } = useI18n()
const rating = ref(5)const message = ref(t('hint'))
const shakeDurationMap: Record<number, number> = { 1: 1, 2: 5, 3: 50, 4: 1000, 5: 10000,}
function handleClick() { message.value = t('clicked', { rating: rating.value })}
function handleExplode() { message.value = t('exploded')}
function handleReset() { message.value = t('hint')}</script>以 Pointer Events 統一處理滑鼠與觸控。
按下時以 requestAnimationFrame 驅動進度,按鈕內部從底部往上填充亮色區塊,呈現水位上升效果。
觸控裝置額外在手指上方顯示圓餅進度 knob,以免肥手指遮擋進度條。
中途放開時,進度條以 outBounce 緩動回縮歸零;進度滿後放開則觸發 click。
進度滿後仍不放開會進入抖動階段,以 CSS @keyframes 產生隨機位移震動,強度隨時間遞增。
超過抖動時限後觸發爆炸,以 SVG clipPath 將按鈕切割成多塊四邊形碎片,再用 anime.js 讓碎片向四周飛散、旋轉、縮小並淡出,同時放射集中線強化衝擊感。ヽ(●`∀´●)ノ
interface Props { /** 按鈕內文字 */ label?: string; /** 是否停用 */ disabled?: boolean; /** 同 html tabindex */ tabindex?: number | string;
/** 長按填滿所需時間(ms)。@default 1000 */ pressDuration?: number; /** 進度滿後抖動多久會爆炸(ms)。@default 500 */ shakeDuration?: number;
/** 爆炸碎片行數。@default 4 */ rowCount?: number; /** 爆炸碎片列數。@default 4 */ colCount?: number;
/** 爆炸後自動回復的延遲時間(ms),設為 0 或不設則不自動回復。@default 5000 */ autoResetDelay?: number;
/** knob 圓餅直徑(px)。@default 30 */ knobSize?: number;}const emit = defineEmits<{ /** 長按完成後放開時觸發 */ click: []; /** 按鈕爆炸時觸發 */ explode: []; /** 自動回復時觸發 */ reset: [];}>()Methods
Section titled “Methods”interface Expose { /** 重置按鈕狀態 */ reset: () => void; /** 手動觸發爆炸 */ explode: () => void;}defineSlots<{ /** 按鈕 */ default?: () => unknown;}>()