Skip to content
Welcome to vote for your favorite component! You can also tell me anything you want to say! (*´∀`)~♥

Charge Button button

A button that requires a long press to trigger. A pie-shaped progress indicator appears while pressing; release when full to trigger click.

If you keep holding after the progress is full, the button starts shaking with increasing intensity. Hold even longer and it shatters into pieces with manga-style speed lines.

Supports both mouse and touch input.

Example

Basic Usage

Long press the button until progress is full, then release to trigger the click event. If you never let go, the button will eventually explode.

Long press the button until progress is full, then release
View Example Source Code
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>

Principle

Pointer Events unify mouse and touch handling. On press, requestAnimationFrame drives the progress, rendered as a pie-shaped SVG arc.

After the progress fills, the shaking phase begins — CSS @keyframes produce random displacement with intensity increasing over time via an easeIn curve.

When the shake timer expires, the explosion triggers: SVG clipPath slices the button into quadrilateral fragments, each containing the original button content. anime.js then scatters the pieces outward with randomized direction, spin, speed, and easing. Manga-style speed lines (individual SVG <line> elements) shoot outward from the center simultaneously.

Source Code

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 0 */
  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