diff --git a/example/app/03-props.js b/example/app/03-props.js index b5621d5..725b359 100644 --- a/example/app/03-props.js +++ b/example/app/03-props.js @@ -26,7 +26,7 @@ export function Props() { return (
- + = width - size ? width - size : x + speed; } - if (input.current.includes(" ")) { + if (input.current.includes("a")) { if (!isShooting) { isShooting = true; BULLETS.push(Bullet({ x: x, y }, context)); diff --git a/example/app/07-bounce-text.js b/example/app/07-bounce-text.js index d197895..4759642 100644 --- a/example/app/07-bounce-text.js +++ b/example/app/07-bounce-text.js @@ -2,7 +2,7 @@ import { useCanvas } from "@kirkegaard/react-use-canvas"; export function BounceText() { - const string = "REACT CANVAS"; + const string = "CANVAS<3"; const size = 42; const spacing = 4; const trail = 10; diff --git a/example/app/globals.css b/example/app/globals.css index 2ac9881..2a99b1d 100644 --- a/example/app/globals.css +++ b/example/app/globals.css @@ -1,4 +1,21 @@ :root { + --shadow-color: 0deg 0% 0%; + --shadow-elevation-low: 0px 0.1px 0.2px hsl(var(--shadow-color) / 0), + 0px 0.1px 0.2px hsl(var(--shadow-color) / 0.05), + 0px 0.3px 0.5px hsl(var(--shadow-color) / 0.1); + --shadow-elevation-medium: 0px 0.1px 0.2px hsl(var(--shadow-color) / 0), + 0px 0.3px 0.5px hsl(var(--shadow-color) / 0.04), + 0.1px 0.7px 1.1px hsl(var(--shadow-color) / 0.08), + 0.1px 1.4px 2.1px hsl(var(--shadow-color) / 0.12); + --shadow-elevation-high: 0px 0.1px 0.2px hsl(var(--shadow-color) / 0), + 0.1px 0.6px 0.9px hsl(var(--shadow-color) / 0.02), + 0.1px 1.1px 1.7px hsl(var(--shadow-color) / 0.04), + 0.1px 1.6px 2.4px hsl(var(--shadow-color) / 0.06), + 0.2px 2.2px 3.3px hsl(var(--shadow-color) / 0.07), + 0.2px 3.1px 4.7px hsl(var(--shadow-color) / 0.09), + 0.3px 4.3px 6.5px hsl(var(--shadow-color) / 0.11), + 0.5px 5.9px 8.9px hsl(var(--shadow-color) / 0.13); + --max-width: 1100px; --border-radius: 12px; --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", @@ -43,7 +60,7 @@ @media (prefers-color-scheme: dark) { :root { --foreground-rgb: 255, 255, 255; - --background-start-rgb: 10, 10, 10; + --background-start-rgb: 100, 10, 10; --background-end-rgb: 0, 0, 0; --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); @@ -88,12 +105,22 @@ body { body { color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); + + background-image: linear-gradient( + 45deg, + hsl(260deg 100% 2%) 0%, + hsl(236deg 45% 5%) 11%, + hsl(216deg 50% 7%) 22%, + hsl(205deg 67% 8%) 33%, + hsl(198deg 91% 8%) 44%, + hsl(193deg 100% 8%) 56%, + hsl(188deg 100% 9%) 67%, + hsl(181deg 100% 9%) 78%, + hsl(172deg 100% 10%) 89%, + hsl(164deg 100% 11%) 100% + ); + + padding: 2rem 0.8rem; display: flex; justify-content: center; @@ -105,6 +132,13 @@ h3 { margin-bottom: 1rem; } +h1 { + font-size: 3.6rem; + background: linear-gradient(90deg, #e21143, #ffb03a); + background-clip: text; + color: transparent; +} + p { margin: 1rem 0; } @@ -113,9 +147,10 @@ section { margin-bottom: 2rem; padding: 1rem; - background-color: rgba(25, 25, 25, 0.5); + background-color: rgba(0, 0, 0, 0.2); + + box-shadow: var(--shadow-elevation-high); - border: 2px solid rgba(20, 20, 20, 0.5); border-radius: 1rem; } diff --git a/example/app/midi/page.js b/example/app/midi/page.js deleted file mode 100644 index 79c47ee..0000000 --- a/example/app/midi/page.js +++ /dev/null @@ -1,228 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { useMIDI, useMIDINote, useMIDIControl } from "@react-midi/hooks"; - -import { useWindowSize } from "@uidotdev/usehooks"; -import { useCanvas } from "@kirkegaard/react-use-canvas"; - -const vs = `#version 300 es - -in vec4 position; - -void main() { - gl_Position = position; -} -`; - -const fs = `#version 300 es -precision highp float; - -out vec4 outColor; - -uniform mat4 u_midi01; -uniform mat4 u_midi02; -uniform float u_time; -uniform vec2 u_resolution; - -float box2(vec2 p,vec2 b) { - p = abs(p)-b; - return length(max(vec2(0.),p))+min(0.,max(p.x,p.y)); -} - -vec3 erot(vec3 p, vec3 ax, float t) { - return mix(dot(ax,p)*ax,p,cos(t))+cross(ax,p)*sin(t); -} - -void main() { - vec2 uv = (gl_FragCoord.xy - .5 * u_resolution.xy) / u_resolution.y; - - vec3 col = vec3(0); - vec3 p, d = normalize(vec3(uv, .75)); - - for(float i = 0., e = 0., j = 0.; i++ < 35.0 * u_midi01[2][2];) { - p = d * j / u_midi01[2][1]; - p.z += 1.0 + u_time / 1. * u_midi01[0][3]; - p.xy -= 3.15; - p = asin(sin(p / 2.) * .8) * 1.2; - float sc = .4 * u_midi01[2][0]; - - for(float j = 0.0; j++ < 6.;) { - p.xy = abs(p.xy) - .35; - p.xy *= 1. + u_midi01[0][0]; - p = erot(p, normalize(vec3( - u_midi01[1][0], - u_midi01[1][1], - u_midi01[1][2] - )), -.785); - sc *= 1.25; - } - - float h = box2( - erot( - p, - vec3(.0, .0, 1.2), - floor(1.0 + length(uv * uv) + pow(dot(uv, uv), 1.0) * .5)).xy, - vec2(.01) - ); - - h = min(h, box2(p.xz, vec2(u_midi01[3][0] / 2.))); - h = min(h, box2(p.yz, vec2(u_midi01[3][1] / 2.))); - h /= sc; - j += e = max(.001, h); - - col += (.8 + .3 * cos(vec3(.075, 2.1, .16) * i + floor(1. / 10. + length(uv * uv)))) * .1 / exp((.8 + p.z * .01) * i * i * e); - } - - float r = u_midi02[1][0] * (1. + u_midi02[0][0]); - float g = u_midi02[1][1] * (1. + u_midi02[0][1]); - float b = u_midi02[1][2] * (1. + u_midi02[0][2]); - float a = u_midi02[1][3] * (1. + u_midi02[0][3]); - - col *= vec3(r, g, b); - - outColor = vec4(col, 1.0); -} -`; - -function compileShader(gl, shaderSource, shaderType) { - const shader = gl.createShader(shaderType); - gl.shaderSource(shader, shaderSource); - gl.compileShader(shader); - - const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS); - if (!status) { - throw Error(`Could not compile shader: ${gl.getShaderInfoLog(shader)}`); - } - - return shader; -} - -function createProgram(gl, vertexShader, fragmentShader) { - const program = gl.createProgram(); - gl.attachShader(program, vertexShader); - gl.attachShader(program, fragmentShader); - - gl.linkProgram(program); - - const status = gl.getProgramParameter(program, gl.LINK_STATUS); - if (!status) { - throw Error(`Program failed to link: ${gl.getProgramInfoLog(program)}`); - } - - return program; -} - -function createUniform(gl, program, type, name) { - const location = gl.getUniformLocation(program, name); - return (...values) => { - gl[`uniform${type}`](location, ...values); - }; -} - -const normalize = (val, min, max) => (val - min) / (max - min); - -export default function FullPage() { - const [midi, setMidi] = useState([ - [ - 0.1889763779527559, 0, 0, 0.007874015748031496, 0.06299212598425197, 0, - 0.25196850393700787, 0, 0.6535433070866141, 1, 1, 0, 0, - 0.031496062992125984, 0, 0, - ], - [ - 0.8818897637795275, 0.08661417322834646, 0.1889763779527559, 1, 0, - 0.47244094488188976, 0.5905511811023622, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ], - ]); - - const { inputs } = useMIDI(); - const { control: value, value: control } = useMIDIControl(inputs[0]); - - let uniformTime = null; - let uniformResolution = null; - - let uniformMidi01 = null; - let uniformMidi02 = null; - - const setup = ({ context: gl, width, height }) => { - const vertexShader = compileShader(gl, vs, gl.VERTEX_SHADER); - const fragmentShader = compileShader(gl, fs, gl.FRAGMENT_SHADER); - const program = createProgram(gl, vertexShader, fragmentShader); - - const positionAttributeLocation = gl.getAttribLocation(program, "position"); - - const vao = gl.createVertexArray(); - gl.bindVertexArray(vao); - - const positionBuffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); - - gl.bufferData( - gl.ARRAY_BUFFER, - new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]), - gl.STATIC_DRAW - ); - - gl.enableVertexAttribArray(positionAttributeLocation); - gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0); - gl.viewport(0, 0, width, height); - - uniformTime = createUniform(gl, program, "1f", "u_time"); - uniformResolution = createUniform(gl, program, "2f", "u_resolution"); - uniformMidi01 = createUniform(gl, program, "Matrix4fv", "u_midi01"); - uniformMidi02 = createUniform(gl, program, "Matrix4fv", "u_midi02"); - - gl.useProgram(program); - - gl.bindVertexArray(vao); - }; - - const draw = ({ context: gl, time, width, height }) => { - uniformTime(time); - uniformResolution(width, height); - uniformMidi01(false, midi[0]); - uniformMidi02(false, midi[1]); - gl.drawArrays(gl.TRIANGLES, 0, 6); - }; - - const boards = [ - { start: 32, end: 47 }, - { start: 48, end: 55 }, - ]; - - useEffect(() => { - for (const [index, board] of boards.entries()) { - if (control >= board.start && control <= board.end) { - const k = Math.round( - normalize(control, board.start, board.end) * (board.end - board.start) - ); - const v = normalize(value, 0, 127); - midi[index][k] = v; - window.localStorage.setItem("midi", JSON.stringify(midi)); - } - } - }, [value]); - - useEffect(() => { - const current = window.localStorage.getItem("midi"); - if (current) { - setMidi(JSON.parse(current)); - } - }, []); - - const { height, width } = useWindowSize(); - const { ref } = useCanvas({ - setup, - draw, - options: { - height, - width, - contextType: "webgl2", - contextAttributes: { - antialias: false, - }, - }, - }); - - return ; -} diff --git a/example/app/page.js b/example/app/page.js index 97259ca..82c65aa 100644 --- a/example/app/page.js +++ b/example/app/page.js @@ -61,15 +61,13 @@ export default function Home() {

WebGL

If you're willing to write a shader pipeline, you can even use it - to render shaders! Here's one created by{" "} - kishimisu. I do - have plans for integrating a proper pipeline in the hook but for now - you'll have to write your own. Or copy the one from the example - :) -

-

- I've also added another example where - you can use midi controllers to control the shader. + to render shaders!{" "} + + Here's an old one from some time ago + + . I do have plans for integrating a proper pipeline in the hook but + for now you'll have to write your own. Or copy the one from the + examples :)

diff --git a/example/app/page.module.css b/example/app/page.module.css index 17c3910..695d5e5 100644 --- a/example/app/page.module.css +++ b/example/app/page.module.css @@ -1,4 +1,4 @@ .main { width: 100%; - max-width: 600px; + max-width: 800px; } diff --git a/example/package.json b/example/package.json index 517d345..ad76460 100644 --- a/example/package.json +++ b/example/package.json @@ -10,7 +10,6 @@ }, "dependencies": { "@kirkegaard/react-use-canvas": "^0.1.3", - "@react-midi/hooks": "^2.0.1", "@uidotdev/usehooks": "^2.4.1", "next": "14.1.3", "react": "^18", diff --git a/src/index.js b/src/index.js index 1d81287..48a7b06 100644 --- a/src/index.js +++ b/src/index.js @@ -80,6 +80,10 @@ export const useCanvas = ({ setup, draw, options = {} }) => { setup({ context, height, width }); } render(); + } else { + throw Error( + `Unable to get context of type "${contextType}". Is webgl enabled?` + ); } }