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.
Snow
bgA snowy background effect for your webpage -- it even accumulates snow! ( ´ ▽ ` )ノ
Tech Keywords
Section titled “Tech Keywords”| Name | Description |
|---|---|
| Babylon.js | 3D engine |
| Particle System | Spawns large numbers of small objects, commonly used to simulate smoke, fire, rain, and snow |
| OffscreenCanvas | Offloads canvas rendering to a separate thread without blocking the main thread |
| Web Worker | Runs scripts on a background thread, keeping UI interactions on the main thread unblocked |
| Vue Directive | Custom Vue directives that encapsulate DOM manipulation logic and repeated behaviors |
| Physics Simulation | Simulates real-world physics such as gravity, collisions, and velocity |
Usage Examples
Section titled “Usage Examples”Basic Usage
Section titled “Basic Usage”Use the v-bg-static directive to make designated elements accumulate snow.
You can also adjust the snowflake image and size.
<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-4 border border-gray-200 rounded-full p-4 px-6 sm:col-span-3" />
<base-btn v-bg-static :label="t('sweep')" class="col-span-4 border-gray-200 sm:col-span-1 !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] w-full h-lvh" :class="{ 'opacity-0': !enable }" :image-src="imgUrl" :size="options.size" /> </div></template>
<script setup lang="ts">import { useObjectUrl } from '@vueuse/core'import { onUnmounted, ref, useTemplateRef, watch } from 'vue'import { useI18n } from 'vue-i18n'import { useSiteDark } from '../../../composables/use-site-dark'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 = useSiteDark()
const oriValue = isDark.value
watch(enable, (value) => { isDark.value = value})
onUnmounted(() => { isDark.value = oriValue})</script>How It Works
Section titled “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. (/≧▽≦)/
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
Section titled “Source Code”Methods
Section titled “Methods”interface Expose { /** 清除積雪 */ sweep: () => void;}If you have any suggestions or questions, feel free to email me( ´ ▽ ` )ノ
If you like cool components, feel free to " Buy me a cup of coffee " to show your support!