Загрузка...
WebGL-эффект грозового фона с настройкой цвета, интенсивности, скорости и масштаба. Анимация на GLSL.
# Lightning Background
You are given a task to integrate an existing React component in the codebase
~~~/README.md
# LightningBackground
A high-performance WebGL-powered atmospheric lightning background effect. Built with GLSL shaders for smooth, fluid electric discharge animations.
## Dependencies
- `react`: ^18.0.0
- `lucide-react`: ^0.454.0
## Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `hue` | `number` | `230` | The hue of the lightning in degrees (0-360). |
| `xOffset` | `number` | `0` | Horizontal displacement of the lightning bolt. |
| `speed` | `number` | `1.0` | Animation speed multiplier. |
| `intensity` | `number` | `1.0` | Brightness and glow intensity. |
| `size` | `number` | `1.0` | Scale and complexity of the noise pattern. |
| `className` | `string` | `""` | Optional CSS classes for the canvas container. |
## Usage
```tsx
import { Lightning } from '@/sd-components/5a42589e-b96a-43a8-9dde-7ec4e869715b';
export default function MyComponent() {
return (
<div style={{ width: '100%', height: '500px', position: 'relative' }}>
<Lightning
hue={210}
speed={1.2}
intensity={1.5}
/>
</div>
);
}
```
~~~
~~~/src/App.tsx
/**
* Demo application showcasing the LightningBackground component.
* Displays a fullscreen atmospheric lightning effect with minimal UI.
*/
import React from 'react';
import { Lightning } from './Component';
export default function App() {
return (
<div className="relative w-full h-screen overflow-hidden bg-[#1A1A1B]">
{/* Background Layer */}
<div className="absolute inset-0 z-0">
<Lightning
hue={210}
speed={0.8}
intensity={1.2}
size={0.9}
/>
</div>
{/* Content Overlay */}
<div className="absolute inset-0 z-10 flex flex-col items-center justify-center p-8 bg-gradient-to-t from-[#1A1A1B]/40 to-transparent">
<div className="max-w-2xl text-center space-y-6">
<h1 className="text-5xl md:text-7xl font-medium tracking-tight text-[#F9F9F9] drop-shadow-2xl">
Lightning Discharge
</h1>
<p className="text-lg text-[#F9F9F9]/60 font-normal">
Atmospheric WebGL background rendering with fluid noise dynamics.
</p>
<div className="pt-8">
<button
className="px-8 py-3 bg-[#F9F9F9] text-[#1A1A1B] rounded-full font-medium transition-all hover:scale-105 active:scale-95 shadow-xl"
onClick={() => alert('Reply Action')}
>
Reply
</button>
</div>
</div>
</div>
{/* Aesthetic Framing */}
<div className="absolute inset-0 pointer-events-none border-[1px] border-[#F9F9F9]/5 m-4 rounded-3xl" />
</div>
);
}
~~~
~~~/package.json
{
"name": "lightning-background",
"description": "WebGL atmospheric lightning background",
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"lucide-react": "^0.454.0"
}
}
~~~
~~~/src/Component.tsx
/**
* Lightning Component
* A high-performance WebGL-powered electric discharge effect.
* Features customizable hue, intensity, speed, and size via GLSL shaders.
*/
import React, { useRef, useEffect } from 'react';
export interface LightningProps {
/** Hue of the lightning in degrees (0-360) */
hue?: number;
/** Horizontal offset of the effect */
xOffset?: number;
/** Animation speed multiplier */
speed?: number;
/** Visual intensity/brightness multiplier */
intensity?: number;
/** Scale/complexity of the noise pattern */
size?: number;
/** Optional className for the canvas container */
className?: string;
}
export const Lightning: React.FC<LightningProps> = ({
hue = 230,
xOffset = 0,
speed = 1,
intensity = 1,
size = 1,
className = ""
}) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const gl = canvas.getContext('webgl', { antialias: true, alpha: true });
if (!gl) {
console.error('WebGL not supported');
return;
}
const resizeCanvas = () => {
const displayWidth = canvas.clientWidth;
const displayHeight = canvas.clientHeight;
if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
canvas.width = displayWidth;
canvas.height = displayHeight;
gl.viewport(0, 0, canvas.width, canvas.height);
}
};
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
const vertexShaderSource = `
attribute vec2 aPosition;
void main() {
gl_Position = vec4(aPosition, 0.0, 1.0);
}
`;
const fragmentShaderSource = `
precision mediump float;
uniform vec2 iResolution;
uniform float iTime;
uniform float uHue;
uniform float uXOffset;
uniform float uSpeed;
uniform float uIntensity;
uniform float uSize;
#define OCTAVE_COUNT 10
vec3 hsv2rgb(vec3 c) {
vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0,4.0,2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
return c.z * mix(vec3(1.0), rgb, c.y);
}
float hash11(float p) {
p = fract(p * .1031);
p *= p + 33.33;
p *= p + p;
return fract(p);
}
float hash12(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * .1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
mat2 rotate2d(float theta) {
float c = cos(theta);
float s = sin(theta);
return mat2(c, -s, s, c);
}
float noise(vec2 p) {
vec2 ip = floor(p);
vec2 fp = fract(p);
float a = hash12(ip);
float b = hash12(ip + vec2(1.0, 0.0));
float c = hash12(ip + vec2(0.0, 1.0));
float d = hash12(ip + vec2(1.0, 1.0));
vec2 t = smoothstep(0.0, 1.0, fp);
return mix(mix(a, b, t.x), mix(c, d, t.x), t.y);
}
float fbm(vec2 p) {
float value = 0.0;
float amplitude = 0.5;
for (int i = 0; i < OCTAVE_COUNT; ++i) {
value += amplitude * noise(p);
p *= rotate2d(0.45);
p *= 2.0;
amplitude *= 0.5;
}
return value;
}
void main() {
vec2 uv = gl_FragCoord.xy / iResolution.xy;
uv = 2.0 * uv - 1.0;
uv.x *= iResolution.x / iResolution.y;
uv.x += uXOffset;
float time = iTime * uSpeed;
// Warp UV coordinates with FBM for lightning shape
uv += 2.0 * fbm(uv * uSize + 0.8 * time) - 1.0;
float dist = abs(uv.x);
// Color based on hue and flickering
vec3 baseColor = hsv2rgb(vec3(uHue / 360.0, 0.7, 0.8));
// Flickering effect
float flicker = mix(0.0, 0.07, hash11(time));
// Core discharge effect
vec3 col = baseColor * (flicker / max(dist, 0.001)) * uIntensity;
// Glow and falloff
col = pow(col, vec3(1.1));
gl_FragColor = vec4(col, 1.0);
}
`;
const compileShader = (source: string, type: number): WebGLShader | null => {
const shader = gl.createShader(type);
if (!shader) return null;
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('Shader compile error:', gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
};
const vertexShader = compileShader(vertexShaderSource, gl.VERTEX_SHADER);
const fragmentShader = compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER);
if (!vertexShader || !fragmentShader) return;
const program = gl.createProgram();
if (!program) return;
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program linking error:', gl.getProgramInfoLog(program));
return;
}
gl.useProgram(program);
const vertices = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const aPosition = gl.getAttribLocation(program, 'aPosition');
gl.enableVertexAttribArray(aPosition);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
const uniforms = {
iResolution: gl.getUniformLocation(program, 'iResolution'),
iTime: gl.getUniformLocation(program, 'iTime'),
uHue: gl.getUniformLocation(program, 'uHue'),
uXOffset: gl.getUniformLocation(program, 'uXOffset'),
uSpeed: gl.getUniformLocation(program, 'uSpeed'),
uIntensity: gl.getUniformLocation(program, 'uIntensity'),
uSize: gl.getUniformLocation(program, 'uSize'),
};
const startTime = performance.now();
let animationFrameId: number;
const render = () => {
resizeCanvas();
gl.uniform2f(uniforms.iResolution, canvas.width, canvas.height);
const currentTime = performance.now();
gl.uniform1f(uniforms.iTime, (currentTime - startTime) / 1000.0);
gl.uniform1f(uniforms.uHue, hue);
gl.uniform1f(uniforms.uXOffset, xOffset);
gl.uniform1f(uniforms.uSpeed, speed);
gl.uniform1f(uniforms.uIntensity, intensity);
gl.uniform1f(uniforms.uSize, size);
gl.drawArrays(gl.TRIANGLES, 0, 6);
animationFrameId = requestAnimationFrame(render);
};
animationFrameId = requestAnimationFrame(render);
return () => {
window.removeEventListener('resize', resizeCanvas);
cancelAnimationFrame(animationFrameId);
};
}, [hue, xOffset, speed, intensity, size]);
return (
<canvas
ref={canvasRef}
className={`w-full h-full block bg-black ${className}`}
/>
);
};
export default Lightning;
~~~
Implementation Guidelines
1. Analyze the component structure, styling, animation implementations
2. Review the component's arguments and state
3. Think through what is the best place to adopt this component/style into the design we are doing
4. Then adopt the component/design to our current system
Help me integrate this into my design