Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 40 additions & 74 deletions core/lib/components/lumiflex/index.tsx
Original file line number Diff line number Diff line change
@@ -1,85 +1,51 @@
import { Renderer, Program, Mesh, Color, Triangle } from "ogl";
import { Color, Mesh, Program, Triangle } from "ogl";
import React from "react";
import { useEffect, useRef } from "react";
import vert from "./vert.glsl";
import frag from "./frag.glsl";
import { CommonProps, ControlProps, TimeProps } from "../../types/CommonProps";
import { useOgl } from "../../hooks";

export interface LumiflexProps extends CommonProps, TimeProps, ControlProps {}

export function Lumiflex(props: LumiflexProps) {
const propsRef = useRef<LumiflexProps>(props);
const ctnDom = useRef<HTMLDivElement>(null);

useEffect(() => {
propsRef.current = props;
});

useEffect(() => {
if (!ctnDom.current) {
return;
}

const ctn = ctnDom.current;
const renderer = new Renderer();
const gl = renderer.gl;
gl.clearColor(1, 1, 1, 1);

function resize() {
if (ctn == null) {
return;
}

const scale = 1;
renderer.setSize(ctn.offsetWidth * scale, ctn.offsetHeight * scale);
}
window.addEventListener("resize", resize, false);
resize();

const geometry = new Triangle(gl);

const program = new Program(gl, {
vertex: vert,
fragment: frag,
uniforms: {
uTime: { value: 0 },
uColor: { value: new Color(0.3, 0.2, 0.5) },
},
const containerRef = useOgl({
props,
init: ({ gl }) => {
const geometry = new Triangle(gl);
const program = new Program(gl, {
vertex: vert,
fragment: frag,
uniforms: {
uTime: { value: 0 },
uColor: { value: new Color(0.3, 0.2, 0.5) },
},
});

const mesh = new Mesh(gl, { geometry, program });

return { geometry, program, mesh };
},
render: ({ time: t, props, program, renderer, mesh }) => {
const { time: time = t * 0.01, speed = 1.0 } = props.current;
program.uniforms.uTime.value = time * speed * 0.1;
renderer.render({ scene: mesh });
},
resize: ({ renderer, container }) => {
renderer.setSize(container.offsetWidth, container.offsetHeight);
},
destroy: ({ gl }) => {
gl.getExtension("WEBGL_lose_context")?.loseContext();
},
});

const mesh = new Mesh(gl, { geometry, program });

let animateId: number;

animateId = requestAnimationFrame(update);

function update(t: number) {
animateId = requestAnimationFrame(update);

const { time: time = t * 0.01, speed = 1.0 } = propsRef.current;

program.uniforms.uTime.value = time * speed * 0.1;

renderer.render({ scene: mesh });
}

ctn.appendChild(gl.canvas);
return () => {
cancelAnimationFrame(animateId);
window.removeEventListener("resize", resize);
ctn.removeChild(gl.canvas);
gl.getExtension("WEBGL_lose_context")?.loseContext();
};
}, []);

return (
<div
ref={ctnDom}
style={{
width: "100%",
height: "100%",
}}
{...props}
/>
);
return (
<div
ref={containerRef}
style={{
width: "100%",
height: "100%",
}}
{...props}
/>
);
}
1 change: 1 addition & 0 deletions core/lib/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useOgl'
113 changes: 113 additions & 0 deletions core/lib/hooks/useOgl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { type OGLRenderingContext, Renderer } from "ogl";
import {MutableRefObject, useEffect, useRef} from "react";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export declare type AnyProps = Record<string, any>;

export declare type UseOglContext<
Props extends AnyProps,
// eslint-disable-next-line @typescript-eslint/ban-types
AdditionalContext extends AnyProps = {},
> = AdditionalContext & {
props: MutableRefObject<Props>;
gl: OGLRenderingContext;
renderer: Renderer;
container: HTMLDivElement;
};

export declare type UseOglContextWithTime<
Props extends AnyProps,
// eslint-disable-next-line @typescript-eslint/ban-types
AdditionalContext extends AnyProps = {},
> = UseOglContext<Props, AdditionalContext> & {
time: number;
};


export interface UseOglProps<
Props extends AnyProps,
InitReturnType extends AnyProps,
> {
props: Props;
init: (context: UseOglContext<Props>) => InitReturnType;
resize: (context: UseOglContext<Props, InitReturnType>) => void;
render: (context: UseOglContextWithTime<Props, InitReturnType>) => void;
destroy: (context: UseOglContext<Props, InitReturnType>) => void;
}

export function useOgl<Props extends AnyProps, InitReturnType extends AnyProps>(
options: UseOglProps<Props, InitReturnType>,
) {
const containerRef = useRef<HTMLDivElement>(null);
const propsRef = useRef<Props>(options.props);

const resizeObserver = useRef<ResizeObserver | null>(null);

const rendererRef = useRef<Renderer | null>(null);
const glRef = useRef<OGLRenderingContext | null>(null);

function onResize() {
const renderer = rendererRef.current;
const container = containerRef.current;
if (!container || !renderer) {
return;
}
renderer.setSize(container.offsetWidth, container.offsetHeight);
}

useEffect(() => {
propsRef.current = options.props;
}, [options.props]);

useEffect(() => {
const container = containerRef.current;
if (!container) {
return;
}

const renderer = (rendererRef.current = new Renderer());
const gl = (glRef.current = renderer.gl);
gl.clearColor(1, 1, 1, 1);

window.addEventListener("resize", onResize);
resizeObserver.current = new ResizeObserver(onResize);
resizeObserver.current.observe(container);
onResize();


const baseContext: UseOglContext<Props> = {
props: propsRef,
gl,
renderer,
container,
};

const initReturn = options.init(baseContext);
const withInitContext: UseOglContext<Props, InitReturnType> = {
...baseContext,
...initReturn,
};

options.resize(withInitContext);

let animateId: number;

function update(t: number) {
animateId = requestAnimationFrame(update);
options.render({ ...withInitContext, time: t });
}
animateId = requestAnimationFrame(update);

container.appendChild(gl.canvas);

return () => {
window.cancelAnimationFrame(animateId);
window.removeEventListener("resize", onResize);
container.removeChild(gl.canvas);
resizeObserver.current?.disconnect();
options.destroy(withInitContext);
};
}, []);

return containerRef;
}