mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 06:29:30 -07:00
FEAT: 🎉 Added <LightPillar /> background
This commit is contained in:
BIN
public/assets/videos/lightpillar.mp4
Normal file
BIN
public/assets/videos/lightpillar.mp4
Normal file
Binary file not shown.
BIN
public/assets/videos/lightpillar.webm
Normal file
BIN
public/assets/videos/lightpillar.webm
Normal file
Binary file not shown.
@@ -19,10 +19,10 @@
|
||||
/>
|
||||
|
||||
<div class="hero-main-content">
|
||||
<router-link to="/backgrounds/floating-lines" class="hero-new-badge-container">
|
||||
<router-link to="/backgrounds/light-pillar" class="hero-new-badge-container">
|
||||
<span class="hero-new-badge">New 🎉</span>
|
||||
<div class="hero-new-badge-text">
|
||||
<span>Floating Lines</span>
|
||||
<span>Light Pillar</span>
|
||||
<i class="pi-arrow-right pi" style="font-size: 0.8rem"></i>
|
||||
</div>
|
||||
</router-link>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Highlighted sidebar items
|
||||
export const NEW = ['Color Bends', 'Ghost Cursor', 'Laser Flow', 'Liquid Ether', 'Pixel Blast', 'Floating Lines'];
|
||||
export const NEW = ['Color Bends', 'Ghost Cursor', 'Laser Flow', 'Liquid Ether', 'Pixel Blast', 'Floating Lines', 'Light Pillar'];
|
||||
export const UPDATED = [];
|
||||
|
||||
// Used for main sidebar navigation
|
||||
@@ -123,6 +123,7 @@ export const CATEGORIES = [
|
||||
'Iridescence',
|
||||
'Letter Glitch',
|
||||
'Lightning',
|
||||
'Light Pillar',
|
||||
'Light Rays',
|
||||
'Liquid Chrome',
|
||||
'Liquid Ether',
|
||||
|
||||
@@ -121,6 +121,7 @@ const backgrounds = {
|
||||
'liquid-ether': () => import('../demo/Backgrounds/LiquidEtherDemo.vue'),
|
||||
'color-bends': () => import('../demo/Backgrounds/ColorBendsDemo.vue'),
|
||||
'floating-lines': () => import('../demo/Backgrounds/FloatingLinesDemo.vue'),
|
||||
'light-pillar': () => import('../demo/Backgrounds/LightPillarDemo.vue'),
|
||||
};
|
||||
|
||||
export const componentMap = {
|
||||
|
||||
@@ -948,5 +948,13 @@ export const componentMetadata: ComponentMetadata = {
|
||||
name: 'LiquidEther',
|
||||
docsUrl: 'https://vue-bits.dev/backgrounds/liquid-ether',
|
||||
tags: []
|
||||
},
|
||||
'Backgrounds/LightPillar': {
|
||||
videoUrl: '/assets/videos/lightpillar.webm',
|
||||
description: 'Vertical pillar of light with glow effects.',
|
||||
category: 'Backgrounds',
|
||||
name: 'LightPillar',
|
||||
docsUrl: 'https://vue-bits.dev/backgrounds/light-pillar',
|
||||
tags: []
|
||||
}
|
||||
};
|
||||
|
||||
27
src/constants/code/Backgrounds/lightPillarCode.ts
Normal file
27
src/constants/code/Backgrounds/lightPillarCode.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import code from '@content/Backgrounds/LightPillar/LightPillar.vue?raw';
|
||||
import { createCodeObject } from '../../../types/code';
|
||||
|
||||
export const lightPillar = createCodeObject(code, 'Backgrounds/LightPillar', {
|
||||
installation: `npm install three`,
|
||||
usage: `<template>
|
||||
<div style="width: 100%; height: 600px; position: relative;">
|
||||
<LightPillar
|
||||
topColor="#48FF28"
|
||||
bottomColor="#9EF19E"
|
||||
:intensity="1.0"
|
||||
:rotationSpeed="0.3"
|
||||
:glowAmount="0.005"
|
||||
:pillarWidth="3.0"
|
||||
:pillarHeight="0.4"
|
||||
:noiseIntensity="0.5"
|
||||
:pillarRotation="0"
|
||||
:interactive="false"
|
||||
mixBlendMode="normal"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import LightPillar from './LightPillar.vue'
|
||||
</script>`
|
||||
});
|
||||
402
src/content/Backgrounds/LightPillar/LightPillar.vue
Normal file
402
src/content/Backgrounds/LightPillar/LightPillar.vue
Normal file
@@ -0,0 +1,402 @@
|
||||
<script setup lang="ts">
|
||||
import * as THREE from 'three';
|
||||
import {
|
||||
onBeforeMount,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
shallowRef,
|
||||
useTemplateRef,
|
||||
watch,
|
||||
type CSSProperties
|
||||
} from 'vue';
|
||||
|
||||
interface LightPillarProps {
|
||||
topColor?: string;
|
||||
bottomColor?: string;
|
||||
intensity?: number;
|
||||
rotationSpeed?: number;
|
||||
interactive?: boolean;
|
||||
className?: string;
|
||||
glowAmount?: number;
|
||||
pillarWidth?: number;
|
||||
pillarHeight?: number;
|
||||
noiseIntensity?: number;
|
||||
mixBlendMode?: CSSProperties['mixBlendMode'];
|
||||
pillarRotation?: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<LightPillarProps>(), {
|
||||
topColor: '#48FF28',
|
||||
bottomColor: '#9EF19E',
|
||||
intensity: 1.0,
|
||||
rotationSpeed: 0.3,
|
||||
interactive: false,
|
||||
className: '',
|
||||
glowAmount: 0.005,
|
||||
pillarWidth: 3.0,
|
||||
pillarHeight: 0.4,
|
||||
noiseIntensity: 0.5,
|
||||
mixBlendMode: 'screen',
|
||||
pillarRotation: 0
|
||||
});
|
||||
|
||||
const containerRef = useTemplateRef('containerRef');
|
||||
const rafRef = ref<number | null>(null);
|
||||
const rendererRef = shallowRef<THREE.WebGLRenderer | null>(null);
|
||||
const materialRef = shallowRef<THREE.ShaderMaterial | null>(null);
|
||||
const sceneRef = shallowRef<THREE.Scene | null>(null);
|
||||
const cameraRef = shallowRef<THREE.OrthographicCamera | null>(null);
|
||||
const geometryRef = shallowRef<THREE.PlaneGeometry | null>(null);
|
||||
const mouseRef = ref<THREE.Vector2>(new THREE.Vector2(0, 0));
|
||||
const timeRef = ref<number>(0);
|
||||
const webGLSupported = ref<boolean>(true);
|
||||
|
||||
onBeforeMount(() => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const gl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
|
||||
|
||||
if (!gl) {
|
||||
webGLSupported.value = false;
|
||||
console.warn('WebGL is not supported in this browser');
|
||||
}
|
||||
|
||||
canvas.remove();
|
||||
});
|
||||
|
||||
let cleanup: (() => void) | null = null;
|
||||
const setup = () => {
|
||||
if (!containerRef.value || !webGLSupported.value) return;
|
||||
|
||||
const container = containerRef.value;
|
||||
const width = container.clientWidth;
|
||||
const height = container.clientHeight;
|
||||
|
||||
// Scene setup
|
||||
const scene = new THREE.Scene();
|
||||
sceneRef.value = scene;
|
||||
const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
||||
cameraRef.value = camera;
|
||||
|
||||
let renderer: THREE.WebGLRenderer;
|
||||
try {
|
||||
renderer = new THREE.WebGLRenderer({
|
||||
antialias: false,
|
||||
alpha: true,
|
||||
powerPreference: 'high-performance',
|
||||
precision: 'lowp',
|
||||
stencil: false,
|
||||
depth: false
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to create WebGL renderer:', error);
|
||||
webGLSupported.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
renderer.setSize(width, height);
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||||
container.appendChild(renderer.domElement);
|
||||
rendererRef.value = renderer;
|
||||
|
||||
// Convert hex colors to RGB
|
||||
const parseColor = (hex: string): THREE.Vector3 => {
|
||||
const color = new THREE.Color(hex);
|
||||
return new THREE.Vector3(color.r, color.g, color.b);
|
||||
};
|
||||
|
||||
// Shader material
|
||||
const vertexShader = `
|
||||
varying vec2 vUv;
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = vec4(position, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentShader = `
|
||||
uniform float uTime;
|
||||
uniform vec2 uResolution;
|
||||
uniform vec2 uMouse;
|
||||
uniform vec3 uTopColor;
|
||||
uniform vec3 uBottomColor;
|
||||
uniform float uIntensity;
|
||||
uniform bool uInteractive;
|
||||
uniform float uGlowAmount;
|
||||
uniform float uPillarWidth;
|
||||
uniform float uPillarHeight;
|
||||
uniform float uNoiseIntensity;
|
||||
uniform float uPillarRotation;
|
||||
varying vec2 vUv;
|
||||
|
||||
const float PI = 3.141592653589793;
|
||||
const float EPSILON = 0.001;
|
||||
const float E = 2.71828182845904523536;
|
||||
const float HALF = 0.5;
|
||||
|
||||
mat2 rot(float angle) {
|
||||
float s = sin(angle);
|
||||
float c = cos(angle);
|
||||
return mat2(c, -s, s, c);
|
||||
}
|
||||
|
||||
// Procedural noise function
|
||||
float noise(vec2 coord) {
|
||||
float G = E;
|
||||
vec2 r = (G * sin(G * coord));
|
||||
return fract(r.x * r.y * (1.0 + coord.x));
|
||||
}
|
||||
|
||||
// Apply layered wave deformation to position
|
||||
vec3 applyWaveDeformation(vec3 pos, float timeOffset) {
|
||||
float frequency = 1.0;
|
||||
float amplitude = 1.0;
|
||||
vec3 deformed = pos;
|
||||
|
||||
for(float i = 0.0; i < 4.0; i++) {
|
||||
deformed.xz *= rot(0.4);
|
||||
float phase = timeOffset * i * 2.0;
|
||||
vec3 oscillation = cos(deformed.zxy * frequency - phase);
|
||||
deformed += oscillation * amplitude;
|
||||
frequency *= 2.0;
|
||||
amplitude *= HALF;
|
||||
}
|
||||
return deformed;
|
||||
}
|
||||
|
||||
// Polynomial smooth blending between two values
|
||||
float blendMin(float a, float b, float k) {
|
||||
float scaledK = k * 4.0;
|
||||
float h = max(scaledK - abs(a - b), 0.0);
|
||||
return min(a, b) - h * h * 0.25 / scaledK;
|
||||
}
|
||||
|
||||
float blendMax(float a, float b, float k) {
|
||||
return -blendMin(-a, -b, k);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 fragCoord = vUv * uResolution;
|
||||
vec2 uv = (fragCoord * 2.0 - uResolution) / uResolution.y;
|
||||
|
||||
// Apply 2D rotation to UV coordinates
|
||||
float rotAngle = uPillarRotation * PI / 180.0;
|
||||
uv *= rot(rotAngle);
|
||||
|
||||
vec3 origin = vec3(0.0, 0.0, -10.0);
|
||||
vec3 direction = normalize(vec3(uv, 1.0));
|
||||
|
||||
float maxDepth = 50.0;
|
||||
float depth = 0.1;
|
||||
|
||||
mat2 rotX = rot(uTime * 0.3);
|
||||
if(uInteractive && length(uMouse) > 0.0) {
|
||||
rotX = rot(uMouse.x * PI * 2.0);
|
||||
}
|
||||
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
for(float i = 0.0; i < 100.0; i++) {
|
||||
vec3 pos = origin + direction * depth;
|
||||
pos.xz *= rotX;
|
||||
|
||||
// Apply vertical scaling and wave deformation
|
||||
vec3 deformed = pos;
|
||||
deformed.y *= uPillarHeight;
|
||||
deformed = applyWaveDeformation(deformed + vec3(0.0, uTime, 0.0), uTime);
|
||||
|
||||
// Calculate distance field using cosine pattern
|
||||
vec2 cosinePair = cos(deformed.xz);
|
||||
float fieldDistance = length(cosinePair) - 0.2;
|
||||
|
||||
// Radial boundary constraint
|
||||
float radialBound = length(pos.xz) - uPillarWidth;
|
||||
fieldDistance = blendMax(radialBound, fieldDistance, 1.0);
|
||||
fieldDistance = abs(fieldDistance) * 0.15 + 0.01;
|
||||
|
||||
vec3 gradient = mix(uBottomColor, uTopColor, smoothstep(15.0, -15.0, pos.y));
|
||||
color += gradient * pow(1.0 / fieldDistance, 1.0);
|
||||
|
||||
if(fieldDistance < EPSILON || depth > maxDepth) break;
|
||||
depth += fieldDistance;
|
||||
}
|
||||
|
||||
// Normalize by pillar width to maintain consistent glow regardless of size
|
||||
float widthNormalization = uPillarWidth / 3.0;
|
||||
color = tanh(color * uGlowAmount / widthNormalization);
|
||||
|
||||
// Add noise postprocessing
|
||||
float rnd = noise(gl_FragCoord.xy);
|
||||
color -= rnd / 15.0 * uNoiseIntensity;
|
||||
|
||||
gl_FragColor = vec4(color * uIntensity, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
uniforms: {
|
||||
uTime: { value: 0 },
|
||||
uResolution: { value: new THREE.Vector2(width, height) },
|
||||
uMouse: { value: mouseRef.value },
|
||||
uTopColor: { value: parseColor(props.topColor) },
|
||||
uBottomColor: { value: parseColor(props.bottomColor) },
|
||||
uIntensity: { value: props.intensity },
|
||||
uInteractive: { value: props.interactive },
|
||||
uGlowAmount: { value: props.glowAmount },
|
||||
uPillarWidth: { value: props.pillarWidth },
|
||||
uPillarHeight: { value: props.pillarHeight },
|
||||
uNoiseIntensity: { value: props.noiseIntensity },
|
||||
uPillarRotation: { value: props.pillarRotation }
|
||||
},
|
||||
transparent: true,
|
||||
depthWrite: false,
|
||||
depthTest: false
|
||||
});
|
||||
materialRef.value = material;
|
||||
|
||||
const geometry = new THREE.PlaneGeometry(2, 2);
|
||||
geometryRef.value = geometry;
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
scene.add(mesh);
|
||||
|
||||
// Mouse interaction - throttled for performance
|
||||
let mouseMoveTimeout: number | null = null;
|
||||
const handleMouseMove = (event: MouseEvent) => {
|
||||
if (!props.interactive) return;
|
||||
|
||||
if (mouseMoveTimeout) return;
|
||||
|
||||
mouseMoveTimeout = window.setTimeout(() => {
|
||||
mouseMoveTimeout = null;
|
||||
}, 16); // ~60fps throttle
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
const x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
||||
const y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
||||
mouseRef.value.set(x, y);
|
||||
};
|
||||
|
||||
if (props.interactive) {
|
||||
container.addEventListener('mousemove', handleMouseMove, { passive: true });
|
||||
}
|
||||
|
||||
// Animation loop with fixed timestep
|
||||
let lastTime = performance.now();
|
||||
const targetFPS = 60;
|
||||
const frameTime = 1000 / targetFPS;
|
||||
|
||||
const animate = (currentTime: number) => {
|
||||
if (!materialRef.value || !rendererRef.value || !sceneRef.value || !cameraRef.value) return;
|
||||
|
||||
const deltaTime = currentTime - lastTime;
|
||||
|
||||
if (deltaTime >= frameTime) {
|
||||
timeRef.value += 0.016 * props.rotationSpeed;
|
||||
materialRef.value.uniforms.uTime.value = timeRef.value;
|
||||
rendererRef.value.render(sceneRef.value, cameraRef.value);
|
||||
lastTime = currentTime - (deltaTime % frameTime);
|
||||
}
|
||||
|
||||
rafRef.value = requestAnimationFrame(animate);
|
||||
};
|
||||
rafRef.value = requestAnimationFrame(animate);
|
||||
|
||||
// Handle resize with debouncing
|
||||
let resizeTimeout: number | null = null;
|
||||
const handleResize = () => {
|
||||
if (resizeTimeout) {
|
||||
clearTimeout(resizeTimeout);
|
||||
}
|
||||
|
||||
resizeTimeout = window.setTimeout(() => {
|
||||
if (!rendererRef.value || !materialRef.value || !containerRef.value) return;
|
||||
const newWidth = containerRef.value.clientWidth;
|
||||
const newHeight = containerRef.value.clientHeight;
|
||||
rendererRef.value.setSize(newWidth, newHeight);
|
||||
materialRef.value.uniforms.uResolution.value.set(newWidth, newHeight);
|
||||
}, 150);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize, { passive: true });
|
||||
|
||||
// Cleanup
|
||||
cleanup = () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (props.interactive) {
|
||||
container.removeEventListener('mousemove', handleMouseMove);
|
||||
}
|
||||
if (rafRef.value) {
|
||||
cancelAnimationFrame(rafRef.value);
|
||||
}
|
||||
if (rendererRef.value) {
|
||||
rendererRef.value.dispose();
|
||||
rendererRef.value.forceContextLoss();
|
||||
if (container.contains(rendererRef.value.domElement)) {
|
||||
container.removeChild(rendererRef.value.domElement);
|
||||
}
|
||||
}
|
||||
if (materialRef.value) {
|
||||
materialRef.value.dispose();
|
||||
}
|
||||
if (geometryRef.value) {
|
||||
geometryRef.value.dispose();
|
||||
}
|
||||
|
||||
rendererRef.value = null;
|
||||
materialRef.value = null;
|
||||
sceneRef.value = null;
|
||||
cameraRef.value = null;
|
||||
geometryRef.value = null;
|
||||
rafRef.value = null;
|
||||
};
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
setup();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
cleanup?.();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => [
|
||||
props.topColor,
|
||||
props.bottomColor,
|
||||
props.intensity,
|
||||
props.rotationSpeed,
|
||||
props.interactive,
|
||||
props.glowAmount,
|
||||
props.pillarWidth,
|
||||
props.pillarHeight,
|
||||
props.noiseIntensity,
|
||||
props.pillarRotation,
|
||||
webGLSupported.value
|
||||
],
|
||||
() => {
|
||||
cleanup?.();
|
||||
setup();
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="!webGLSupported"
|
||||
:class="`w-full h-full absolute top-0 left-0 flex items-center justify-center bg-black/10 text-gray-500 text-sm ${className}`"
|
||||
:style="{ mixBlendMode }"
|
||||
>
|
||||
WebGL not supported
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
ref="containerRef"
|
||||
:class="`w-full h-full absolute top-0 left-0 ${className}`"
|
||||
:style="{ mixBlendMode }"
|
||||
/>
|
||||
</template>
|
||||
161
src/demo/Backgrounds/LightPillarDemo.vue
Normal file
161
src/demo/Backgrounds/LightPillarDemo.vue
Normal file
@@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="relative p-0 h-[600px] overflow-hidden demo-container">
|
||||
<LightPillar
|
||||
:top-color="topColor"
|
||||
:bottom-color="bottomColor"
|
||||
:intensity="intensity"
|
||||
:rotation-speed="rotationSpeed"
|
||||
:interactive="interactive"
|
||||
:glow-amount="glowAmount"
|
||||
:pillar-width="pillarWidth"
|
||||
:pillar-height="pillarHeight"
|
||||
:noise-intensity="noiseIntensity"
|
||||
:pillar-rotation="pillarRotation"
|
||||
:mix-blend-mode="mixBlendMode"
|
||||
/>
|
||||
|
||||
<BackgroundContent pill-text="New Background" headline="Ethereal light pillar for your hero sections." />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewColor v-model="topColor" title="Top Color" />
|
||||
<PreviewColor v-model="bottomColor" title="Bottom Color" class="mt-4" />
|
||||
<PreviewSlider :min="0.1" :max="3" :step="0.1" v-model="intensity" title="Intensity" />
|
||||
<PreviewSlider :min="0" :max="2" :step="0.1" v-model="rotationSpeed" title="Rotation Speed" />
|
||||
<PreviewSlider :min="0.001" :max="0.02" :step="0.001" v-model="glowAmount" title="Glow Amount" />
|
||||
<PreviewSlider :min="1" :max="10" :step="0.1" v-model="pillarWidth" title="Pillar Width" />
|
||||
<PreviewSlider :min="0.1" :max="2" :step="0.1" v-model="pillarHeight" title="Pillar Height" />
|
||||
<PreviewSlider :min="0" :max="2" :step="0.1" v-model="noiseIntensity" title="Noise Intensity" />
|
||||
<PreviewSlider :min="0" :max="360" :step="1" v-model="pillarRotation" title="Pillar Rotation" />
|
||||
<PreviewSwitch title="Interactive Rotation" v-model="interactive" />
|
||||
<PreviewSelect :options="blendModeOptions" v-model="mixBlendMode" title="Mix Blend Mode" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['three']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="lightPillar" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="lightPillar.cli" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { lightPillar } from '@/constants/code/Backgrounds/lightPillarCode';
|
||||
import { ref, type CSSProperties } 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 PreviewSlider from '../../components/common/PreviewSlider.vue';
|
||||
import PreviewColor from '../../components/common/PreviewColor.vue';
|
||||
import PreviewSwitch from '../../components/common/PreviewSwitch.vue';
|
||||
import PreviewSelect from '../../components/common/PreviewSelect.vue';
|
||||
import PropTable from '../../components/common/PropTable.vue';
|
||||
import TabbedLayout from '../../components/common/TabbedLayout.vue';
|
||||
import LightPillar from '../../content/Backgrounds/LightPillar/LightPillar.vue';
|
||||
|
||||
const topColor = ref('#48FF28');
|
||||
const bottomColor = ref('#9EF19E');
|
||||
const intensity = ref(1.0);
|
||||
const rotationSpeed = ref(0.3);
|
||||
const interactive = ref(false);
|
||||
const glowAmount = ref(0.002);
|
||||
const pillarWidth = ref(3.0);
|
||||
const pillarHeight = ref(0.4);
|
||||
const noiseIntensity = ref(0.5);
|
||||
const pillarRotation = ref(25);
|
||||
const mixBlendMode = ref<CSSProperties['mixBlendMode']>('screen');
|
||||
|
||||
const blendModeOptions = [
|
||||
{ value: 'normal', label: 'Normal' },
|
||||
{ value: 'screen', label: 'Screen' },
|
||||
{ value: 'darken', label: 'Darken' },
|
||||
{ value: 'lighten', label: 'Lighten' },
|
||||
{ value: 'color-dodge', label: 'Color Dodge' },
|
||||
{ value: 'luminosity', label: 'Luminosity' }
|
||||
];
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'topColor',
|
||||
type: 'string',
|
||||
default: "'#48FF28'",
|
||||
description: 'Hex color string for the top gradient color of the light pillar.'
|
||||
},
|
||||
{
|
||||
name: 'bottomColor',
|
||||
type: 'string',
|
||||
default: "'#9EF19E'",
|
||||
description: 'Hex color string for the bottom gradient color of the light pillar.'
|
||||
},
|
||||
{
|
||||
name: 'intensity',
|
||||
type: 'number',
|
||||
default: '1.0',
|
||||
description: 'Controls the overall brightness and intensity of the effect.'
|
||||
},
|
||||
{
|
||||
name: 'rotationSpeed',
|
||||
type: 'number',
|
||||
default: '0.3',
|
||||
description: 'Speed multiplier for the pillar rotation animation.'
|
||||
},
|
||||
{
|
||||
name: 'interactive',
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Enable mouse interaction to control the pillar rotation.'
|
||||
},
|
||||
{
|
||||
name: 'glowAmount',
|
||||
type: 'number',
|
||||
default: '0.005',
|
||||
description: 'Controls the glow intensity and spread of the light effect.'
|
||||
},
|
||||
{
|
||||
name: 'pillarWidth',
|
||||
type: 'number',
|
||||
default: '3.0',
|
||||
description: 'Width/radius of the light pillar.'
|
||||
},
|
||||
{
|
||||
name: 'pillarHeight',
|
||||
type: 'number',
|
||||
default: '0.4',
|
||||
description: 'Height scaling factor for the pillar distortion.'
|
||||
},
|
||||
{
|
||||
name: 'noiseIntensity',
|
||||
type: 'number',
|
||||
default: '0.5',
|
||||
description: 'Intensity of the film grain noise postprocessing effect.'
|
||||
},
|
||||
{
|
||||
name: 'className',
|
||||
type: 'string',
|
||||
default: "''",
|
||||
description: 'Additional CSS class names to apply to the container element.'
|
||||
},
|
||||
{
|
||||
name: 'mixBlendMode',
|
||||
type: 'string',
|
||||
default: "'screen'",
|
||||
description: 'CSS mix-blend-mode property to control how the component blends with its background.'
|
||||
},
|
||||
{
|
||||
name: 'pillarRotation',
|
||||
type: 'number',
|
||||
default: '0',
|
||||
description: 'Rotation angle of the pillar in degrees (0-360).'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
Reference in New Issue
Block a user