Skip to content

Commit

Permalink
feat: support custom uniforms
Browse files Browse the repository at this point in the history
  • Loading branch information
fand committed Jul 17, 2020
1 parent 9a534c1 commit 8336eb5
Show file tree
Hide file tree
Showing 15 changed files with 161 additions and 36 deletions.
16 changes: 8 additions & 8 deletions docs/asset-manifest.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
{
"files": {
"main.css": "/react-vfx/static/css/main.b22d7dc7.chunk.css",
"main.js": "/react-vfx/static/js/main.6e5e9de0.chunk.js",
"main.js.map": "/react-vfx/static/js/main.6e5e9de0.chunk.js.map",
"main.js": "/react-vfx/static/js/main.c60ce1f4.chunk.js",
"main.js.map": "/react-vfx/static/js/main.c60ce1f4.chunk.js.map",
"runtime-main.js": "/react-vfx/static/js/runtime-main.73b26e45.js",
"runtime-main.js.map": "/react-vfx/static/js/runtime-main.73b26e45.js.map",
"static/js/2.bf7cab29.chunk.js": "/react-vfx/static/js/2.bf7cab29.chunk.js",
"static/js/2.bf7cab29.chunk.js.map": "/react-vfx/static/js/2.bf7cab29.chunk.js.map",
"static/js/2.994023f2.chunk.js": "/react-vfx/static/js/2.994023f2.chunk.js",
"static/js/2.994023f2.chunk.js.map": "/react-vfx/static/js/2.994023f2.chunk.js.map",
"index.html": "/react-vfx/index.html",
"precache-manifest.4728e449b2629e34986daa9e3f7a7117.js": "/react-vfx/precache-manifest.4728e449b2629e34986daa9e3f7a7117.js",
"precache-manifest.d124989437cd11a2c0969974ba7a99a1.js": "/react-vfx/precache-manifest.d124989437cd11a2c0969974ba7a99a1.js",
"service-worker.js": "/react-vfx/service-worker.js",
"static/css/main.b22d7dc7.chunk.css.map": "/react-vfx/static/css/main.b22d7dc7.chunk.css.map",
"static/js/2.bf7cab29.chunk.js.LICENSE": "/react-vfx/static/js/2.bf7cab29.chunk.js.LICENSE"
"static/js/2.994023f2.chunk.js.LICENSE": "/react-vfx/static/js/2.994023f2.chunk.js.LICENSE"
},
"entrypoints": [
"static/js/runtime-main.73b26e45.js",
"static/js/2.bf7cab29.chunk.js",
"static/js/2.994023f2.chunk.js",
"static/css/main.b22d7dc7.chunk.css",
"static/js/main.6e5e9de0.chunk.js"
"static/js/main.c60ce1f4.chunk.js"
]
}
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>REACT-VFX - WebGL effects for React! #REACTVFX</title><meta name="theme-color" content="#000000"/><meta name="description" content="REACT-VFX is a React component library which allows you to add WebGL powered effects to you React aaplication."/><link rel="apple-touch-icon" href="/react-vfx/logo-with-bg.png"/><meta property="og:url" content="https://amagi.dev/react-vfx/"/><meta property="og:type" content="website"/><meta property="og:title" content="REACT-VFX - WebGL effects for React! #REACTVFX"/><meta property="og:image" content="/react-vfx/logo-with-bg.png"/><meta property="og:description" content="REACT-VFX is a React component library which allows you to add WebGL powered effects to you React aaplication."/><meta property="og:site_name" content="REACT-VFX - WebGL effects for React! #REACTVFX"/><meta property="og:locale" content="en_US"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:site" content="@amagitakayosi"/><meta name="twitter:creator" content="@amagitakayosi"/><meta name="twitter:url" content="https://amagi.dev/react-vfx/"/><meta name="twitter:title" content="REACT-VFX - WebGL effects for React! #REACTVFX"/><meta name="twitter:description" content="REACT-VFX is a React component library which allows you to add WebGL powered effects to you React aaplication."/><meta name="twitter:image" content="https://amagi.dev/react-vfx/logo-with-bg.png"/><link rel="icon" href="/react-vfx/favicon/favicon.ico"/><link rel="icon" type="image/x-icon" sizes="16x16 32x32" href="/react-vfx/favicon/favicon-36.png"/><link rel="apple-touch-icon" sizes="180x180" href="/react-vfx/favicion/favicon-180.png"/><link rel="icon" sizes="192x192" href="/react-vfx/favicon/favicon-192.png"/><link href="/react-vfx/static/css/main.b22d7dc7.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(c){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,a=[];f<n.length;f++)t=n[f],Object.prototype.hasOwnProperty.call(i,t)&&i[t]&&a.push(i[t][0]),i[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(c[r]=o[r]);for(s&&s(e);a.length;)a.shift()();return p.push.apply(p,u||[]),l()}function l(){for(var e,r=0;r<p.length;r++){for(var t=p[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==i[u]&&(n=!1)}n&&(p.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},i={1:0},p=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return c[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=c,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/react-vfx/";var r=this["webpackJsonpreact-vfx-docs"]=this["webpackJsonpreact-vfx-docs"]||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;l()}([])</script><script src="/react-vfx/static/js/2.bf7cab29.chunk.js"></script><script src="/react-vfx/static/js/main.6e5e9de0.chunk.js"></script></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>REACT-VFX - WebGL effects for React! #REACTVFX</title><meta name="theme-color" content="#000000"/><meta name="description" content="REACT-VFX is a React component library which allows you to add WebGL powered effects to you React aaplication."/><link rel="apple-touch-icon" href="/react-vfx/logo-with-bg.png"/><meta property="og:url" content="https://amagi.dev/react-vfx/"/><meta property="og:type" content="website"/><meta property="og:title" content="REACT-VFX - WebGL effects for React! #REACTVFX"/><meta property="og:image" content="/react-vfx/logo-with-bg.png"/><meta property="og:description" content="REACT-VFX is a React component library which allows you to add WebGL powered effects to you React aaplication."/><meta property="og:site_name" content="REACT-VFX - WebGL effects for React! #REACTVFX"/><meta property="og:locale" content="en_US"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:site" content="@amagitakayosi"/><meta name="twitter:creator" content="@amagitakayosi"/><meta name="twitter:url" content="https://amagi.dev/react-vfx/"/><meta name="twitter:title" content="REACT-VFX - WebGL effects for React! #REACTVFX"/><meta name="twitter:description" content="REACT-VFX is a React component library which allows you to add WebGL powered effects to you React aaplication."/><meta name="twitter:image" content="https://amagi.dev/react-vfx/logo-with-bg.png"/><link rel="icon" href="/react-vfx/favicon/favicon.ico"/><link rel="icon" type="image/x-icon" sizes="16x16 32x32" href="/react-vfx/favicon/favicon-36.png"/><link rel="apple-touch-icon" sizes="180x180" href="/react-vfx/favicion/favicon-180.png"/><link rel="icon" sizes="192x192" href="/react-vfx/favicon/favicon-192.png"/><link href="/react-vfx/static/css/main.b22d7dc7.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(c){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,a=[];f<n.length;f++)t=n[f],Object.prototype.hasOwnProperty.call(i,t)&&i[t]&&a.push(i[t][0]),i[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(c[r]=o[r]);for(s&&s(e);a.length;)a.shift()();return p.push.apply(p,u||[]),l()}function l(){for(var e,r=0;r<p.length;r++){for(var t=p[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==i[u]&&(n=!1)}n&&(p.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},i={1:0},p=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return c[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=c,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/react-vfx/";var r=this["webpackJsonpreact-vfx-docs"]=this["webpackJsonpreact-vfx-docs"]||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;l()}([])</script><script src="/react-vfx/static/js/2.994023f2.chunk.js"></script><script src="/react-vfx/static/js/main.c60ce1f4.chunk.js"></script></body></html>
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "c9b81ed5224f4ece3f3e4600326dcda8",
"revision": "b11af0761aee2c324a339ff4f3da23b0",
"url": "/react-vfx/index.html"
},
{
"revision": "d0088c10b773f017072b",
"revision": "01a3297aa427b84e46a2",
"url": "/react-vfx/static/css/main.b22d7dc7.chunk.css"
},
{
"revision": "afa608b6be80c5b8f7ef",
"url": "/react-vfx/static/js/2.bf7cab29.chunk.js"
"revision": "35579c88253663f8bd51",
"url": "/react-vfx/static/js/2.994023f2.chunk.js"
},
{
"revision": "9d3a2d98b698f760103d62357ad1d9e9",
"url": "/react-vfx/static/js/2.bf7cab29.chunk.js.LICENSE"
"url": "/react-vfx/static/js/2.994023f2.chunk.js.LICENSE"
},
{
"revision": "d0088c10b773f017072b",
"url": "/react-vfx/static/js/main.6e5e9de0.chunk.js"
"revision": "01a3297aa427b84e46a2",
"url": "/react-vfx/static/js/main.c60ce1f4.chunk.js"
},
{
"revision": "717b0a951c92951e26a7",
Expand Down
2 changes: 1 addition & 1 deletion docs/service-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");

importScripts(
"/react-vfx/precache-manifest.4728e449b2629e34986daa9e3f7a7117.js"
"/react-vfx/precache-manifest.d124989437cd11a2c0969974ba7a99a1.js"
);

self.addEventListener('message', (event) => {
Expand Down

Large diffs are not rendered by default.

File renamed without changes.

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions docs/static/js/main.6e5e9de0.chunk.js

This file was deleted.

1 change: 0 additions & 1 deletion docs/static/js/main.6e5e9de0.chunk.js.map

This file was deleted.

2 changes: 2 additions & 0 deletions docs/static/js/main.c60ce1f4.chunk.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/static/js/main.c60ce1f4.chunk.js.map

Large diffs are not rendered by default.

88 changes: 87 additions & 1 deletion docs_src/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,22 @@ void main() {
}
`;

const scrollByScroll = `
uniform vec2 resolution;
uniform vec2 offset;
uniform float scroll; // custom uniform passed as React props
uniform sampler2D src;
void main() {
vec2 uv = (gl_FragCoord.xy - offset) / resolution;
// scroll X by scroll
uv.x = fract(uv.x + scroll * 30.);
gl_FragColor = texture2D(src, uv);
}
`;

const App: React.FC = () => {
return (
<VFX.VFXProvider pixelRatio={1}>
Expand Down Expand Up @@ -232,7 +248,7 @@ const App: React.FC = () => {
transition effects.{" "}
</p>
<Code>{dedent`
import { VFXImg } from 'react-vfx';
import { VFXSpan } from 'react-vfx';
const fadeIn = \`
uniform vec2 resolution;
Expand Down Expand Up @@ -266,6 +282,76 @@ const App: React.FC = () => {
I'm fading!
</VFX.VFXSpan>
</section>
<section className="Secton4">
<h3>Custom Uniforms</h3>
<p>
REACT-VFX accepts custom uniform variables as
`uniforms`. You can pass objects of parameters or
functions:
</p>
<Code>
{dedent`
// dictionary of parameters or functions
export type VFXUniforms = {
[name: string]: VFXUniformValue | (() => VFXUniformValue);
};
// REACT-VFX currently supports float, vec2, vec3 and vec4.
export type VFXUniformValue =
| number // float
| [number, number] // vec2
| [number, number, number] // vec3
| [number, number, number, number]; // vec4
`}
</Code>
<p>
If a parameter frequently changes over time (e.g.
scroll position), consider passing it as a function
than a native value to avoid performance problem.
</p>
<Code>{dedent`
import { VFXSpan } from 'react-vfx';
const scrollByScroll = \`
uniform vec2 resolution;
uniform vec2 offset;
uniform float scroll; // custom uniform passed as props
uniform sampler2D src;
void main() {
vec2 uv = (gl_FragCoord.xy - offset) / resolution;
// scroll X by scroll
uv.x = fract(uv.x + scroll * 30.);
gl_FragColor = texture2D(src, uv);
}
\`;
export default = () => (
<VFXSpan shader={scrollByScroll} uniforms={{
scroll: () => window.scrollY / (document.body.scrollHeight - window.innerHeight);
}}>I'm blinking!</VFXSpan>
);
`}</Code>
<p>This renders like this:</p>
<VFX.VFXSpan
shader={scrollByScroll}
style={{
fontSize: "72px",
fontWeight: "bold",
fontStyle: "italic"
}}
uniforms={{
scroll: () =>
window.scrollY /
(document.body.scrollHeight -
window.innerHeight)
}}
>
I'm scrolling!
</VFX.VFXSpan>
</section>
</section>
<AuthorSection />
</div>
Expand Down
6 changes: 4 additions & 2 deletions src/element.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ function VFXElementFactory<T extends keyof JSX.IntrinsicElements>(
}

const shader = props.shader;
player?.addElement(element, { shader });
const release = props.release;
const uniforms = props.uniforms;
player?.addElement(element, { shader, uniforms, release });

return () => {
player?.removeElement(element);
};
}, [ref, player, props.shader]);
}, [ref, player, props.shader, props.release, props.uniforms]);

// Rerender if the content is updated
useEffect(() => {
Expand Down
14 changes: 14 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import THREE from "three";

export interface VFXProps {
shader?: string;
release?: number;
uniforms?: VFXUniforms;
}

export type VFXUniforms = {
[name: string]: VFXUniformValue | (() => VFXUniformValue);
};

export type VFXUniformValue =
| number // float
| [number, number] // vec2
| [number, number, number] // vec3
| [number, number, number, number]; // vec4

export type VFXElementType = "img" | "video" | "text";

export interface VFXElement {
Expand All @@ -13,6 +26,7 @@ export interface VFXElement {
height: number;
scene: THREE.Scene;
uniforms: { [name: string]: THREE.IUniform };
uniformGenerators: { [name: string]: () => VFXUniformValue };
startTime: number;
enterTime: number;
leaveTime: number;
Expand Down
41 changes: 32 additions & 9 deletions src/vfx-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as THREE from "three";
import dom2canvas from "./dom-to-canvas";
import { shaders, DEFAULT_VERTEX_SHADER } from "./constants";
import GIFData from "./gif";
import { VFXProps, VFXElement, VFXElementType } from "./types";
import { VFXProps, VFXElement, VFXElementType, VFXUniformValue } from "./types";

const gifFor = new Map<HTMLElement, GIFData>();

Expand Down Expand Up @@ -174,19 +174,37 @@ export default class VFXPlayer {
const opacity = type === "video" ? "0.0001" : "0"; // don't hide video element completely to prevent jank frames
element.style.setProperty("opacity", opacity);

const uniforms = {
src: { type: "t", value: texture },
const uniforms: { [name: string]: THREE.IUniform } = {
src: { value: texture },
resolution: {
type: "v2",
value: new THREE.Vector2()
},
offset: { type: "v2", value: new THREE.Vector2() },
time: { type: "f", value: 0.0 },
enterTime: { type: "f", value: -1.0 },
leaveTime: { type: "f", value: -1.0 },
mouse: { type: "v2", value: new THREE.Vector2() }
offset: { value: new THREE.Vector2() },
time: { value: 0.0 },
enterTime: { value: -1.0 },
leaveTime: { value: -1.0 },
mouse: { value: new THREE.Vector2() }
};

const uniformGenerators: {
[name: string]: () => VFXUniformValue;
} = {};

if (opts.uniforms !== undefined) {
const keys = Object.keys(opts.uniforms);
for (const key of keys) {
const value = opts.uniforms[key];
if (typeof value === "function") {
uniforms[key] = {
value: value()
};
uniformGenerators[key] = value;
} else {
uniforms[key] = { value };
}
}
}

const scene = new THREE.Scene();
const geometry = new THREE.PlaneGeometry(2, 2);

Expand Down Expand Up @@ -214,6 +232,7 @@ export default class VFXPlayer {
height: rect.height,
scene,
uniforms,
uniformGenerators,
startTime: now,
enterTime: isInViewport ? now : -1,
leaveTime: Infinity,
Expand Down Expand Up @@ -290,6 +309,10 @@ export default class VFXPlayer {
e.uniforms["mouse"].value.x = this.mouseX * this.pixelRatio;
e.uniforms["mouse"].value.y = this.mouseY * this.pixelRatio;

for (const [key, gen] of Object.entries(e.uniformGenerators)) {
e.uniforms[key].value = gen();
}

// Update GIF frame
const gif = gifFor.get(e.element);
if (gif !== undefined) {
Expand Down

0 comments on commit 8336eb5

Please sign in to comment.