當機卡片 card
致敬 Windows XP 卡住時,視窗移動疊影 BUG
技術關鍵字
名稱 | 描述 |
---|---|
Pointer 事件 | 偵測滑鼠或觸控點移動、點擊、懸停等等事件,取得座標、目標等等資訊 |
Canvas API | HTML5 繪圖 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 上?ლ(╹ε╹ლ)」
html2canvas
與 dom-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;
}