Skip to content

當機卡片 card

致敬 Windows XP 卡住時,視窗移動疊影 BUG

技術關鍵字

名稱 描述
Pointer 事件偵測滑鼠或觸控點移動、點擊、懸停等等事件,取得座標、目標等等資訊
Canvas APIHTML5 繪圖 API,可以高效繪製比 DOM 更複雜的圖形
CSS 動畫基於 CSS transition 和 animation 實現
SVG可在 DOM 中使用的圖形格式,適合複雜圖形和動畫,配合 Vue v-bind 可以實現基於資料的動態效果
DOM to Image將 DOM 元素轉換為圖片的技術,基於 SVG foreignObject 實現

使用範例

基本用法

拉拉看視窗吧 ( ´ ▽ ` )ノ

查看範例原始碼
vue
<template>
  <div class="w-full flex flex-col gap-4 py-4">
    <div class="flex items-center justify-center py-[8vh]">
      <card-render-glitch
        v-slot="{ bindHandle }"
        :draggable
        class="window z-50 overflow-hidden border rounded-xl shadow-xl"
      >
        <div
          :ref="bindHandle"
          class="handle w-full flex items-center px-4"
          :class="draggable ? 'cursor-move' : 'cursor-no-drop'"
        >
          <div class="flex-1 select-none text-center text-sm font-medium">
            Hello, Windows XP!
          </div>

          <button class="mb-1 text-2xl">
            ×
          </button>
        </div>

        <div class="flex flex-col gap-2 p-8 px-12">
          <base-checkbox
            v-model="draggable"
            label="啟用拖動"
          />

          <p class="leading-relaxed">
            嘗試拖動這個視窗
          </p>
        </div>
      </card-render-glitch>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import BaseCheckbox from '../../base-checkbox.vue'
import CardRenderGlitch from '../card-render-glitch.vue'

const draggable = ref(true)
</script>

<style lang="sass" scoped>
:deep(.window)
  background: light-dark(#FFF, #1e1e1e)
  border-color: light-dark(#d0d0d0, #888)
  color: light-dark(#374151, #d1d5db)
  .handle
    background: light-dark(#f3f4f6, #111827)
    color: light-dark(#1f2937, #d1d5db)
</style>

原理

將 DOM 轉換成圖片,然後不斷繪製圖片至 canvas。

如何將 DOM 變成 Image?

原本實作方式想說同 btn-ninja 一樣,使用 v-for 產生多個 default slot 內容,但是性能實在過於慘烈,所以改用 canvas 實現。

想是這麼想,這裡會遇到一個難題:「要怎麼將 DOM 畫到 canvas 上?ლ(╹ε╹ლ)」

html2canvasdom-to-image 都不太理想,後來發現 vfx.js 這個套件內有一個完整實作

於是我就直接複製過來,並稍微修改成符合我需求的版本惹。◝( •ω• )◟

原始碼

API

Props

interface Props {
  draggable?: boolean;
  throttle?: number;
}

Methods

interface Expose {
  clearArtifacts: () => void;
}

Slots

interface SlotScope {
  isDragging: boolean;
  /**
   * 用於綁定拖動把手
   * @param el - 要綁定的元素
   */
  bindHandle: (el: any) => void;
}

interface Slots {
  default?: (params: SlotScope) => unknown;
}

v0.41.0