mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 14:39:30 -07:00
🎉 New <PixelSnow /> component
This commit is contained in:
@@ -19,10 +19,10 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="hero-main-content">
|
<div class="hero-main-content">
|
||||||
<router-link to="/backgrounds/light-pillar" class="hero-new-badge-container">
|
<router-link to="/backgrounds/pixel-snow" class="hero-new-badge-container">
|
||||||
<span class="hero-new-badge">New 🎉</span>
|
<span class="hero-new-badge">Christmas Special 🎁</span>
|
||||||
<div class="hero-new-badge-text">
|
<div class="hero-new-badge-text">
|
||||||
<span>Light Pillar</span>
|
<span>Pixel Snow</span>
|
||||||
<i class="pi-arrow-right pi" style="font-size: 0.8rem"></i>
|
<i class="pi-arrow-right pi" style="font-size: 0.8rem"></i>
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Highlighted sidebar items
|
// Highlighted sidebar items
|
||||||
export const NEW = ['Color Bends', 'Ghost Cursor', 'Laser Flow', 'Liquid Ether', 'Pixel Blast', 'Floating Lines', 'Light Pillar'];
|
export const NEW = ['Color Bends', 'Ghost Cursor', 'Laser Flow', 'Liquid Ether', 'Pixel Blast', 'Floating Lines', 'Light Pillar', 'Pixel Snow'];
|
||||||
export const UPDATED = [];
|
export const UPDATED = [];
|
||||||
|
|
||||||
// Used for main sidebar navigation
|
// Used for main sidebar navigation
|
||||||
@@ -130,6 +130,7 @@ export const CATEGORIES = [
|
|||||||
'Orb',
|
'Orb',
|
||||||
'Particles',
|
'Particles',
|
||||||
'Pixel Blast',
|
'Pixel Blast',
|
||||||
|
'Pixel Snow',
|
||||||
'Plasma',
|
'Plasma',
|
||||||
'Prism',
|
'Prism',
|
||||||
'Prismatic Burst',
|
'Prismatic Burst',
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ const backgrounds = {
|
|||||||
'color-bends': () => import('../demo/Backgrounds/ColorBendsDemo.vue'),
|
'color-bends': () => import('../demo/Backgrounds/ColorBendsDemo.vue'),
|
||||||
'floating-lines': () => import('../demo/Backgrounds/FloatingLinesDemo.vue'),
|
'floating-lines': () => import('../demo/Backgrounds/FloatingLinesDemo.vue'),
|
||||||
'light-pillar': () => import('../demo/Backgrounds/LightPillarDemo.vue'),
|
'light-pillar': () => import('../demo/Backgrounds/LightPillarDemo.vue'),
|
||||||
|
'pixel-snow': () => import('../demo/Backgrounds/PixelSnowDemo.vue'),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const componentMap = {
|
export const componentMap = {
|
||||||
|
|||||||
@@ -916,5 +916,13 @@ export const componentMetadata: ComponentMetadata = {
|
|||||||
name: 'LightPillar',
|
name: 'LightPillar',
|
||||||
docsUrl: 'https://vue-bits.dev/backgrounds/light-pillar',
|
docsUrl: 'https://vue-bits.dev/backgrounds/light-pillar',
|
||||||
tags: []
|
tags: []
|
||||||
|
},
|
||||||
|
'Backgrounds/PixelSnow': {
|
||||||
|
videoUrl: '/assets/videos/pixelsnow.webm',
|
||||||
|
description: 'Falling pixelated snow effect with customizable density and speed.',
|
||||||
|
category: 'Backgrounds',
|
||||||
|
name: 'PixelSnow',
|
||||||
|
docsUrl: 'https://vue-bits.dev/backgrounds/pixel-snow',
|
||||||
|
tags: []
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
24
src/constants/code/Backgrounds/pixelSnowCode.ts
Normal file
24
src/constants/code/Backgrounds/pixelSnowCode.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import code from '@content/Backgrounds/PixelSnow/PixelSnow.vue?raw';
|
||||||
|
import { createCodeObject } from '../../../types/code';
|
||||||
|
|
||||||
|
export const pixelSnow = createCodeObject(code, 'Backgrounds/PixelSnow', {
|
||||||
|
installation: `npm install three`,
|
||||||
|
usage: `<template>
|
||||||
|
<div style="width: 100%; height: 600px; position: relative">
|
||||||
|
<PixelSnow
|
||||||
|
color="#ffffff"
|
||||||
|
:flakeSize="0.01"
|
||||||
|
:minFlakeSize="1.25"
|
||||||
|
:pixelResolution="200"
|
||||||
|
:speed="1.25"
|
||||||
|
:density="0.3"
|
||||||
|
:direction="125"
|
||||||
|
:brightness="1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import PixelSnow from './PixelSnow.vue'
|
||||||
|
</script>`
|
||||||
|
});
|
||||||
277
src/content/Backgrounds/PixelSnow/PixelSnow.vue
Normal file
277
src/content/Backgrounds/PixelSnow/PixelSnow.vue
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
Color,
|
||||||
|
Mesh,
|
||||||
|
OrthographicCamera,
|
||||||
|
PlaneGeometry,
|
||||||
|
Scene,
|
||||||
|
ShaderMaterial,
|
||||||
|
Vector2,
|
||||||
|
Vector3,
|
||||||
|
WebGLRenderer
|
||||||
|
} from 'three';
|
||||||
|
import { onBeforeUnmount, onMounted, ref, useTemplateRef, watch, type CSSProperties } from 'vue';
|
||||||
|
|
||||||
|
const vertexShader = `
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(position, 1.0);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const fragmentShader = `
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
uniform float uTime;
|
||||||
|
uniform vec2 uResolution;
|
||||||
|
uniform float uFlakeSize;
|
||||||
|
uniform float uMinFlakeSize;
|
||||||
|
uniform float uPixelResolution;
|
||||||
|
uniform float uSpeed;
|
||||||
|
uniform float uDepthFade;
|
||||||
|
uniform float uFarPlane;
|
||||||
|
uniform vec3 uColor;
|
||||||
|
uniform float uBrightness;
|
||||||
|
uniform float uGamma;
|
||||||
|
uniform float uDensity;
|
||||||
|
uniform float uVariant;
|
||||||
|
uniform float uDirection;
|
||||||
|
|
||||||
|
#define M1 1597334677U
|
||||||
|
#define M2 3812015801U
|
||||||
|
#define M3 3299493293U
|
||||||
|
#define F0 (1.0/float(0xffffffffU))
|
||||||
|
#define hash(n) n*(n^(n>>15))
|
||||||
|
#define coord3(p) (uvec3(p).x*M1^uvec3(p).y*M2^uvec3(p).z*M3)
|
||||||
|
|
||||||
|
vec3 hash3(uint n) {
|
||||||
|
return vec3(hash(n) * uvec3(0x1U, 0x1ffU, 0x3ffffU)) * F0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float snowflakeDist(vec2 p) {
|
||||||
|
float r = length(p);
|
||||||
|
float a = atan(p.y, p.x);
|
||||||
|
float PI = 3.14159265;
|
||||||
|
a = abs(mod(a + PI / 6.0, PI / 3.0) - PI / 6.0);
|
||||||
|
vec2 q = r * vec2(cos(a), sin(a));
|
||||||
|
float dMain = abs(q.y);
|
||||||
|
dMain = max(dMain, max(-q.x, q.x - 1.0));
|
||||||
|
vec2 b1s = vec2(0.4, 0.0);
|
||||||
|
vec2 b1d = vec2(0.574, 0.819);
|
||||||
|
float b1t = clamp(dot(q - b1s, b1d), 0.0, 0.4);
|
||||||
|
float dB1 = length(q - b1s - b1t * b1d);
|
||||||
|
vec2 b2s = vec2(0.7, 0.0);
|
||||||
|
float b2t = clamp(dot(q - b2s, b1d), 0.0, 0.25);
|
||||||
|
float dB2 = length(q - b2s - b2t * b1d);
|
||||||
|
return min(dMain, min(dB1, dB2)) * 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float pixelSize = max(1.0, floor(0.5 + uResolution.x / uPixelResolution));
|
||||||
|
vec2 fragCoord = floor(gl_FragCoord.xy / pixelSize);
|
||||||
|
vec2 res = uResolution / pixelSize;
|
||||||
|
|
||||||
|
vec3 ray = normalize(vec3((fragCoord - res * 0.5) / res.x, 1.0));
|
||||||
|
|
||||||
|
vec3 camK = normalize(vec3(1.0, 1.0, 1.0));
|
||||||
|
vec3 camI = normalize(vec3(1.0, 0.0, -1.0));
|
||||||
|
vec3 camJ = cross(camK, camI);
|
||||||
|
ray = ray.x * camI + ray.y * camJ + ray.z * camK;
|
||||||
|
|
||||||
|
float windX = cos(uDirection) * 0.4;
|
||||||
|
float windY = sin(uDirection) * 0.4;
|
||||||
|
vec3 camPos = (windX * camI + windY * camJ + 0.1 * camK) * uTime * uSpeed;
|
||||||
|
vec3 pos = camPos;
|
||||||
|
|
||||||
|
vec3 strides = 1.0 / max(abs(ray), vec3(0.001));
|
||||||
|
vec3 phase = fract(pos) * strides;
|
||||||
|
phase = mix(strides - phase, phase, step(ray, vec3(0.0)));
|
||||||
|
|
||||||
|
float t = 0.0;
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
if (t >= uFarPlane) break;
|
||||||
|
vec3 fpos = floor(pos);
|
||||||
|
float cellHash = hash3(coord3(fpos)).x;
|
||||||
|
|
||||||
|
if (cellHash < uDensity) {
|
||||||
|
vec3 h = hash3(coord3(fpos));
|
||||||
|
vec3 flakePos = 0.5 - 0.5 * cos(
|
||||||
|
4.0 * sin(fpos.yzx * 0.073) +
|
||||||
|
4.0 * sin(fpos.zxy * 0.27) +
|
||||||
|
2.0 * h +
|
||||||
|
uTime * uSpeed * 0.1 * vec3(7.0, 8.0, 5.0)
|
||||||
|
);
|
||||||
|
flakePos = flakePos * 0.8 + 0.1 + fpos;
|
||||||
|
|
||||||
|
float toIntersection = dot(flakePos - pos, camK) / dot(ray, camK);
|
||||||
|
if (toIntersection > 0.0) {
|
||||||
|
vec3 testPos = pos + ray * toIntersection - flakePos;
|
||||||
|
vec2 testUV = abs(vec2(dot(testPos, camI), dot(testPos, camJ)));
|
||||||
|
float depth = dot(flakePos - camPos, camK);
|
||||||
|
float flakeSize = max(uFlakeSize, uMinFlakeSize * depth * 0.5 / res.x);
|
||||||
|
float dist;
|
||||||
|
if (uVariant < 0.5) dist = max(testUV.x, testUV.y);
|
||||||
|
else if (uVariant < 1.5) dist = length(testUV);
|
||||||
|
else dist = snowflakeDist(vec2(dot(testPos, camI), dot(testPos, camJ)) / flakeSize) * flakeSize;
|
||||||
|
|
||||||
|
if (dist < flakeSize) {
|
||||||
|
float intensity = exp2(-(t + toIntersection) / uDepthFade) *
|
||||||
|
min(1.0, pow(uFlakeSize / flakeSize, 2.0)) * uBrightness;
|
||||||
|
gl_FragColor = vec4(uColor * pow(vec3(intensity), vec3(uGamma)), 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float nextStep = min(min(phase.x, phase.y), phase.z);
|
||||||
|
vec3 sel = step(phase, vec3(nextStep));
|
||||||
|
phase = phase - nextStep + strides * sel;
|
||||||
|
t += nextStep;
|
||||||
|
pos = mix(pos + ray * nextStep, floor(pos + ray * nextStep + 0.5), sel);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = vec4(0.0);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface PixelSnowProps {
|
||||||
|
color?: string;
|
||||||
|
flakeSize?: number;
|
||||||
|
minFlakeSize?: number;
|
||||||
|
pixelResolution?: number;
|
||||||
|
speed?: number;
|
||||||
|
depthFade?: number;
|
||||||
|
farPlane?: number;
|
||||||
|
brightness?: number;
|
||||||
|
gamma?: number;
|
||||||
|
density?: number;
|
||||||
|
variant?: 'square' | 'round' | 'snowflake';
|
||||||
|
direction?: number;
|
||||||
|
className?: string;
|
||||||
|
style?: CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<PixelSnowProps>(), {
|
||||||
|
color: '#ffffff',
|
||||||
|
flakeSize: 0.01,
|
||||||
|
minFlakeSize: 1.25,
|
||||||
|
pixelResolution: 200,
|
||||||
|
speed: 1.25,
|
||||||
|
depthFade: 8,
|
||||||
|
farPlane: 20,
|
||||||
|
brightness: 1,
|
||||||
|
gamma: 0.4545,
|
||||||
|
density: 0.3,
|
||||||
|
variant: 'square',
|
||||||
|
direction: 125,
|
||||||
|
className: '',
|
||||||
|
style: () => ({})
|
||||||
|
});
|
||||||
|
|
||||||
|
const containerRef = useTemplateRef<HTMLDivElement>('containerRef');
|
||||||
|
const animationRef = ref<number>(0);
|
||||||
|
|
||||||
|
let cleanupFn: (() => void) | null = null;
|
||||||
|
const setupFn = () => {
|
||||||
|
const container = containerRef.value;
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const scene = new Scene();
|
||||||
|
const camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
||||||
|
const renderer = new WebGLRenderer({
|
||||||
|
antialias: false,
|
||||||
|
alpha: true,
|
||||||
|
premultipliedAlpha: false,
|
||||||
|
powerPreference: 'high-performance'
|
||||||
|
});
|
||||||
|
|
||||||
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||||||
|
renderer.setSize(container.offsetWidth, container.offsetHeight);
|
||||||
|
renderer.setClearColor(0x000000, 0);
|
||||||
|
container.appendChild(renderer.domElement);
|
||||||
|
|
||||||
|
const threeColor = new Color(props.color);
|
||||||
|
const material = new ShaderMaterial({
|
||||||
|
vertexShader,
|
||||||
|
fragmentShader,
|
||||||
|
uniforms: {
|
||||||
|
uTime: { value: 0 },
|
||||||
|
uResolution: { value: new Vector2(container.offsetWidth, container.offsetHeight) },
|
||||||
|
uFlakeSize: { value: props.flakeSize },
|
||||||
|
uMinFlakeSize: { value: props.minFlakeSize },
|
||||||
|
uPixelResolution: { value: props.pixelResolution },
|
||||||
|
uSpeed: { value: props.speed },
|
||||||
|
uDepthFade: { value: props.depthFade },
|
||||||
|
uFarPlane: { value: props.farPlane },
|
||||||
|
uColor: { value: new Vector3(threeColor.r, threeColor.g, threeColor.b) },
|
||||||
|
uBrightness: { value: props.brightness },
|
||||||
|
uGamma: { value: props.gamma },
|
||||||
|
uDensity: { value: props.density },
|
||||||
|
uVariant: { value: props.variant === 'round' ? 1.0 : props.variant === 'snowflake' ? 2.0 : 0.0 },
|
||||||
|
uDirection: { value: (props.direction * Math.PI) / 180 }
|
||||||
|
},
|
||||||
|
transparent: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const geometry = new PlaneGeometry(2, 2);
|
||||||
|
scene.add(new Mesh(geometry, material));
|
||||||
|
|
||||||
|
const handleResize = () => {
|
||||||
|
const w = container.offsetWidth,
|
||||||
|
h = container.offsetHeight;
|
||||||
|
renderer.setSize(w, h);
|
||||||
|
material.uniforms.uResolution.value.set(w, h);
|
||||||
|
};
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
|
||||||
|
const startTime = performance.now();
|
||||||
|
const animate = () => {
|
||||||
|
animationRef.value = requestAnimationFrame(animate);
|
||||||
|
material.uniforms.uTime.value = (performance.now() - startTime) * 0.001;
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
};
|
||||||
|
animate();
|
||||||
|
|
||||||
|
cleanupFn = () => {
|
||||||
|
cancelAnimationFrame(animationRef.value);
|
||||||
|
window.removeEventListener('resize', handleResize);
|
||||||
|
container.removeChild(renderer.domElement);
|
||||||
|
renderer.dispose();
|
||||||
|
geometry.dispose();
|
||||||
|
material.dispose();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setupFn();
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
cleanupFn?.();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [
|
||||||
|
props.color,
|
||||||
|
props.flakeSize,
|
||||||
|
props.minFlakeSize,
|
||||||
|
props.pixelResolution,
|
||||||
|
props.speed,
|
||||||
|
props.depthFade,
|
||||||
|
props.farPlane,
|
||||||
|
props.brightness,
|
||||||
|
props.gamma,
|
||||||
|
props.density,
|
||||||
|
props.variant,
|
||||||
|
props.direction
|
||||||
|
],
|
||||||
|
() => {
|
||||||
|
cleanupFn?.();
|
||||||
|
setupFn();
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="containerRef" :class="['absolute inset-0 w-full h-full', className]" :style="style"></div>
|
||||||
|
</template>
|
||||||
170
src/demo/Backgrounds/PixelSnowDemo.vue
Normal file
170
src/demo/Backgrounds/PixelSnowDemo.vue
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<template>
|
||||||
|
<TabbedLayout>
|
||||||
|
<template #preview>
|
||||||
|
<div class="relative p-0 h-[600px] overflow-hidden demo-container">
|
||||||
|
<PixelSnow
|
||||||
|
:color="color"
|
||||||
|
:flake-size="flakeSize"
|
||||||
|
:min-flake-size="minFlakeSize"
|
||||||
|
:pixel-resolution="pixelResolution"
|
||||||
|
:speed="speed"
|
||||||
|
:depth-fade="depthFade"
|
||||||
|
:far-plane="farPlane"
|
||||||
|
:brightness="brightness"
|
||||||
|
:gamma="gamma"
|
||||||
|
:density="density"
|
||||||
|
:variant="variant"
|
||||||
|
:direction="direction"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<BackgroundContent pill-text="New Background" headline="Oh, the weather outside is frightful!" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Customize>
|
||||||
|
<PreviewSelect title="Variant" v-model="variant" :options="variantOptions" />
|
||||||
|
<PreviewColor title="Color" v-model="color" />
|
||||||
|
<PreviewSlider title="Pixel Resolution" v-model="pixelResolution" :min="50" :max="500" :step="10" />
|
||||||
|
<PreviewSlider title="Speed" v-model="speed" :min="0" :max="5" :step="0.1" />
|
||||||
|
<PreviewSlider title="Density" v-model="density" :min="0.1" :max="1" :step="0.05" />
|
||||||
|
<PreviewSlider title="Flake Size" v-model="flakeSize" :min="0.001" :max="0.05" :step="0.001" />
|
||||||
|
<PreviewSlider title="Brightness" v-model="brightness" :min="0.1" :max="3" :step="0.1" />
|
||||||
|
<PreviewSlider title="Depth Fade" v-model="depthFade" :min="1" :max="20" :step="0.5" />
|
||||||
|
<PreviewSlider title="Far Plane" v-model="farPlane" :min="5" :max="50" :step="1" />
|
||||||
|
<PreviewSlider title="Direction" v-model="direction" :min="0" :max="360" :step="5" />
|
||||||
|
</Customize>
|
||||||
|
|
||||||
|
<PropTable :data="propData" />
|
||||||
|
<Dependencies :dependency-list="['three']" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #code>
|
||||||
|
<CodeExample :code-object="pixelSnow" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #cli>
|
||||||
|
<CliInstallation :command="pixelSnow.cli" />
|
||||||
|
</template>
|
||||||
|
</TabbedLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { pixelSnow } from '@/constants/code/Backgrounds/pixelSnowCode';
|
||||||
|
import PixelSnow from '@/content/Backgrounds/PixelSnow/PixelSnow.vue';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import CliInstallation from '../../components/code/CliInstallation.vue';
|
||||||
|
import CodeExample from '../../components/code/CodeExample.vue';
|
||||||
|
import Dependencies from '../../components/code/Dependencies.vue';
|
||||||
|
import BackgroundContent from '../../components/common/BackgroundContent.vue';
|
||||||
|
import Customize from '../../components/common/Customize.vue';
|
||||||
|
import PreviewColor from '../../components/common/PreviewColor.vue';
|
||||||
|
import PreviewSelect from '../../components/common/PreviewSelect.vue';
|
||||||
|
import PreviewSlider from '../../components/common/PreviewSlider.vue';
|
||||||
|
import PropTable from '../../components/common/PropTable.vue';
|
||||||
|
import TabbedLayout from '../../components/common/TabbedLayout.vue';
|
||||||
|
|
||||||
|
const color = ref('#FFFFFF');
|
||||||
|
const flakeSize = ref(0.01);
|
||||||
|
const minFlakeSize = ref(1.25);
|
||||||
|
const pixelResolution = ref(200);
|
||||||
|
const speed = ref(1.25);
|
||||||
|
const depthFade = ref(8);
|
||||||
|
const farPlane = ref(20);
|
||||||
|
const brightness = ref(1);
|
||||||
|
const gamma = ref(0.4545);
|
||||||
|
const density = ref(0.3);
|
||||||
|
const variant = ref<'square' | 'round'>('square');
|
||||||
|
const direction = ref(125);
|
||||||
|
|
||||||
|
const variantOptions = [
|
||||||
|
{ label: 'Square', value: 'square' },
|
||||||
|
{ label: 'Round', value: 'round' },
|
||||||
|
{ label: 'Snowflake', value: 'snowflake' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const propData = [
|
||||||
|
{
|
||||||
|
name: 'color',
|
||||||
|
type: 'string',
|
||||||
|
default: '"#ffffff"',
|
||||||
|
description: 'Color of the snowflakes (hex or CSS color)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'flakeSize',
|
||||||
|
type: 'number',
|
||||||
|
default: '0.01',
|
||||||
|
description: 'Size of snowflakes in scene units'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'minFlakeSize',
|
||||||
|
type: 'number',
|
||||||
|
default: '1.25',
|
||||||
|
description: 'Minimum flake size in pixels on screen'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pixelResolution',
|
||||||
|
type: 'number',
|
||||||
|
default: '200',
|
||||||
|
description: 'Pixel resolution - lower values create larger pixels for a more retro look'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'speed',
|
||||||
|
type: 'number',
|
||||||
|
default: '1.25',
|
||||||
|
description: 'Animation speed multiplier'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'depthFade',
|
||||||
|
type: 'number',
|
||||||
|
default: '8',
|
||||||
|
description: 'Depth fade intensity - higher values make distant flakes fade faster'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'farPlane',
|
||||||
|
type: 'number',
|
||||||
|
default: '20',
|
||||||
|
description: 'Far plane distance for rendering - higher values show more distant flakes'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'brightness',
|
||||||
|
type: 'number',
|
||||||
|
default: '1',
|
||||||
|
description: 'Overall brightness multiplier'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'gamma',
|
||||||
|
type: 'number',
|
||||||
|
default: '0.4545',
|
||||||
|
description: 'Gamma correction value for final color output'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'density',
|
||||||
|
type: 'number',
|
||||||
|
default: '0.3',
|
||||||
|
description: 'Probability of snowflakes appearing (0-1) - lower values = fewer flakes'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'variant',
|
||||||
|
type: '"square" | "round" | "snowflake"',
|
||||||
|
default: '"square"',
|
||||||
|
description: 'Shape of the snowflakes - square, round, or snowflake pattern'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'direction',
|
||||||
|
type: 'number',
|
||||||
|
default: '125',
|
||||||
|
description: 'Wind direction angle in degrees (0-360)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'className',
|
||||||
|
type: 'string',
|
||||||
|
default: '""',
|
||||||
|
description: 'Additional CSS class name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'style',
|
||||||
|
type: 'object',
|
||||||
|
default: '{}',
|
||||||
|
description: 'Additional inline styles'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user