Skip to content

Jigsaw Puzzle Button button

A button that can only be clicked after you finish the jigsaw puzzle ◝( ゚∀゚ )◟

Usage Examples

Basic Usage

Drag the pieces and complete the puzzle!

View example source code
vue
<template>
  <div class="w-full flex flex-col gap-4 border border-gray-200 rounded-xl p-6">
    <div class="flex justify-center">
      <btn-jigsaw-puzzle
        :label="t('clickMe')"
        @click="handleClick"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import BtnJigsawPuzzle from '../btn-jigsaw-puzzle.vue'

const { t } = useI18n()

function handleClick() {
  // eslint-disable-next-line no-alert
  alert(t('congratulations'))
}
</script>

Form Example

Block bots and annoying complaints! (・∀・)9 (eh?)

Complaint Feedback Form
If you have any concerns, please fill in your feedback here, we will handle your problem as soon as possible.
To prevent abuse, the jigsaw puzzle must be completed before submitting (*´∀`)~♥
View example source code
vue
<template>
  <div class="relative w-full flex flex-col gap-10 border border-gray-200 rounded-xl p-6">
    <div class="flex flex-col gap-3">
      <div class="text-3xl font-bold">
        {{ t('complaintFeedbackForm') }}
      </div>
      <div class="text-sm text-gray-500">
        {{ t('complaintFeedbackFormTip') }}
      </div>

      <div class="flex justify-center border border-gray-200 rounded-lg p-3">
        <textarea
          v-model="text"
          :placeholder="t('inputPlaceholder')"
          class="min-h-[30vh] w-full"
        />
      </div>

      <div class="w-full flex justify-center">
        <btn-jigsaw-puzzle
          ref="jigsawPuzzleRef"
          :row-count="3"
          :col-count="4"
          @click="handleSubmit"
        >
          <button class="btn w-full select-none rounded p-3 px-20 text-3xl">
            {{ t('submitBtn') }}
          </button>
        </btn-jigsaw-puzzle>
      </div>

      <div class="mt-2 text-center text-xs text-gray-500">
        {{ t('jigsawPuzzleTip') }}
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, useTemplateRef } from 'vue'
import { useI18n } from 'vue-i18n'
import BtnJigsawPuzzle from '../btn-jigsaw-puzzle.vue'

const { t } = useI18n()

const jigsawPuzzleRef = useTemplateRef('jigsawPuzzleRef')

const text = ref('')
function handleSubmit() {
  // eslint-disable-next-line no-alert
  alert(t('thanks'))

  text.value = ''
  jigsawPuzzleRef.value?.scatter()
}
</script>

<style scoped lang="sass">
.btn
  background-color: light-dark(#444, #EEE)
  color: light-dark(#EEE, #444)
  transition-duration: 0.2s
  &:active
    transition-duration: 0.1s
    transform: scale(0.98)
</style>

How It Works

Uses an SVG mask to implement the jigsaw splitting, and Pointer events to implement the drag-and-drop behavior.

Source Code

API

Props

interface Props {
  /** 按鈕內文字 */
  label?: string;
  /** 是否停用 */
  disabled?: boolean;
  /** 同 CSS z-index */
  zIndex?: number | string;
  /** 同 html tabindex */
  tabindex?: number | string;

  /** 拼圖行數 */
  rowCount?: number;
  /** 拼圖列數 */
  colCount?: number;
}

Emits

const emit = defineEmits<{
  /** 開始拖動 */
  dragStart: [piece: Piece, evt: PointerEvent];
  dragging: [piece: Piece, evt: PointerEvent];
  dragStop: [piece: Piece, evt: PointerEvent];
  completed: [];
  click: [];
}>()

Methods

defineExpose({
  /** 打散拼圖 */
  scatter,
  /** 自動完成 */
  autoComplete,
})

Slots

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

v0.54.1