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.
Starry Sea
bgThousands of little fish swimming upward in winding formations, with perspective depth and atmospheric fog.
Tech Keywords
Section titled “Tech Keywords”| Name | Description |
|---|---|
| Canvas Shader | Written in GLSL and executed directly on the GPU, faster than the Canvas 2D API but harder to master |
| Noise | Produces more natural randomness than Math.random, commonly used for terrain, clouds, and textures |
| WebGL2 Instanced Rendering | Draws large numbers of identical geometries at once via WebGL2 instanced drawing, greatly reducing draw calls |
| Simplex Noise | Improved Perlin Noise that computes faster without directional bias, commonly used in procedural generation |
| SDF (Signed Distance Field) | Describes shape outlines with mathematical functions, commonly used in shaders to draw smooth graphics |
Usage Examples
Section titled “Usage Examples”Basic Usage
Section titled “Basic Usage”<template> <div> <div class="grid grid-cols-2 gap-x-6 gap-y-2"> <base-checkbox v-model="enable" :label="t('enable')" class="example-ctrl col-span-2" />
<base-input v-model="options.fishSize" :label="`${t('fishSize')}: ${options.fishSize}`" type="range" :min="5" :max="60" :step="1" />
<base-input v-model="options.spread" :label="`${t('spread')}: ${options.spread}`" type="range" :min="0.02" :max="0.5" :step="0.01" />
<base-input v-model="options.riseSpeed" :label="`${t('riseSpeed')}: ${options.riseSpeed}`" type="range" :min="0" :max="0.002" :step="0.0001" />
<base-input v-model="options.meanderStrength" :label="`${t('meanderStrength')}: ${options.meanderStrength}`" type="range" :min="0" :max="0.01" :step="0.0005" />
<base-input v-model="options.followSpeed" :label="`${t('followSpeed')}: ${options.followSpeed}`" type="range" :min="0.005" :max="0.15" :step="0.005" />
<base-input v-model="options.turnRate" :label="`${t('turnRate')}: ${options.turnRate}`" type="range" :min="0.01" :max="0.2" :step="0.01" />
<base-input v-model="options.spreadCurve" :label="`${t('spreadCurve')}: ${options.spreadCurve}`" type="range" :min="0.5" :max="5" :step="0.5" /> </div>
<bg-starry-sea v-if="enable" class="pointer-events-none left-0 top-0 z-[99999] w-full h-lvh !fixed" :fish-size="Number(options.fishSize)" :spread="Number(options.spread)" :rise-speed="Number(options.riseSpeed)" :meander-strength="Number(options.meanderStrength)" :follow-speed="Number(options.followSpeed)" :turn-rate="Number(options.turnRate)" :spread-curve="Number(options.spreadCurve)" /> </div></template>
<script setup lang="ts">import { useMediaQuery } from '@vueuse/core'import { onMounted, onUnmounted, ref, watch } from 'vue'import { useI18n } from 'vue-i18n'import { useSiteDark } from '../../../composables/use-site-dark'import BaseCheckbox from '../../base-checkbox.vue'import BaseInput from '../../base-input.vue'import BgStarrySea from '../bg-starry-sea.vue'
const { t } = useI18n()
const enable = ref(false)
const isMobile = useMediaQuery('(max-width: 768px)')
const options = ref({ fishSize: 18, spread: 0.18, riseSpeed: 0.0005, meanderStrength: 0.003, followSpeed: 0.03, turnRate: 0.04, spreadCurve: 2,})
// 掛載後才依螢幕寬度調整,否則伺服器端渲染的數值與手機上不同,水合會對不上onMounted(() => { if (isMobile.value) options.value.fishSize = 10})
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”- Trail Path System: Multiple winding paths driven by Simplex Noise. Fish follow the path history in sequence, forming long dragon-like formations
- WebGL2 Instanced Rendering: All fish share the same vertex data. Position, angle, color and depth are passed via instance attributes, completing the render in a single draw call -- even 10,000 fish at once without breaking a sweat! (ゝ∀・)b
- SDF Fish Shape: An ellipse and rounded triangle SDF are combined in the fragment shader to form the fish silhouette, with a matching color glow effect
- Perspective Depth: Paths carry Z-axis depth variation. Distant fish shrink, compress spacing, slow down, and fade into the background color for an atmospheric fog effect
- Depth Sorting: Instance data is sorted by depth each frame, ensuring closer fish are drawn on top of distant ones
Source Code
Section titled “Source Code”interface Props { /** 魚的數量。@default 10000 */ fishCount?: number;
/** 魚的大小(px)。@default 18 */ fishSize?: number;
/** 長龍路徑數量。@default 6 */ trailCount?: number;
/** 路徑歷史步數,越長龍身越長。@default 2000 */ trailLength?: number;
/** 魚群散佈寬度(正規化空間)。@default 0.18 */ spread?: number;
/** 上升速度。@default 0.0005 */ riseSpeed?: number;
/** 路徑蜿蜒幅度。@default 0.003 */ meanderStrength?: number;
/** 魚跟隨路徑的平滑速度,0~1,越大越緊跟。@default 0.03 */ followSpeed?: number;
/** 路徑轉彎頻率,越小轉彎越平緩。@default 0.04 */ turnRate?: number;
/** 散佈集中度,越大魚群越集中在路徑中心,偶有離群魚。@default 2 */ spreadCurve?: number;}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!
Cod Lin thanks you! (´▽`ʃ💖ƪ)