Rework website with bulma instead of bootstrap #6

Manually merged
tlater merged 43 commits from tlater/bulma2 into master 2022-08-14 00:26:49 +01:00
4 changed files with 68 additions and 19 deletions
Showing only changes of commit 323189f469 - Show all commits

View file

@ -7,12 +7,10 @@ import { musicPlayer } from "./musicPlayerSlice";
function MusicPlayer() { function MusicPlayer() {
return ( return (
<div className="is-flex-grow-1 is-flex is-flex-direction-column"> <div className="is-flex-grow-1 is-flex is-flex-direction-column">
<div className="is-flex-grow-1 is-overflow-hidden">
<Visualizer <Visualizer
audioContext={musicPlayer.audioContext} audioContext={musicPlayer.audioContext}
audioNode={musicPlayer.audioNode} audioNode={musicPlayer.audioNode}
/> />
</div>
<div className="is-flex-grow-0"> <div className="is-flex-grow-0">
<Controls /> <Controls />
</div> </div>

View file

@ -11,11 +11,14 @@ class RendererError extends Error {}
class Renderer { class Renderer {
private canvas: HTMLCanvasElement; private canvas: HTMLCanvasElement;
private overlay: HTMLSpanElement;
private analyser: AnalyserNode; private analyser: AnalyserNode;
private analyserData: Uint8Array; private analyserData: Uint8Array;
private time: number; private lastFrameTime: number;
private dTime: number;
private buffers: { private buffers: {
indices?: WebGLBuffer; indices?: WebGLBuffer;
positions?: WebGLBuffer; positions?: WebGLBuffer;
@ -26,7 +29,8 @@ class Renderer {
constructor( constructor(
context: AudioContext, context: AudioContext,
node: AudioNode, node: AudioNode,
canvas: HTMLCanvasElement canvas: HTMLCanvasElement,
overlay: HTMLSpanElement
) { ) {
const analyser = context.createAnalyser(); const analyser = context.createAnalyser();
analyser.fftSize = 2048; analyser.fftSize = 2048;
@ -34,10 +38,12 @@ class Renderer {
node.connect(analyser); node.connect(analyser);
this.canvas = canvas; this.canvas = canvas;
this.overlay = overlay;
this.analyser = analyser; this.analyser = analyser;
this.analyserData = new Uint8Array(analyser.frequencyBinCount); this.analyserData = new Uint8Array(analyser.frequencyBinCount);
this.time = 0; this.lastFrameTime = 0;
this.dTime = 0;
this.buffers = {}; this.buffers = {};
} }
@ -150,7 +156,7 @@ class Renderer {
mat4.rotateY( mat4.rotateY(
modelViewMatrix, modelViewMatrix,
modelViewMatrix, modelViewMatrix,
this.time * ROTATION_SPEED this.lastFrameTime * ROTATION_SPEED
); );
mat4.translate(modelViewMatrix, modelViewMatrix, [-1.0, 0.0, 0.0]); mat4.translate(modelViewMatrix, modelViewMatrix, [-1.0, 0.0, 0.0]);
gl.uniformMatrix4fv( gl.uniformMatrix4fv(
@ -222,6 +228,8 @@ class Renderer {
gl.vertexAttribDivisor(shader.getAttribute("aHeight"), 1); gl.vertexAttribDivisor(shader.getAttribute("aHeight"), 1);
gl.enableVertexAttribArray(shader.getAttribute("aHeight")); gl.enableVertexAttribArray(shader.getAttribute("aHeight"));
const cpuTime = Math.round(performance.now() - this.lastFrameTime);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawElementsInstanced( gl.drawElementsInstanced(
gl.TRIANGLES, gl.TRIANGLES,
@ -231,8 +239,16 @@ class Renderer {
this.analyser.frequencyBinCount this.analyser.frequencyBinCount
); );
const gpuTime = Math.round(performance.now() - this.lastFrameTime);
this.overlay.innerText = `${Math.round(
this.dTime
)}ms (${cpuTime}ms / ${gpuTime}ms)`;
requestAnimationFrame((time) => { requestAnimationFrame((time) => {
this.time = time; this.dTime = time - this.lastFrameTime;
this.lastFrameTime = time;
this.drawScene(gl, shader); this.drawScene(gl, shader);
}); });
} }

View file

@ -11,19 +11,37 @@ function Visualizer({
}) { }) {
const [renderError, setRenderError] = useState<JSX.Element | null>(null); const [renderError, setRenderError] = useState<JSX.Element | null>(null);
const canvas = useCallback( const visualizer = useCallback(
(canvas: HTMLCanvasElement | null) => { (visualizer: HTMLDivElement | null) => {
// If we're rendering an error message, we won't be // If we're rendering an error message, we won't be
// setting a canvas. // setting up the visualizer.
// //
// Also, nonintuitively, renderError will be null here on // Also, nonintuitively, renderError will be null here on
// subsequent iterations, so we can't rely on it to // subsequent iterations, so we can't rely on it to
// identify errors. // identify errors.
if (canvas === null) { if (visualizer === null) {
return; return;
} }
const renderer = new Renderer(audioContext, audioNode, canvas); const canvas = visualizer.children[0];
const overlay = visualizer.children[1];
if (
!(canvas instanceof HTMLCanvasElement) ||
!(overlay instanceof HTMLSpanElement)
) {
throw new Error(
"react did not create our visualizer div correctly"
);
}
const renderer = new Renderer(
audioContext,
audioNode,
canvas,
overlay
);
try { try {
renderer.initializeScene(); renderer.initializeScene();
} catch (error) { } catch (error) {
@ -78,7 +96,18 @@ function Visualizer({
); );
if (renderError === null) { if (renderError === null) {
return <canvas ref={canvas} style={{ display: "block" }}></canvas>; return (
<div
ref={visualizer}
className="is-flex-grow-1 is-clipped is-relative"
>
<canvas style={{ display: "block" }}></canvas>
<span
className="is-bottom-left"
style={{ display: "relative" }}
></span>
</div>
);
} else { } else {
return renderError; return renderError;
} }

View file

@ -3,6 +3,12 @@ $fa-font-path: "npm:@fortawesome/fontawesome-free/webfonts";
@import "~/node_modules/@fortawesome/fontawesome-free/scss/fontawesome"; @import "~/node_modules/@fortawesome/fontawesome-free/scss/fontawesome";
@import "~/node_modules/@fortawesome/fontawesome-free/scss/solid"; @import "~/node_modules/@fortawesome/fontawesome-free/scss/solid";
.is-overflow-hidden { .is-relative {
overflow: hidden !important; position: relative !important;
}
.is-bottom-left {
bottom: 0;
left: 0;
position: absolute !important;
} }