FEAT: 🎉 Add New <Aurora /> background!
This commit is contained in:
@@ -19,6 +19,8 @@ const componentNameLower = componentName.charAt(0).toLowerCase() + componentName
|
||||
const paths = {
|
||||
content: path.join(__dirname, "../src/content", componentType, componentName),
|
||||
tailwind: path.join(__dirname, "../src/tailwind", componentType, componentName),
|
||||
ts: path.join(__dirname, "../src/ts-default", componentType, componentName),
|
||||
tsTailwind: path.join(__dirname, "../src/ts-tailwind", componentType, componentName),
|
||||
demo: path.join(__dirname, "../src/demo", componentType),
|
||||
constants: path.join(__dirname, "../src/constants/code", componentType),
|
||||
};
|
||||
@@ -33,6 +35,9 @@ const files = [
|
||||
path.join(paths.content, `${componentName}.jsx`),
|
||||
path.join(paths.content, `${componentName}.css`),
|
||||
path.join(paths.tailwind, `${componentName}.jsx`),
|
||||
path.join(paths.ts, `${componentName}.tsx`),
|
||||
path.join(paths.ts, `${componentName}.css`),
|
||||
path.join(paths.tsTailwind, `${componentName}.tsx`),
|
||||
path.join(paths.demo, `${componentName}Demo.jsx`),
|
||||
path.join(paths.constants, `${componentNameLower}Code.js`),
|
||||
];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Highlighted sidebar items
|
||||
export const NEW = ['Stepper', 'Infinite Menu', 'Flying Posters', 'Image Trail', 'Flowing Menu', 'Pixel Trail', 'Rotating Text', 'Circular Gallery'];
|
||||
export const UPDATED = ['Follow Cursor'];
|
||||
export const NEW = ['Infinite Menu', 'Flying Posters', 'Pixel Trail', 'Circular Gallery', 'Aurora'];
|
||||
export const UPDATED = [''];
|
||||
|
||||
// Used for main sidebar navigation
|
||||
export const CATEGORIES = [
|
||||
@@ -97,6 +97,7 @@ export const CATEGORIES = [
|
||||
{
|
||||
name: 'Backgrounds',
|
||||
subcategories: [
|
||||
'Aurora',
|
||||
'Shape Blur',
|
||||
'Hyperspeed',
|
||||
'Grid Distortion',
|
||||
|
||||
@@ -51,6 +51,7 @@ const components = {
|
||||
};
|
||||
|
||||
const backgrounds = {
|
||||
'aurora': () => import("../demo/Backgrounds/AuroraDemo"),
|
||||
'squares': () => import("../demo/Backgrounds/SquaresDemo"),
|
||||
'hyperspeed': () => import("../demo/Backgrounds/HyperspeedDemo"),
|
||||
'grid-motion': () => import("../demo/Backgrounds/GridMotionDemo"),
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { generateCliCommands } from '@/utils/utils';
|
||||
|
||||
import code from '@content/Backgrounds/Aurora/Aurora.jsx?raw';
|
||||
import css from '@content/Backgrounds/Aurora/Aurora.css?raw';
|
||||
import tailwind from '@tailwind/Backgrounds/Aurora/Aurora.jsx?raw';
|
||||
import tsCode from '@ts-default/Backgrounds/Aurora/Aurora.tsx?raw';
|
||||
import tsTailwind from '@ts-tailwind/Backgrounds/Aurora/Aurora.tsx?raw';
|
||||
|
||||
export const aurora = {
|
||||
...(generateCliCommands('Backgrounds/Aurora')),
|
||||
installation: `npm i ogl`,
|
||||
usage: `import Aurora from './Aurora';
|
||||
|
||||
<Aurora
|
||||
colors={["#3A29FF", "#FF94B4", "#FF3232"]}
|
||||
speed={0.5}
|
||||
/>`,
|
||||
code,
|
||||
css,
|
||||
tailwind,
|
||||
tsCode,
|
||||
tsTailwind
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import tsTailwind from '@ts-tailwind/Components/Stepper/Stepper.tsx?raw';
|
||||
|
||||
export const stepper = {
|
||||
...(generateCliCommands('Components/Stepper')),
|
||||
installation: `npm i framer-motion`,
|
||||
usage: `import Stepper, { Step } from './Stepper';
|
||||
|
||||
<Stepper
|
||||
|
||||
@@ -9,6 +9,7 @@ import tsTailwind from '@ts-tailwind/TextAnimations/RotatingText/RotatingText.ts
|
||||
|
||||
export const rotatingText = {
|
||||
...(generateCliCommands('TextAnimations/RotatingText')),
|
||||
installation: `npm i framer-motion`,
|
||||
usage: `import RotatingText from './RotatingText'
|
||||
|
||||
<RotatingText
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
.aurora-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
import { Renderer, Program, Mesh, Color, Triangle } from "ogl";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
import './Aurora.css';
|
||||
|
||||
const VERT = `#version 300 es
|
||||
in vec2 uv;
|
||||
in vec2 position;
|
||||
|
||||
out vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
const FRAG = `#version 300 es
|
||||
precision highp float;
|
||||
|
||||
uniform float uTime;
|
||||
uniform float uAmplitude;
|
||||
uniform vec3 uColorStops[3];
|
||||
|
||||
in vec2 vUv;
|
||||
out vec4 fragColor;
|
||||
|
||||
vec3 permute(vec3 x) {
|
||||
return mod(((x * 34.0) + 1.0) * x, 289.0);
|
||||
}
|
||||
|
||||
float snoise(vec2 v){
|
||||
const vec4 C = vec4(
|
||||
0.211324865405187, 0.366025403784439,
|
||||
-0.577350269189626, 0.024390243902439
|
||||
);
|
||||
vec2 i = floor(v + dot(v, C.yy));
|
||||
vec2 x0 = v - i + dot(i, C.xx);
|
||||
vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
||||
vec4 x12 = x0.xyxy + C.xxzz;
|
||||
x12.xy -= i1;
|
||||
i = mod(i, 289.0);
|
||||
|
||||
vec3 p = permute(
|
||||
permute(i.y + vec3(0.0, i1.y, 1.0))
|
||||
+ i.x + vec3(0.0, i1.x, 1.0)
|
||||
);
|
||||
|
||||
vec3 m = max(
|
||||
0.5 - vec3(
|
||||
dot(x0, x0),
|
||||
dot(x12.xy, x12.xy),
|
||||
dot(x12.zw, x12.zw)
|
||||
),
|
||||
0.0
|
||||
);
|
||||
m = m * m;
|
||||
m = m * m;
|
||||
|
||||
vec3 x = 2.0 * fract(p * C.www) - 1.0;
|
||||
vec3 h = abs(x) - 0.5;
|
||||
vec3 ox = floor(x + 0.5);
|
||||
vec3 a0 = x - ox;
|
||||
m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
|
||||
|
||||
vec3 g;
|
||||
g.x = a0.x * x0.x + h.x * x0.y;
|
||||
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
|
||||
return 130.0 * dot(m, g);
|
||||
}
|
||||
|
||||
struct ColorStop {
|
||||
vec3 color;
|
||||
float position;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper macro to blend between consecutive ColorStops
|
||||
* based on vUv.x
|
||||
*/
|
||||
#define COLOR_RAMP(colors, factor, finalColor) { \
|
||||
int index = 0; \
|
||||
for (int i = 0; i < colors.length() - 1; i++) { \
|
||||
ColorStop currentColor = colors[i]; \
|
||||
bool isInBetween = currentColor.position <= factor; \
|
||||
index = int(mix(float(index), float(i), float(isInBetween))); \
|
||||
} \
|
||||
ColorStop currentColor = colors[index]; \
|
||||
ColorStop nextColor = colors[index + 1]; \
|
||||
float range = nextColor.position - currentColor.position; \
|
||||
float lerpFactor = (factor - currentColor.position) / range; \
|
||||
finalColor = mix(currentColor.color, nextColor.color, lerpFactor); \
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Build our three color stops from uniform array uColorStops
|
||||
ColorStop colors[3];
|
||||
colors[0] = ColorStop(uColorStops[0], 0.0);
|
||||
colors[1] = ColorStop(uColorStops[1], 0.5);
|
||||
colors[2] = ColorStop(uColorStops[2], 1.0);
|
||||
|
||||
// Interpolate color along vUv.x
|
||||
vec3 rampColor;
|
||||
COLOR_RAMP(colors, vUv.x, rampColor);
|
||||
|
||||
// Noise-based "height," scaled by amplitude
|
||||
float height = snoise(vec2(vUv.x * 2.0 + uTime * 0.1, uTime * 0.25))
|
||||
* 0.5
|
||||
* uAmplitude;
|
||||
height = exp(height);
|
||||
height = (vUv.y * 2.0 - height + 0.2);
|
||||
|
||||
fragColor.rgb = 0.6 * height * rampColor;
|
||||
fragColor.a = 1.0;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Aurora(props) {
|
||||
const { colorStops = ["#00d8ff", "#7cff67", "#00d8ff"], amplitude = 1.0 } =
|
||||
props;
|
||||
|
||||
const propsRef = useRef(props);
|
||||
propsRef.current = props;
|
||||
|
||||
const ctnDom = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const ctn = ctnDom.current;
|
||||
if (!ctn) return;
|
||||
|
||||
const renderer = new Renderer();
|
||||
const gl = renderer.gl;
|
||||
gl.clearColor(1, 1, 1, 1);
|
||||
|
||||
function resize() {
|
||||
if (!ctn) return;
|
||||
renderer.setSize(ctn.offsetWidth, ctn.offsetHeight);
|
||||
}
|
||||
window.addEventListener("resize", resize);
|
||||
resize();
|
||||
|
||||
const geometry = new Triangle(gl);
|
||||
geometry.addAttribute("uv", {
|
||||
size: 2,
|
||||
data: new Float32Array([0, 0, 2, 0, 0, 2]),
|
||||
});
|
||||
|
||||
const colorStopsArray = colorStops.map((hex) => {
|
||||
const c = new Color(hex);
|
||||
return [c.r, c.g, c.b];
|
||||
});
|
||||
|
||||
const program = new Program(gl, {
|
||||
vertex: VERT,
|
||||
fragment: FRAG,
|
||||
uniforms: {
|
||||
uTime: { value: 0 },
|
||||
uAmplitude: { value: amplitude },
|
||||
uColorStops: { value: colorStopsArray },
|
||||
},
|
||||
});
|
||||
|
||||
const mesh = new Mesh(gl, { geometry, program });
|
||||
ctn.appendChild(gl.canvas);
|
||||
|
||||
let animateId = 0;
|
||||
|
||||
const update = (t) => {
|
||||
animateId = requestAnimationFrame(update);
|
||||
|
||||
const { time = t * 0.01, speed = 1.0 } = propsRef.current;
|
||||
program.uniforms.uTime.value = time * speed * 0.1;
|
||||
|
||||
program.uniforms.uAmplitude.value = propsRef.current.amplitude ?? 1.0;
|
||||
const stops = propsRef.current.colorStops ?? colorStops;
|
||||
program.uniforms.uColorStops.value = stops.map((hex) => {
|
||||
const c = new Color(hex);
|
||||
return [c.r, c.g, c.b];
|
||||
});
|
||||
|
||||
renderer.render({ scene: mesh });
|
||||
};
|
||||
animateId = requestAnimationFrame(update);
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(animateId);
|
||||
window.removeEventListener("resize", resize);
|
||||
|
||||
ctn?.removeChild(gl.canvas);
|
||||
|
||||
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [amplitude, colorStops]);
|
||||
|
||||
return <div ref={ctnDom} className="aurora-container" />;
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import { CodeTab, PreviewTab, CliTab, TabbedLayout } from "../../components/common/TabbedLayout";
|
||||
import { Box, Flex, Slider, SliderFilledTrack, SliderThumb, SliderTrack, Text } from "@chakra-ui/react";
|
||||
|
||||
import CodeExample from "../../components/code/CodeExample";
|
||||
import CliInstallation from "../../components/code/CliInstallation";
|
||||
import PropTable from "../../components/common/PropTable";
|
||||
import Dependencies from '../../components/code/Dependencies';
|
||||
|
||||
import { useState } from "react";
|
||||
import useForceRerender from "../../hooks/useForceRerender";
|
||||
import Aurora from "../../content/Backgrounds/Aurora/Aurora";
|
||||
import { aurora } from "../../constants/code/Backgrounds/auroraCode";
|
||||
|
||||
const AuroraDemo = () => {
|
||||
const [color1, setColor1] = useState('#00d8ff');
|
||||
const [color2, setColor2] = useState('#7cff67');
|
||||
const [color3, setColor3] = useState('#00d8ff');
|
||||
|
||||
const [speed, setSpeed] = useState(1);
|
||||
|
||||
const [key, forceRerender] = useForceRerender();
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: "speed",
|
||||
type: "number",
|
||||
default: "1.0",
|
||||
description: "Controls the animation speed. Higher values make the aurora move faster.",
|
||||
},
|
||||
{
|
||||
name: "colorStops",
|
||||
type: "[string, string, string]",
|
||||
default: '["#3A29FF", "#FF94B4", "#FF3232"]',
|
||||
description: "An array of three hex colors defining the aurora gradient.",
|
||||
},
|
||||
{
|
||||
name: "amplitude",
|
||||
type: "number",
|
||||
default: "1.0",
|
||||
description: "Controls the height intensity of the aurora effect.",
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<TabbedLayout>
|
||||
<PreviewTab>
|
||||
<Box position="relative" className="demo-container" h={500} p={0} overflow="hidden">
|
||||
<Aurora key={key} speed={speed} colorStops={[color1, color2, color3]} />
|
||||
</Box>
|
||||
|
||||
<div className="preview-options">
|
||||
<h2 className="demo-title-extra">Options</h2>
|
||||
<Flex gap={4} mb={2}>
|
||||
<Flex alignItems="center">
|
||||
<Text mr={2}>Color 1</Text>
|
||||
<input
|
||||
type="color"
|
||||
value={color1}
|
||||
style={{ height: '22px', outline: 'none', border: 'none' }}
|
||||
onChange={(e) => {
|
||||
setColor1(e.target.value);
|
||||
forceRerender();
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Flex alignItems="center">
|
||||
<Text mr={2}>Color 2</Text>
|
||||
<input
|
||||
type="color"
|
||||
value={color2}
|
||||
style={{ height: '22px', outline: 'none', border: 'none' }}
|
||||
onChange={(e) => {
|
||||
setColor2(e.target.value);
|
||||
forceRerender();
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Flex alignItems="center">
|
||||
<Text mr={2}>Color 3</Text>
|
||||
<input
|
||||
type="color"
|
||||
value={color3}
|
||||
style={{ height: '22px', outline: 'none', border: 'none' }}
|
||||
onChange={(e) => {
|
||||
setColor3(e.target.value);
|
||||
forceRerender();
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex gap={4} align="center" mt={4}>
|
||||
<Text fontSize="sm">Speed</Text>
|
||||
<Slider
|
||||
min={0}
|
||||
max={2}
|
||||
step={0.1}
|
||||
value={speed}
|
||||
onChange={(val) => {
|
||||
setSpeed(val);
|
||||
forceRerender();
|
||||
}}
|
||||
width="200px"
|
||||
>
|
||||
<SliderTrack>
|
||||
<SliderFilledTrack />
|
||||
</SliderTrack>
|
||||
<SliderThumb />
|
||||
</Slider>
|
||||
<Text fontSize="sm">{speed}</Text>
|
||||
</Flex>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<PropTable data={propData} />
|
||||
<Dependencies dependencyList={['ogl']} />
|
||||
</PreviewTab>
|
||||
|
||||
<CodeTab>
|
||||
<CodeExample codeObject={aurora} />
|
||||
</CodeTab>
|
||||
|
||||
<CliTab>
|
||||
<CliInstallation {...aurora} />
|
||||
</CliTab>
|
||||
</TabbedLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuroraDemo;
|
||||
@@ -0,0 +1,195 @@
|
||||
import { Renderer, Program, Mesh, Color, Triangle } from "ogl";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
const VERT = `#version 300 es
|
||||
in vec2 uv;
|
||||
in vec2 position;
|
||||
|
||||
out vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
const FRAG = `#version 300 es
|
||||
precision highp float;
|
||||
|
||||
uniform float uTime;
|
||||
uniform float uAmplitude;
|
||||
uniform vec3 uColorStops[3];
|
||||
|
||||
in vec2 vUv;
|
||||
out vec4 fragColor;
|
||||
|
||||
vec3 permute(vec3 x) {
|
||||
return mod(((x * 34.0) + 1.0) * x, 289.0);
|
||||
}
|
||||
|
||||
float snoise(vec2 v){
|
||||
const vec4 C = vec4(
|
||||
0.211324865405187, 0.366025403784439,
|
||||
-0.577350269189626, 0.024390243902439
|
||||
);
|
||||
vec2 i = floor(v + dot(v, C.yy));
|
||||
vec2 x0 = v - i + dot(i, C.xx);
|
||||
vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
||||
vec4 x12 = x0.xyxy + C.xxzz;
|
||||
x12.xy -= i1;
|
||||
i = mod(i, 289.0);
|
||||
|
||||
vec3 p = permute(
|
||||
permute(i.y + vec3(0.0, i1.y, 1.0))
|
||||
+ i.x + vec3(0.0, i1.x, 1.0)
|
||||
);
|
||||
|
||||
vec3 m = max(
|
||||
0.5 - vec3(
|
||||
dot(x0, x0),
|
||||
dot(x12.xy, x12.xy),
|
||||
dot(x12.zw, x12.zw)
|
||||
),
|
||||
0.0
|
||||
);
|
||||
m = m * m;
|
||||
m = m * m;
|
||||
|
||||
vec3 x = 2.0 * fract(p * C.www) - 1.0;
|
||||
vec3 h = abs(x) - 0.5;
|
||||
vec3 ox = floor(x + 0.5);
|
||||
vec3 a0 = x - ox;
|
||||
m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
|
||||
|
||||
vec3 g;
|
||||
g.x = a0.x * x0.x + h.x * x0.y;
|
||||
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
|
||||
return 130.0 * dot(m, g);
|
||||
}
|
||||
|
||||
struct ColorStop {
|
||||
vec3 color;
|
||||
float position;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper macro to blend between consecutive ColorStops
|
||||
* based on vUv.x
|
||||
*/
|
||||
#define COLOR_RAMP(colors, factor, finalColor) { \
|
||||
int index = 0; \
|
||||
for (int i = 0; i < colors.length() - 1; i++) { \
|
||||
ColorStop currentColor = colors[i]; \
|
||||
bool isInBetween = currentColor.position <= factor; \
|
||||
index = int(mix(float(index), float(i), float(isInBetween))); \
|
||||
} \
|
||||
ColorStop currentColor = colors[index]; \
|
||||
ColorStop nextColor = colors[index + 1]; \
|
||||
float range = nextColor.position - currentColor.position; \
|
||||
float lerpFactor = (factor - currentColor.position) / range; \
|
||||
finalColor = mix(currentColor.color, nextColor.color, lerpFactor); \
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Build our three color stops from uniform array uColorStops
|
||||
ColorStop colors[3];
|
||||
colors[0] = ColorStop(uColorStops[0], 0.0);
|
||||
colors[1] = ColorStop(uColorStops[1], 0.5);
|
||||
colors[2] = ColorStop(uColorStops[2], 1.0);
|
||||
|
||||
// Interpolate color along vUv.x
|
||||
vec3 rampColor;
|
||||
COLOR_RAMP(colors, vUv.x, rampColor);
|
||||
|
||||
// Noise-based "height," scaled by amplitude
|
||||
float height = snoise(vec2(vUv.x * 2.0 + uTime * 0.1, uTime * 0.25))
|
||||
* 0.5
|
||||
* uAmplitude;
|
||||
height = exp(height);
|
||||
height = (vUv.y * 2.0 - height + 0.2);
|
||||
|
||||
fragColor.rgb = 0.6 * height * rampColor;
|
||||
fragColor.a = 1.0;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Aurora(props) {
|
||||
const { colorStops = ["#00d8ff", "#7cff67", "#00d8ff"], amplitude = 1.0 } =
|
||||
props;
|
||||
|
||||
const propsRef = useRef(props);
|
||||
propsRef.current = props;
|
||||
|
||||
const ctnDom = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const ctn = ctnDom.current;
|
||||
if (!ctn) return;
|
||||
|
||||
const renderer = new Renderer();
|
||||
const gl = renderer.gl;
|
||||
gl.clearColor(1, 1, 1, 1);
|
||||
|
||||
function resize() {
|
||||
if (!ctn) return;
|
||||
renderer.setSize(ctn.offsetWidth, ctn.offsetHeight);
|
||||
}
|
||||
window.addEventListener("resize", resize);
|
||||
resize();
|
||||
|
||||
const geometry = new Triangle(gl);
|
||||
geometry.addAttribute("uv", {
|
||||
size: 2,
|
||||
data: new Float32Array([0, 0, 2, 0, 0, 2]),
|
||||
});
|
||||
|
||||
const colorStopsArray = colorStops.map((hex) => {
|
||||
const c = new Color(hex);
|
||||
return [c.r, c.g, c.b];
|
||||
});
|
||||
|
||||
const program = new Program(gl, {
|
||||
vertex: VERT,
|
||||
fragment: FRAG,
|
||||
uniforms: {
|
||||
uTime: { value: 0 },
|
||||
uAmplitude: { value: amplitude },
|
||||
uColorStops: { value: colorStopsArray },
|
||||
},
|
||||
});
|
||||
|
||||
const mesh = new Mesh(gl, { geometry, program });
|
||||
ctn.appendChild(gl.canvas);
|
||||
|
||||
let animateId = 0;
|
||||
|
||||
const update = (t) => {
|
||||
animateId = requestAnimationFrame(update);
|
||||
|
||||
const { time = t * 0.01, speed = 1.0 } = propsRef.current;
|
||||
program.uniforms.uTime.value = time * speed * 0.1;
|
||||
|
||||
program.uniforms.uAmplitude.value = propsRef.current.amplitude ?? 1.0;
|
||||
const stops = propsRef.current.colorStops ?? colorStops;
|
||||
program.uniforms.uColorStops.value = stops.map((hex) => {
|
||||
const c = new Color(hex);
|
||||
return [c.r, c.g, c.b];
|
||||
});
|
||||
|
||||
renderer.render({ scene: mesh });
|
||||
};
|
||||
animateId = requestAnimationFrame(update);
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(animateId);
|
||||
window.removeEventListener("resize", resize);
|
||||
|
||||
ctn?.removeChild(gl.canvas);
|
||||
|
||||
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [amplitude, colorStops]);
|
||||
|
||||
return <div ref={ctnDom} className="w-full h-full" />;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
.aurora-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
import { Renderer, Program, Mesh, Color, Triangle } from "ogl";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
import './Aurora.css';
|
||||
|
||||
export interface CommonProps {
|
||||
onReady?: () => void;
|
||||
}
|
||||
|
||||
export interface TimeProps {
|
||||
time?: number;
|
||||
speed?: number;
|
||||
}
|
||||
|
||||
export interface ControlProps {
|
||||
paused?: boolean;
|
||||
}
|
||||
|
||||
export interface AuroraProps extends CommonProps, TimeProps, ControlProps {
|
||||
colorStops?: [string, string, string];
|
||||
amplitude?: number;
|
||||
}
|
||||
|
||||
const VERT = `#version 300 es
|
||||
in vec2 uv;
|
||||
in vec2 position;
|
||||
|
||||
out vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
const FRAG = `#version 300 es
|
||||
precision highp float;
|
||||
|
||||
uniform float uTime;
|
||||
uniform float uAmplitude;
|
||||
uniform vec3 uColorStops[3];
|
||||
|
||||
in vec2 vUv;
|
||||
out vec4 fragColor;
|
||||
|
||||
vec3 permute(vec3 x) {
|
||||
return mod(((x * 34.0) + 1.0) * x, 289.0);
|
||||
}
|
||||
|
||||
float snoise(vec2 v){
|
||||
const vec4 C = vec4(
|
||||
0.211324865405187, 0.366025403784439,
|
||||
-0.577350269189626, 0.024390243902439
|
||||
);
|
||||
vec2 i = floor(v + dot(v, C.yy));
|
||||
vec2 x0 = v - i + dot(i, C.xx);
|
||||
vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
||||
vec4 x12 = x0.xyxy + C.xxzz;
|
||||
x12.xy -= i1;
|
||||
i = mod(i, 289.0);
|
||||
|
||||
vec3 p = permute(
|
||||
permute(i.y + vec3(0.0, i1.y, 1.0))
|
||||
+ i.x + vec3(0.0, i1.x, 1.0)
|
||||
);
|
||||
|
||||
vec3 m = max(
|
||||
0.5 - vec3(
|
||||
dot(x0, x0),
|
||||
dot(x12.xy, x12.xy),
|
||||
dot(x12.zw, x12.zw)
|
||||
),
|
||||
0.0
|
||||
);
|
||||
m = m * m;
|
||||
m = m * m;
|
||||
|
||||
vec3 x = 2.0 * fract(p * C.www) - 1.0;
|
||||
vec3 h = abs(x) - 0.5;
|
||||
vec3 ox = floor(x + 0.5);
|
||||
vec3 a0 = x - ox;
|
||||
m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
|
||||
|
||||
vec3 g;
|
||||
g.x = a0.x * x0.x + h.x * x0.y;
|
||||
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
|
||||
return 130.0 * dot(m, g);
|
||||
}
|
||||
|
||||
struct ColorStop {
|
||||
vec3 color;
|
||||
float position;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper macro to blend between consecutive ColorStops
|
||||
* based on vUv.x
|
||||
*/
|
||||
#define COLOR_RAMP(colors, factor, finalColor) { \
|
||||
int index = 0; \
|
||||
for (int i = 0; i < colors.length() - 1; i++) { \
|
||||
ColorStop currentColor = colors[i]; \
|
||||
bool isInBetween = currentColor.position <= factor; \
|
||||
index = int(mix(float(index), float(i), float(isInBetween))); \
|
||||
} \
|
||||
ColorStop currentColor = colors[index]; \
|
||||
ColorStop nextColor = colors[index + 1]; \
|
||||
float range = nextColor.position - currentColor.position; \
|
||||
float lerpFactor = (factor - currentColor.position) / range; \
|
||||
finalColor = mix(currentColor.color, nextColor.color, lerpFactor); \
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Build our three color stops from uniform array uColorStops
|
||||
ColorStop colors[3];
|
||||
colors[0] = ColorStop(uColorStops[0], 0.0);
|
||||
colors[1] = ColorStop(uColorStops[1], 0.5);
|
||||
colors[2] = ColorStop(uColorStops[2], 1.0);
|
||||
|
||||
// Interpolate color along vUv.x
|
||||
vec3 rampColor;
|
||||
COLOR_RAMP(colors, vUv.x, rampColor);
|
||||
|
||||
// Noise-based "height," scaled by amplitude
|
||||
float height = snoise(vec2(vUv.x * 2.0 + uTime * 0.1, uTime * 0.25))
|
||||
* 0.5
|
||||
* uAmplitude;
|
||||
height = exp(height);
|
||||
height = (vUv.y * 2.0 - height + 0.2);
|
||||
|
||||
fragColor.rgb = 0.6 * height * rampColor;
|
||||
fragColor.a = 1.0;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Aurora(props: AuroraProps) {
|
||||
const { colorStops = ["#00d8ff", "#7cff67", "#00d8ff"], amplitude = 1.0 } =
|
||||
props;
|
||||
|
||||
const propsRef = useRef(props);
|
||||
propsRef.current = props;
|
||||
|
||||
const ctnDom = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const ctn = ctnDom.current;
|
||||
if (!ctn) return;
|
||||
|
||||
const renderer = new Renderer();
|
||||
const gl = renderer.gl;
|
||||
gl.clearColor(1, 1, 1, 1);
|
||||
|
||||
function resize() {
|
||||
if (!ctn) return;
|
||||
renderer.setSize(ctn.offsetWidth, ctn.offsetHeight);
|
||||
}
|
||||
window.addEventListener("resize", resize);
|
||||
resize();
|
||||
|
||||
const geometry = new Triangle(gl);
|
||||
geometry.addAttribute("uv", {
|
||||
size: 2,
|
||||
data: new Float32Array([0, 0, 2, 0, 0, 2]),
|
||||
});
|
||||
|
||||
const colorStopsArray = colorStops.map((hex) => {
|
||||
const c = new Color(hex);
|
||||
return [c.r, c.g, c.b] as [number, number, number];
|
||||
});
|
||||
|
||||
const program = new Program(gl, {
|
||||
vertex: VERT,
|
||||
fragment: FRAG,
|
||||
uniforms: {
|
||||
uTime: { value: 0 },
|
||||
uAmplitude: { value: amplitude },
|
||||
uColorStops: { value: colorStopsArray },
|
||||
},
|
||||
});
|
||||
|
||||
const mesh = new Mesh(gl, { geometry, program });
|
||||
ctn.appendChild(gl.canvas);
|
||||
|
||||
let animateId = 0;
|
||||
|
||||
const update = (t: number) => {
|
||||
animateId = requestAnimationFrame(update);
|
||||
|
||||
const { time = t * 0.01, speed = 1.0 } = propsRef.current;
|
||||
program.uniforms.uTime.value = time * speed * 0.1;
|
||||
|
||||
program.uniforms.uAmplitude.value = propsRef.current.amplitude ?? 1.0;
|
||||
const stops = propsRef.current.colorStops ?? colorStops;
|
||||
program.uniforms.uColorStops.value = stops.map((hex) => {
|
||||
const c = new Color(hex);
|
||||
return [c.r, c.g, c.b] as [number, number, number];
|
||||
});
|
||||
|
||||
renderer.render({ scene: mesh });
|
||||
};
|
||||
animateId = requestAnimationFrame(update);
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(animateId);
|
||||
window.removeEventListener("resize", resize);
|
||||
|
||||
ctn?.removeChild(gl.canvas);
|
||||
|
||||
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
||||
};
|
||||
}, [amplitude, colorStops]);
|
||||
|
||||
return <div ref={ctnDom} className="aurora-container" />;
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
import { Renderer, Program, Mesh, Color, Triangle } from "ogl";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
export interface CommonProps {
|
||||
onReady?: () => void;
|
||||
}
|
||||
|
||||
export interface TimeProps {
|
||||
time?: number;
|
||||
speed?: number;
|
||||
}
|
||||
|
||||
export interface ControlProps {
|
||||
paused?: boolean;
|
||||
}
|
||||
|
||||
export interface AuroraProps extends CommonProps, TimeProps, ControlProps {
|
||||
colorStops?: [string, string, string];
|
||||
amplitude?: number;
|
||||
}
|
||||
|
||||
const VERT = `#version 300 es
|
||||
in vec2 uv;
|
||||
in vec2 position;
|
||||
|
||||
out vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
const FRAG = `#version 300 es
|
||||
precision highp float;
|
||||
|
||||
uniform float uTime;
|
||||
uniform float uAmplitude;
|
||||
uniform vec3 uColorStops[3];
|
||||
|
||||
in vec2 vUv;
|
||||
out vec4 fragColor;
|
||||
|
||||
vec3 permute(vec3 x) {
|
||||
return mod(((x * 34.0) + 1.0) * x, 289.0);
|
||||
}
|
||||
|
||||
float snoise(vec2 v){
|
||||
const vec4 C = vec4(
|
||||
0.211324865405187, 0.366025403784439,
|
||||
-0.577350269189626, 0.024390243902439
|
||||
);
|
||||
vec2 i = floor(v + dot(v, C.yy));
|
||||
vec2 x0 = v - i + dot(i, C.xx);
|
||||
vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
||||
vec4 x12 = x0.xyxy + C.xxzz;
|
||||
x12.xy -= i1;
|
||||
i = mod(i, 289.0);
|
||||
|
||||
vec3 p = permute(
|
||||
permute(i.y + vec3(0.0, i1.y, 1.0))
|
||||
+ i.x + vec3(0.0, i1.x, 1.0)
|
||||
);
|
||||
|
||||
vec3 m = max(
|
||||
0.5 - vec3(
|
||||
dot(x0, x0),
|
||||
dot(x12.xy, x12.xy),
|
||||
dot(x12.zw, x12.zw)
|
||||
),
|
||||
0.0
|
||||
);
|
||||
m = m * m;
|
||||
m = m * m;
|
||||
|
||||
vec3 x = 2.0 * fract(p * C.www) - 1.0;
|
||||
vec3 h = abs(x) - 0.5;
|
||||
vec3 ox = floor(x + 0.5);
|
||||
vec3 a0 = x - ox;
|
||||
m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
|
||||
|
||||
vec3 g;
|
||||
g.x = a0.x * x0.x + h.x * x0.y;
|
||||
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
|
||||
return 130.0 * dot(m, g);
|
||||
}
|
||||
|
||||
struct ColorStop {
|
||||
vec3 color;
|
||||
float position;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper macro to blend between consecutive ColorStops
|
||||
* based on vUv.x
|
||||
*/
|
||||
#define COLOR_RAMP(colors, factor, finalColor) { \
|
||||
int index = 0; \
|
||||
for (int i = 0; i < colors.length() - 1; i++) { \
|
||||
ColorStop currentColor = colors[i]; \
|
||||
bool isInBetween = currentColor.position <= factor; \
|
||||
index = int(mix(float(index), float(i), float(isInBetween))); \
|
||||
} \
|
||||
ColorStop currentColor = colors[index]; \
|
||||
ColorStop nextColor = colors[index + 1]; \
|
||||
float range = nextColor.position - currentColor.position; \
|
||||
float lerpFactor = (factor - currentColor.position) / range; \
|
||||
finalColor = mix(currentColor.color, nextColor.color, lerpFactor); \
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Build our three color stops from uniform array uColorStops
|
||||
ColorStop colors[3];
|
||||
colors[0] = ColorStop(uColorStops[0], 0.0);
|
||||
colors[1] = ColorStop(uColorStops[1], 0.5);
|
||||
colors[2] = ColorStop(uColorStops[2], 1.0);
|
||||
|
||||
// Interpolate color along vUv.x
|
||||
vec3 rampColor;
|
||||
COLOR_RAMP(colors, vUv.x, rampColor);
|
||||
|
||||
// Noise-based "height," scaled by amplitude
|
||||
float height = snoise(vec2(vUv.x * 2.0 + uTime * 0.1, uTime * 0.25))
|
||||
* 0.5
|
||||
* uAmplitude;
|
||||
height = exp(height);
|
||||
height = (vUv.y * 2.0 - height + 0.2);
|
||||
|
||||
fragColor.rgb = 0.6 * height * rampColor;
|
||||
fragColor.a = 1.0;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Aurora(props: AuroraProps) {
|
||||
const { colorStops = ["#00d8ff", "#7cff67", "#00d8ff"], amplitude = 1.0 } =
|
||||
props;
|
||||
|
||||
const propsRef = useRef(props);
|
||||
propsRef.current = props;
|
||||
|
||||
const ctnDom = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const ctn = ctnDom.current;
|
||||
if (!ctn) return;
|
||||
|
||||
const renderer = new Renderer();
|
||||
const gl = renderer.gl;
|
||||
gl.clearColor(1, 1, 1, 1);
|
||||
|
||||
function resize() {
|
||||
if (!ctn) return;
|
||||
renderer.setSize(ctn.offsetWidth, ctn.offsetHeight);
|
||||
}
|
||||
window.addEventListener("resize", resize);
|
||||
resize();
|
||||
|
||||
const geometry = new Triangle(gl);
|
||||
geometry.addAttribute("uv", {
|
||||
size: 2,
|
||||
data: new Float32Array([0, 0, 2, 0, 0, 2]),
|
||||
});
|
||||
|
||||
const colorStopsArray = colorStops.map((hex) => {
|
||||
const c = new Color(hex);
|
||||
return [c.r, c.g, c.b] as [number, number, number];
|
||||
});
|
||||
|
||||
const program = new Program(gl, {
|
||||
vertex: VERT,
|
||||
fragment: FRAG,
|
||||
uniforms: {
|
||||
uTime: { value: 0 },
|
||||
uAmplitude: { value: amplitude },
|
||||
uColorStops: { value: colorStopsArray },
|
||||
},
|
||||
});
|
||||
|
||||
const mesh = new Mesh(gl, { geometry, program });
|
||||
ctn.appendChild(gl.canvas);
|
||||
|
||||
let animateId = 0;
|
||||
|
||||
const update = (t: number) => {
|
||||
animateId = requestAnimationFrame(update);
|
||||
|
||||
const { time = t * 0.01, speed = 1.0 } = propsRef.current;
|
||||
program.uniforms.uTime.value = time * speed * 0.1;
|
||||
|
||||
program.uniforms.uAmplitude.value = propsRef.current.amplitude ?? 1.0;
|
||||
const stops = propsRef.current.colorStops ?? colorStops;
|
||||
program.uniforms.uColorStops.value = stops.map((hex) => {
|
||||
const c = new Color(hex);
|
||||
return [c.r, c.g, c.b] as [number, number, number];
|
||||
});
|
||||
|
||||
renderer.render({ scene: mesh });
|
||||
};
|
||||
animateId = requestAnimationFrame(update);
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(animateId);
|
||||
window.removeEventListener("resize", resize);
|
||||
|
||||
ctn?.removeChild(gl.canvas);
|
||||
|
||||
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
||||
};
|
||||
}, [amplitude, colorStops]);
|
||||
|
||||
return <div ref={ctnDom} className="w-full h-full" />;
|
||||
}
|
||||
Reference in New Issue
Block a user