Skip to content
Welcome to vote for your favorite component! You can also tell me anything you want to say! (*´∀`)~♥

Snow bg

A snowy background effect for your webpage -- it even accumulates snow! ( ´ ▽ ` )ノ

Usage Examples

Basic Usage

Use the v-bg-static directive to make designated elements accumulate snow.

You can also adjust the snowflake image and size.

View example source code
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="t('startSnow')"
        class="col-span-3 border border-gray-200 rounded-full p-4 px-6"
      />

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

      <base-input
        :label="t('snowflakeImage')"
        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="`${t('particleSize')}: ${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 { useI18n } from 'vue-i18n'
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 { t } = useI18n()

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>

How It Works

The snow animation is based on Babylon.js's particle system. The snow accumulation effect performs collision detection on DOM elements marked with v-bg-static.

Here's a small challenge: since the number of snowflakes can be very large, the computational cost can be quite significant. If we calculate directly on the main thread, the page would become laggy and janky.

This is where a neat trick comes in: Offscreen Canvas! ( •̀ ω •́ )✧

This technology allows the entire Canvas rendering to be handed off to a Web Worker. This means all computations, including the expensive calculation tasks, no longer run on the main thread, so you don't have to worry about UI jank. (/≧▽≦)/

So, what about browser support? ლ(´ 口`ლ)

As of 2024/12/22, browser support is at 95.21%, so it should be safe to use. ᕕ( ゚ ∀。)ᕗ

When you look at the source code, you might notice I used a package called Comlink. This package greatly simplifies the way messages are passed between Web Workers and the main thread. ( ´ ▽ ` )ノ

Otherwise, using postMessage to pass messages is really cumbersome. ⎝(・ω´・⎝)

Source Code

API

Methods

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

v0.60.0