Skip to content

蓄力按鈕 button

需要長按才能觸發的按鈕,按住時會出現進度條,進度滿了放開即觸發點擊。

進度滿後仍不放開,會有驚喜喔。(「・ω・)「

技術關鍵字

名稱 描述
Pointer 事件偵測滑鼠或觸控點移動、點擊、懸停等等事件,取得座標、目標等等資訊
SVG可在 DOM 中使用的圖形格式,適合複雜圖形和動畫,配合 Vue v-bind 可以實現基於資料的動態效果
CSS 動畫基於 CSS transition 和 animation 實現
Anime.js輕量級 JavaScript 動畫函式庫

使用範例

基本用法

按住按鈕直到進度滿再放開,即可觸發 click 事件。

長按按鈕直到進度滿再放開
查看範例原始碼
vue
<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>

星級評分

評分高低與按鈕堅固程度成正比。乁( ◔ ௰◔)「

選擇星級後長按送出
查看範例原始碼
vue
<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 讓碎片向四周飛散、旋轉、縮小並淡出,同時放射集中線強化衝擊感。ヽ(●`∀´●)ノ

原始碼

API

Props

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;
}

Emits

const emit = defineEmits<{
  /** 長按完成後放開時觸發 */
  click: [];
  /** 按鈕爆炸時觸發 */
  explode: [];
  /** 自動回復時觸發 */
  reset: [];
}>()

Methods

defineExpose({
  /** 重置按鈕狀態 */
  reset,
  /** 手動觸發爆炸 */
  explode,
})

Slots

defineSlots<{
  /** 按鈕 */
  default?: () => unknown;
}>()

v0.63.0