Add jsrepo package and config
Add light ray component for footer
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="bg-black relative overflow-hidden">
|
<div class="bg-black relative overflow-hidden">
|
||||||
<NuxtImg
|
<!-- <NuxtImg
|
||||||
src="/img/footer-bg.jpg"
|
src="/img/footer-bg.jpg"
|
||||||
alt=""
|
alt=""
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
@@ -9,7 +9,16 @@
|
|||||||
:style="{
|
:style="{
|
||||||
mask: 'linear-gradient(to bottom, black 0%, rgba(0,0,0,0) 100%',
|
mask: 'linear-gradient(to bottom, black 0%, rgba(0,0,0,0) 100%',
|
||||||
}"
|
}"
|
||||||
|
/> -->
|
||||||
|
|
||||||
|
<ClientOnly>
|
||||||
|
<div class="absolute top-0 w-full max-md:scale-200 origin-top">
|
||||||
|
<LightRays
|
||||||
|
rays-color="#007bff"
|
||||||
|
:rays-speed="1.75"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</ClientOnly>
|
||||||
|
|
||||||
<div class="flex flex-col gap-4 items-center justify-center relative z-20">
|
<div class="flex flex-col gap-4 items-center justify-center relative z-20">
|
||||||
<div class="flex items-center flex-col gap-8 pb-[10px] pt-[80px] lg:pt-[100px] lg:pb-[50px] justify-center">
|
<div class="flex items-center flex-col gap-8 pb-[10px] pt-[80px] lg:pt-[100px] lg:pb-[50px] justify-center">
|
||||||
|
|||||||
@@ -0,0 +1,509 @@
|
|||||||
|
<!--
|
||||||
|
Installed from https://vue-bits.dev/ui/
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="containerRef" :class="['w-full h-full relative pointer-events-none z-[3] overflow-hidden', className]" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, onUnmounted, watch, useTemplateRef, computed, nextTick } from 'vue';
|
||||||
|
import { Renderer, Program, Triangle, Mesh } from 'ogl';
|
||||||
|
|
||||||
|
export type RaysOrigin =
|
||||||
|
| 'top-center'
|
||||||
|
| 'top-left'
|
||||||
|
| 'top-right'
|
||||||
|
| 'right'
|
||||||
|
| 'left'
|
||||||
|
| 'bottom-center'
|
||||||
|
| 'bottom-right'
|
||||||
|
| 'bottom-left';
|
||||||
|
|
||||||
|
interface LightRaysProps {
|
||||||
|
raysOrigin?: RaysOrigin;
|
||||||
|
raysColor?: string;
|
||||||
|
raysSpeed?: number;
|
||||||
|
lightSpread?: number;
|
||||||
|
rayLength?: number;
|
||||||
|
pulsating?: boolean;
|
||||||
|
fadeDistance?: number;
|
||||||
|
saturation?: number;
|
||||||
|
followMouse?: boolean;
|
||||||
|
mouseInfluence?: number;
|
||||||
|
noiseAmount?: number;
|
||||||
|
distortion?: number;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MousePosition {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AnchorAndDirection {
|
||||||
|
anchor: [number, number];
|
||||||
|
dir: [number, number];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WebGLUniforms {
|
||||||
|
iTime: { value: number };
|
||||||
|
iResolution: { value: [number, number] };
|
||||||
|
rayPos: { value: [number, number] };
|
||||||
|
rayDir: { value: [number, number] };
|
||||||
|
raysColor: { value: [number, number, number] };
|
||||||
|
raysSpeed: { value: number };
|
||||||
|
lightSpread: { value: number };
|
||||||
|
rayLength: { value: number };
|
||||||
|
pulsating: { value: number };
|
||||||
|
fadeDistance: { value: number };
|
||||||
|
saturation: { value: number };
|
||||||
|
mousePos: { value: [number, number] };
|
||||||
|
mouseInfluence: { value: number };
|
||||||
|
noiseAmount: { value: number };
|
||||||
|
distortion: { value: number };
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<LightRaysProps>(), {
|
||||||
|
raysOrigin: 'top-center',
|
||||||
|
raysColor: '#ffffff',
|
||||||
|
raysSpeed: 1,
|
||||||
|
lightSpread: 1,
|
||||||
|
rayLength: 2,
|
||||||
|
pulsating: false,
|
||||||
|
fadeDistance: 1.0,
|
||||||
|
saturation: 1.0,
|
||||||
|
followMouse: true,
|
||||||
|
mouseInfluence: 0.1,
|
||||||
|
noiseAmount: 0.0,
|
||||||
|
distortion: 0.0,
|
||||||
|
className: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const containerRef = useTemplateRef<HTMLDivElement>('containerRef');
|
||||||
|
|
||||||
|
const uniformsRef = ref<WebGLUniforms | null>(null);
|
||||||
|
const rendererRef = ref<Renderer | null>(null);
|
||||||
|
const mouseRef = ref<MousePosition>({ x: 0.5, y: 0.5 });
|
||||||
|
const smoothMouseRef = ref<MousePosition>({ x: 0.5, y: 0.5 });
|
||||||
|
const animationIdRef = ref<number | null>(null);
|
||||||
|
const meshRef = ref<Mesh | null>(null);
|
||||||
|
const cleanupFunctionRef = ref<(() => void) | null>(null);
|
||||||
|
const isVisible = ref<boolean>(false);
|
||||||
|
const observerRef = ref<IntersectionObserver | null>(null);
|
||||||
|
const resizeTimeoutRef = ref<number | null>(null);
|
||||||
|
|
||||||
|
const rgbColor = computed<[number, number, number]>(() => hexToRgb(props.raysColor));
|
||||||
|
const pulsatingValue = computed<number>(() => (props.pulsating ? 1.0 : 0.0));
|
||||||
|
const devicePixelRatio = computed<number>(() => Math.min(window.devicePixelRatio || 1, 2));
|
||||||
|
|
||||||
|
const hexToRgb = (hex: string): [number, number, number] => {
|
||||||
|
const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||||
|
return m ? [parseInt(m[1], 16) / 255, parseInt(m[2], 16) / 255, parseInt(m[3], 16) / 255] : [1, 1, 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAnchorAndDir = (origin: RaysOrigin, w: number, h: number): AnchorAndDirection => {
|
||||||
|
const outside = 0.2;
|
||||||
|
switch (origin) {
|
||||||
|
case 'top-left':
|
||||||
|
return { anchor: [0, -outside * h], dir: [0, 1] };
|
||||||
|
case 'top-right':
|
||||||
|
return { anchor: [w, -outside * h], dir: [0, 1] };
|
||||||
|
case 'left':
|
||||||
|
return { anchor: [-outside * w, 0.5 * h], dir: [1, 0] };
|
||||||
|
case 'right':
|
||||||
|
return { anchor: [(1 + outside) * w, 0.5 * h], dir: [-1, 0] };
|
||||||
|
case 'bottom-left':
|
||||||
|
return { anchor: [0, (1 + outside) * h], dir: [0, -1] };
|
||||||
|
case 'bottom-center':
|
||||||
|
return { anchor: [0.5 * w, (1 + outside) * h], dir: [0, -1] };
|
||||||
|
case 'bottom-right':
|
||||||
|
return { anchor: [w, (1 + outside) * h], dir: [0, -1] };
|
||||||
|
default:
|
||||||
|
return { anchor: [0.5 * w, -outside * h], dir: [0, 1] };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const debouncedUpdatePlacement = (() => {
|
||||||
|
let timeoutId: number | null = null;
|
||||||
|
|
||||||
|
return (updateFn: () => void): void => {
|
||||||
|
if (timeoutId !== null) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
timeoutId = window.setTimeout(() => {
|
||||||
|
updateFn();
|
||||||
|
timeoutId = null;
|
||||||
|
}, 16);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
const vertexShader: string = `
|
||||||
|
attribute vec2 position;
|
||||||
|
varying vec2 vUv;
|
||||||
|
void main() {
|
||||||
|
vUv = position * 0.5 + 0.5;
|
||||||
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const fragmentShader: string = `precision highp float;
|
||||||
|
|
||||||
|
uniform float iTime;
|
||||||
|
uniform vec2 iResolution;
|
||||||
|
|
||||||
|
uniform vec2 rayPos;
|
||||||
|
uniform vec2 rayDir;
|
||||||
|
uniform vec3 raysColor;
|
||||||
|
uniform float raysSpeed;
|
||||||
|
uniform float lightSpread;
|
||||||
|
uniform float rayLength;
|
||||||
|
uniform float pulsating;
|
||||||
|
uniform float fadeDistance;
|
||||||
|
uniform float saturation;
|
||||||
|
uniform vec2 mousePos;
|
||||||
|
uniform float mouseInfluence;
|
||||||
|
uniform float noiseAmount;
|
||||||
|
uniform float distortion;
|
||||||
|
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
float noise(vec2 st) {
|
||||||
|
return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
|
||||||
|
}
|
||||||
|
|
||||||
|
float rayStrength(vec2 raySource, vec2 rayRefDirection, vec2 coord,
|
||||||
|
float seedA, float seedB, float speed) {
|
||||||
|
vec2 sourceToCoord = coord - raySource;
|
||||||
|
vec2 dirNorm = normalize(sourceToCoord);
|
||||||
|
float cosAngle = dot(dirNorm, rayRefDirection);
|
||||||
|
|
||||||
|
float distortedAngle = cosAngle + distortion * sin(iTime * 2.0 + length(sourceToCoord) * 0.01) * 0.2;
|
||||||
|
|
||||||
|
float spreadFactor = pow(max(distortedAngle, 0.0), 1.0 / max(lightSpread, 0.001));
|
||||||
|
|
||||||
|
float distance = length(sourceToCoord);
|
||||||
|
float maxDistance = iResolution.x * rayLength;
|
||||||
|
float lengthFalloff = clamp((maxDistance - distance) / maxDistance, 0.0, 1.0);
|
||||||
|
|
||||||
|
float fadeFalloff = clamp((iResolution.x * fadeDistance - distance) / (iResolution.x * fadeDistance), 0.5, 1.0);
|
||||||
|
float pulse = pulsating > 0.5 ? (0.8 + 0.2 * sin(iTime * speed * 3.0)) : 1.0;
|
||||||
|
|
||||||
|
float baseStrength = clamp(
|
||||||
|
(0.45 + 0.15 * sin(distortedAngle * seedA + iTime * speed)) +
|
||||||
|
(0.3 + 0.2 * cos(-distortedAngle * seedB + iTime * speed)),
|
||||||
|
0.0, 1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
return baseStrength * lengthFalloff * fadeFalloff * spreadFactor * pulse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
||||||
|
vec2 coord = vec2(fragCoord.x, iResolution.y - fragCoord.y);
|
||||||
|
|
||||||
|
vec2 finalRayDir = rayDir;
|
||||||
|
if (mouseInfluence > 0.0) {
|
||||||
|
vec2 mouseScreenPos = mousePos * iResolution.xy;
|
||||||
|
vec2 mouseDirection = normalize(mouseScreenPos - rayPos);
|
||||||
|
finalRayDir = normalize(mix(rayDir, mouseDirection, mouseInfluence));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 rays1 = vec4(1.0) *
|
||||||
|
rayStrength(rayPos, finalRayDir, coord, 36.2214, 21.11349,
|
||||||
|
1.5 * raysSpeed);
|
||||||
|
vec4 rays2 = vec4(1.0) *
|
||||||
|
rayStrength(rayPos, finalRayDir, coord, 22.3991, 18.0234,
|
||||||
|
1.1 * raysSpeed);
|
||||||
|
|
||||||
|
fragColor = rays1 * 0.5 + rays2 * 0.4;
|
||||||
|
|
||||||
|
if (noiseAmount > 0.0) {
|
||||||
|
float n = noise(coord * 0.01 + iTime * 0.1);
|
||||||
|
fragColor.rgb *= (1.0 - noiseAmount + noiseAmount * n);
|
||||||
|
}
|
||||||
|
|
||||||
|
float brightness = 1.0 - (coord.y / iResolution.y);
|
||||||
|
fragColor.x *= 0.1 + brightness * 0.8;
|
||||||
|
fragColor.y *= 0.3 + brightness * 0.6;
|
||||||
|
fragColor.z *= 0.5 + brightness * 0.5;
|
||||||
|
|
||||||
|
if (saturation != 1.0) {
|
||||||
|
float gray = dot(fragColor.rgb, vec3(0.299, 0.587, 0.114));
|
||||||
|
fragColor.rgb = mix(vec3(gray), fragColor.rgb, saturation);
|
||||||
|
}
|
||||||
|
|
||||||
|
fragColor.rgb *= raysColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 color;
|
||||||
|
mainImage(color, gl_FragCoord.xy);
|
||||||
|
gl_FragColor = color;
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const initializeWebGL = async (): Promise<void> => {
|
||||||
|
if (!containerRef.value) return;
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
if (!containerRef.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const renderer = new Renderer({
|
||||||
|
dpr: devicePixelRatio.value,
|
||||||
|
alpha: true,
|
||||||
|
antialias: false,
|
||||||
|
powerPreference: 'high-performance'
|
||||||
|
});
|
||||||
|
rendererRef.value = renderer;
|
||||||
|
|
||||||
|
const gl = renderer.gl;
|
||||||
|
gl.canvas.style.width = '100%';
|
||||||
|
gl.canvas.style.height = '100%';
|
||||||
|
|
||||||
|
while (containerRef.value.firstChild) {
|
||||||
|
containerRef.value.removeChild(containerRef.value.firstChild);
|
||||||
|
}
|
||||||
|
containerRef.value.appendChild(gl.canvas);
|
||||||
|
|
||||||
|
const uniforms: WebGLUniforms = {
|
||||||
|
iTime: { value: 0 },
|
||||||
|
iResolution: { value: [1, 1] },
|
||||||
|
rayPos: { value: [0, 0] },
|
||||||
|
rayDir: { value: [0, 1] },
|
||||||
|
raysColor: { value: rgbColor.value },
|
||||||
|
raysSpeed: { value: props.raysSpeed },
|
||||||
|
lightSpread: { value: props.lightSpread },
|
||||||
|
rayLength: { value: props.rayLength },
|
||||||
|
pulsating: { value: pulsatingValue.value },
|
||||||
|
fadeDistance: { value: props.fadeDistance },
|
||||||
|
saturation: { value: props.saturation },
|
||||||
|
mousePos: { value: [0.5, 0.5] },
|
||||||
|
mouseInfluence: { value: props.mouseInfluence },
|
||||||
|
noiseAmount: { value: props.noiseAmount },
|
||||||
|
distortion: { value: props.distortion }
|
||||||
|
};
|
||||||
|
uniformsRef.value = uniforms;
|
||||||
|
|
||||||
|
const geometry = new Triangle(gl);
|
||||||
|
const program = new Program(gl, {
|
||||||
|
vertex: vertexShader,
|
||||||
|
fragment: fragmentShader,
|
||||||
|
uniforms
|
||||||
|
});
|
||||||
|
const mesh = new Mesh(gl, { geometry, program });
|
||||||
|
meshRef.value = mesh;
|
||||||
|
|
||||||
|
const updatePlacement = (): void => {
|
||||||
|
if (!containerRef.value || !renderer) return;
|
||||||
|
|
||||||
|
renderer.dpr = devicePixelRatio.value;
|
||||||
|
|
||||||
|
const { clientWidth: wCSS, clientHeight: hCSS } = containerRef.value;
|
||||||
|
renderer.setSize(wCSS, hCSS);
|
||||||
|
|
||||||
|
const dpr = renderer.dpr;
|
||||||
|
const w = wCSS * dpr;
|
||||||
|
const h = hCSS * dpr;
|
||||||
|
|
||||||
|
uniforms.iResolution.value = [w, h];
|
||||||
|
|
||||||
|
const { anchor, dir } = getAnchorAndDir(props.raysOrigin, w, h);
|
||||||
|
uniforms.rayPos.value = anchor;
|
||||||
|
uniforms.rayDir.value = dir;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loop = (t: number): void => {
|
||||||
|
if (!rendererRef.value || !uniformsRef.value || !meshRef.value || !isVisible.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uniforms.iTime.value = t * 0.001;
|
||||||
|
|
||||||
|
if (props.followMouse && props.mouseInfluence > 0.0) {
|
||||||
|
const smoothing = 0.92;
|
||||||
|
|
||||||
|
smoothMouseRef.value.x = smoothMouseRef.value.x * smoothing + mouseRef.value.x * (1 - smoothing);
|
||||||
|
smoothMouseRef.value.y = smoothMouseRef.value.y * smoothing + mouseRef.value.y * (1 - smoothing);
|
||||||
|
|
||||||
|
uniforms.mousePos.value = [smoothMouseRef.value.x, smoothMouseRef.value.y];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
renderer.render({ scene: mesh });
|
||||||
|
animationIdRef.value = requestAnimationFrame(loop);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('WebGL rendering error:', error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResize = (): void => {
|
||||||
|
debouncedUpdatePlacement(updatePlacement);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('resize', handleResize, { passive: true });
|
||||||
|
updatePlacement();
|
||||||
|
animationIdRef.value = requestAnimationFrame(loop);
|
||||||
|
|
||||||
|
cleanupFunctionRef.value = (): void => {
|
||||||
|
if (animationIdRef.value) {
|
||||||
|
cancelAnimationFrame(animationIdRef.value);
|
||||||
|
animationIdRef.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.removeEventListener('resize', handleResize);
|
||||||
|
|
||||||
|
if (resizeTimeoutRef.value) {
|
||||||
|
clearTimeout(resizeTimeoutRef.value);
|
||||||
|
resizeTimeoutRef.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderer) {
|
||||||
|
try {
|
||||||
|
const canvas = renderer.gl.canvas;
|
||||||
|
const loseContextExt = renderer.gl.getExtension('WEBGL_lose_context');
|
||||||
|
if (loseContextExt) {
|
||||||
|
loseContextExt.loseContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canvas && canvas.parentNode) {
|
||||||
|
canvas.parentNode.removeChild(canvas);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Error during WebGL cleanup:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rendererRef.value = null;
|
||||||
|
uniformsRef.value = null;
|
||||||
|
meshRef.value = null;
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize WebGL:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mouseThrottleId: number | null = null;
|
||||||
|
const handleMouseMove = (e: MouseEvent): void => {
|
||||||
|
if (!containerRef.value || !rendererRef.value) return;
|
||||||
|
|
||||||
|
if (mouseThrottleId) return;
|
||||||
|
|
||||||
|
mouseThrottleId = requestAnimationFrame(() => {
|
||||||
|
if (!containerRef.value) return;
|
||||||
|
|
||||||
|
const rect = containerRef.value.getBoundingClientRect();
|
||||||
|
const x = (e.clientX - rect.left) / rect.width;
|
||||||
|
const y = (e.clientY - rect.top) / rect.height;
|
||||||
|
mouseRef.value = { x, y };
|
||||||
|
mouseThrottleId = null;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted((): void => {
|
||||||
|
if (!containerRef.value) return;
|
||||||
|
|
||||||
|
observerRef.value = new IntersectionObserver(
|
||||||
|
(entries: IntersectionObserverEntry[]): void => {
|
||||||
|
const entry = entries[0];
|
||||||
|
isVisible.value = entry.isIntersecting;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
threshold: 0.1,
|
||||||
|
rootMargin: '50px'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
observerRef.value.observe(containerRef.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isVisible, (newVisible: boolean): void => {
|
||||||
|
if (newVisible && containerRef.value) {
|
||||||
|
if (cleanupFunctionRef.value) {
|
||||||
|
cleanupFunctionRef.value();
|
||||||
|
cleanupFunctionRef.value = null;
|
||||||
|
}
|
||||||
|
initializeWebGL();
|
||||||
|
} else if (!newVisible && cleanupFunctionRef.value) {
|
||||||
|
if (animationIdRef.value) {
|
||||||
|
cancelAnimationFrame(animationIdRef.value);
|
||||||
|
animationIdRef.value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[
|
||||||
|
() => props.raysColor,
|
||||||
|
() => props.raysSpeed,
|
||||||
|
() => props.lightSpread,
|
||||||
|
() => props.raysOrigin,
|
||||||
|
() => props.rayLength,
|
||||||
|
() => props.pulsating,
|
||||||
|
() => props.fadeDistance,
|
||||||
|
() => props.saturation,
|
||||||
|
() => props.mouseInfluence,
|
||||||
|
() => props.noiseAmount,
|
||||||
|
() => props.distortion
|
||||||
|
],
|
||||||
|
(): void => {
|
||||||
|
if (!uniformsRef.value || !containerRef.value || !rendererRef.value) return;
|
||||||
|
|
||||||
|
const u = uniformsRef.value;
|
||||||
|
const renderer = rendererRef.value;
|
||||||
|
|
||||||
|
u.raysColor.value = rgbColor.value;
|
||||||
|
u.raysSpeed.value = props.raysSpeed;
|
||||||
|
u.lightSpread.value = props.lightSpread;
|
||||||
|
u.rayLength.value = props.rayLength;
|
||||||
|
u.pulsating.value = pulsatingValue.value;
|
||||||
|
u.fadeDistance.value = props.fadeDistance;
|
||||||
|
u.saturation.value = props.saturation;
|
||||||
|
u.mouseInfluence.value = props.mouseInfluence;
|
||||||
|
u.noiseAmount.value = props.noiseAmount;
|
||||||
|
u.distortion.value = props.distortion;
|
||||||
|
|
||||||
|
const { clientWidth: wCSS, clientHeight: hCSS } = containerRef.value;
|
||||||
|
const dpr = renderer.dpr;
|
||||||
|
const { anchor, dir } = getAnchorAndDir(props.raysOrigin, wCSS * dpr, hCSS * dpr);
|
||||||
|
u.rayPos.value = anchor;
|
||||||
|
u.rayDir.value = dir;
|
||||||
|
},
|
||||||
|
{ flush: 'post' }
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.followMouse,
|
||||||
|
(newFollowMouse: boolean): void => {
|
||||||
|
if (newFollowMouse) {
|
||||||
|
window.addEventListener('mousemove', handleMouseMove, { passive: true });
|
||||||
|
} else {
|
||||||
|
window.removeEventListener('mousemove', handleMouseMove);
|
||||||
|
if (mouseThrottleId) {
|
||||||
|
cancelAnimationFrame(mouseThrottleId);
|
||||||
|
mouseThrottleId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
onUnmounted((): void => {
|
||||||
|
if (observerRef.value) {
|
||||||
|
observerRef.value.disconnect();
|
||||||
|
observerRef.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cleanupFunctionRef.value) {
|
||||||
|
cleanupFunctionRef.value();
|
||||||
|
cleanupFunctionRef.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mouseThrottleId) {
|
||||||
|
cancelAnimationFrame(mouseThrottleId);
|
||||||
|
mouseThrottleId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.removeEventListener('mousemove', handleMouseMove);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://unpkg.com/jsrepo@2.5.0/schemas/project-config.json",
|
||||||
|
"repos": [
|
||||||
|
"https://vue-bits.dev/ui"
|
||||||
|
],
|
||||||
|
"includeTests": false,
|
||||||
|
"includeDocs": false,
|
||||||
|
"watermark": true,
|
||||||
|
"configFiles": {},
|
||||||
|
"paths": {
|
||||||
|
"*": "./components/global/VueBits"
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+88
-4067
File diff suppressed because it is too large
Load Diff
@@ -37,6 +37,7 @@
|
|||||||
"jalali-ts": "^8.0.0",
|
"jalali-ts": "^8.0.0",
|
||||||
"motion-v": "^1.1.1",
|
"motion-v": "^1.1.1",
|
||||||
"nuxt": "^3.15.4",
|
"nuxt": "^3.15.4",
|
||||||
|
"ogl": "^1.0.11",
|
||||||
"reka-ui": "^1.0.0-alpha.6",
|
"reka-ui": "^1.0.0-alpha.6",
|
||||||
"sanitize-html": "^2.15.0",
|
"sanitize-html": "^2.15.0",
|
||||||
"swiper": "^11.2.6",
|
"swiper": "^11.2.6",
|
||||||
|
|||||||
Reference in New Issue
Block a user