Skip to content

Every little fish swimming around is a visitor on this page right now, and one of them is you ( ´ ▽ ` )ノ If they get in the way of reading, feel free to turn them off.

Render Glitch Card card ​

A tribute to the Windows XP window-dragging ghost trail bug.

Tech Keywords ​

NameDescription
Pointer EventsDetects pointer movement, clicks, hovers, and more, providing coordinates and target information
Canvas 2D APIBasic 2D drawing API that efficiently renders graphics too complex for the DOM
CSS AnimationImplemented with CSS transition and animation
DOM to ImageConverts DOM elements into images, based on SVG foreignObject

Usage Examples ​

Basic Usage ​

Try dragging the window! ( ´ ▽ ` )ノ

Hello, Windows XP!

Try dragging this window

View example source code
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="t('enableDrag')"
          />

          <p class="leading-relaxed">
            {{ t('tryDrag') }}
          </p>
        </div>
      </card-render-glitch>
    </div>
  </div>
</template>

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

const draggable = ref(true)

const { t } = useI18n()
</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>

How It Works ​

Converts the DOM into an image, then continuously draws the image onto a canvas.

How to convert DOM to Image? ​

The original implementation idea was to use v-for to generate multiple default slot contents like btn-ninja, but the performance was terrible, so canvas was used instead.

The challenge was: "How to draw DOM onto a canvas? ლ(╹ε╹ლ)"

Both html2canvas and dom-to-image were not ideal. Later, a complete implementation was found in the vfx.js library.

So I copied it over and slightly modified it to fit my needs. ◝( •ω• )◟

Source Code ​

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.93.1