Skip to content
歡迎來票選你最喜歡的元件! 也可以告訴我任何你想說的話喔!(*´∀`)~♥

落雪 bg

讓網頁飄雪的背景效果,還會積雪喔。( ´ ▽ ` )ノ

技術關鍵字

名稱 描述
WebGPU現代網頁圖形 API,WebGL 繼任者
Babylon.js3D 引擎
粒子系統產生大量小物件的系統,常用於模擬煙霧、火焰、雨雪等效果
OffscreenCanvas由外部執行緒計算 Canvas 繪製,不阻塞主執行緒
Web Worker在背景執行緒執行腳本,避免阻塞主執行緒的 UI 操作
Vue Directive自定義 Vue 指令,用於封裝 DOM 操作邏輯和重複行為

使用範例

基本用法

使用 v-bg-static 指令可以讓指定物體積雪。

還可以調整雪片圖片與大小。

查看範例原始碼
vue
<template>
  <div class="w-full flex items-center justify-center py-10">
    <div class="grid grid-cols-4 gap-10">
      <base-checkbox
        v-model="enable"
        v-bg-static
        label="開始下雪!੭ ˙ᗜ˙ )੭"
        class="col-span-3 border border-gray-200 rounded-full p-4 px-6"
      />

      <base-btn
        v-bg-static
        label="除雪"
        class="border-gray-200 !rounded-full"
        @click="sweep()"
      />

      <base-input
        label="雪片圖片"
        class="col-span-2"
      >
        <div
          v-bg-static
          class="border rounded"
        >
          <input
            type="file"
            accept="image/*"
            class="px-2 py-1"
            @change="handleImageChange"
          >
        </div>
      </base-input>

      <base-input
        v-model="options.size"
        :label="`粒子大小: ${options.size}`"
        type="range"
        :min="1"
        :max="20"
        class="col-span-2"
      />
    </div>

    <bg-snow
      ref="bgRef"
      class="fixed left-0 top-0 z-[100] h-full w-full"
      :class="{ 'opacity-0': !enable }"
      :image-src="imgUrl"
      :size="options.size"
    />
  </div>
</template>

<script setup lang="ts">
import { useObjectUrl } from '@vueuse/core'
import { useData } from 'vitepress'
import { onUnmounted, ref, useTemplateRef, watch } from 'vue'
import BaseBtn from '../../base-btn.vue'
import BaseCheckbox from '../../base-checkbox.vue'
import BaseInput from '../../base-input.vue'
import BgSnow from '../bg-snow.vue'
import { vBgStatic } from '../v-bg-static'

const enable = ref(false)

const imgFile = ref<File>()
function handleImageChange(event: Event) {
  const file = (event.target as HTMLInputElement).files?.[0]
  imgFile.value = file
}
const imgUrl = useObjectUrl(imgFile)

const bgRef = useTemplateRef('bgRef')
const options = ref({
  size: 8,
})
function sweep() {
  bgRef.value?.sweep()
}

const { isDark } = useData()

const oriValue = isDark.value

watch(enable, (value) => {
  isDark.value = value
})

onUnmounted(() => {
  isDark.value = oriValue
})
</script>

原理

下雪動畫基於 babylon.js 的粒子系統,積雪效果則是對被 v-bg-static 標記的 DOM 進行碰撞偵測

這裡就會有個小問題,因為雪的數量非常多,其計算量可能會相當可觀,如果直接在主執行續進行計算,就會導致畫面卡卡、不流暢。

這裡使用了一個有趣的小技巧,那就是 Offscreen Canvas!( •̀ ω •́ )✧

這個技術可以讓整個 Canvas 繪製都交給 Web Worker 處理,也就是說包含費時的計算任務在內,所有的計算都不會再主執行續進行,就不用擔心畫面卡頓了。(/≧▽≦)/

所以我說那個支援度呢?ლ(´ 口`ლ)

截至 2024/12/22,瀏覽器支援度為 95.21%,應該是可以放心用啦 ᕕ( ゚ ∀。)ᕗ

大家查看原始碼時,可能會發現我用了一個名為 Comlink 的套件,這個套件可以大幅簡化 Web Worker 與主執行續之間傳遞訊息的方式。( ´ ▽ ` )ノ

不然原本要用 postMessage 傳遞訊息,寫起來真滴麻煩。⎝(・ω´・⎝)

原始碼

API

Methods

defineExpose({
  /** 清除積雪 */
  sweep,
})

v0.57.0