Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add intersecion options #85

Merged
merged 24 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
dc2d9c2
refactor: add utilities for Rect
fand Jul 23, 2024
88034ef
chore: add growRect & shrinkRect
fand Jul 23, 2024
03bda1c
test: add test for Rect
fand Jul 23, 2024
c75fcd3
feat: add getIntersection
fand Jul 23, 2024
44b3fb3
refactor: split e.overflow and e.isFullScreen
fand Jul 23, 2024
e8d6432
refactor: use Rect operation in isRectInViewport
fand Jul 23, 2024
c305973
feat: add VFXProps.intersection
fand Jul 23, 2024
91d0d2e
fix: pass threshold in test
fand Jul 23, 2024
05f53cf
fix: change the boundary condition for adjacent rects in isRectInView…
fand Jul 23, 2024
95698ba
Revert "fix: change the boundary condition for adjacent rects in isRe…
fand Jul 25, 2024
0c24904
fix: consider adjacent rects to be intersecting
fand Jul 25, 2024
598c858
feat: add rootMargin
fand Jul 25, 2024
38fbfab
fix: consider elements to be inside viewport when the intersection ==…
fand Jul 25, 2024
f9ba7cc
feat: split viewport intersection and transition area intersection ca…
fand Jul 26, 2024
2c77c6e
feat: add out animation to transition shader presets
fand Jul 26, 2024
1622439
chore: add threshold property to transition examples
fand Jul 26, 2024
661204a
refactor: process overflow outside isRectInViewport
fand Aug 14, 2024
8f44fbd
refactor: check intersection and collision separately
fand Aug 14, 2024
33fa57a
chore: rename functions
fand Aug 14, 2024
5ed9403
chore: rename variables
fand Aug 14, 2024
027f893
feat: add "intersection" uniform
fand Aug 14, 2024
d5ad64a
chore: add comments
fand Aug 14, 2024
6b92782
fix: use actual BoundingRect for intersection calculation
fand Aug 14, 2024
f30be83
chore: update profile shader in docs
fand Aug 14, 2024
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
16 changes: 16 additions & 0 deletions packages/docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ <h3>Transitions</h3>
<img
src="./vfx-js-logo-no-padding.svg"
data-shader="slitScanTransition"
data-threshold="1"
/>
</div>
</div>
Expand All @@ -405,6 +406,7 @@ <h3>Transitions</h3>
<img
src="./vfx-js-logo-no-padding.svg"
data-shader="warpTransition"
data-threshold="1"
/>
</div>
</div>
Expand All @@ -418,6 +420,20 @@ <h3>Transitions</h3>
<img
src="./vfx-js-logo-no-padding.svg"
data-shader="pixelateTransition"
data-threshold="1"
/>
</div>
</div>
<div class="row">
<div class="col">
<pre><code class="language-javascript">
vfx.add(el, { shader: "focusTransition" });
</code></pre>
</div>
<div class="col">
<img
src="./vfx-js-logo-no-padding.svg"
data-shader="focusTransition"
/>
</div>
</div>
Expand Down
23 changes: 21 additions & 2 deletions packages/docs/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const shaders: Record<string, string> = {
uniform vec2 offset;
uniform float time;
uniform float enterTime;
uniform float leaveTime;
uniform sampler2D src;

uniform float delay;
Expand Down Expand Up @@ -79,7 +80,10 @@ const shaders: Record<string, string> = {

void main (void) {
vec2 uv = (gl_FragCoord.xy - offset) / resolution;
if (enterTime < 1.0) {
if (leaveTime > 0.) {
float t = clamp(leaveTime - 0.5, 0., 1.);
gl_FragColor = glitch(uv) * (1. - t);
} else if (enterTime < 1.0) {
gl_FragColor = slitscan(uv);
} else {
gl_FragColor = glitch(uv);
Expand Down Expand Up @@ -229,6 +233,11 @@ class App {
shader,
overflow: parseFloat(e.getAttribute("data-overflow") ?? "0"),
uniforms,
intersection: {
threshold: parseFloat(
e.getAttribute("data-threshold") ?? "0",
),
},
});
}
}
Expand Down Expand Up @@ -349,22 +358,32 @@ class App {
shader: shaders.logo,
overflow: [0, 3000, 0, 100],
uniforms: { delay: 0 },
intersection: {
threshold: 1,
},
});

const tagline = document.getElementById("LogoTagline")!;
this.vfx.add(tagline, {
shader: shaders.logo,
overflow: [0, 3000, 0, 1000],
uniforms: { delay: 0.3 },
intersection: {
threshold: 1,
},
});
}

showProfile() {
const profile = document.getElementById("profile")!;
this.vfx.add(profile, {
shader: shaders.logo,
overflow: [0, 2000, 0, 1000],
overflow: [0, 2000, 0, 2000],
uniforms: { delay: 0.5 },
intersection: {
rootMargin: [-100, 0, -100, 0],
threshold: 1,
},
});
}
}
Expand Down
69 changes: 61 additions & 8 deletions packages/vfx-js/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export type ShaderPreset =
| "halftone"
| "slitScanTransition"
| "warpTransition"
| "pixelateTransition";
| "pixelateTransition"
| "focusTransition";

/**
* Shader code for presets.
Expand Down Expand Up @@ -578,13 +579,23 @@ export const shaders: Record<ShaderPreset, string> = {
uniform vec2 offset;
uniform float time;
uniform float enterTime;
uniform float leaveTime;
uniform sampler2D src;

#define DURATION 1.0

void main (void) {
vec2 uv = (gl_FragCoord.xy - offset) / resolution;

if (enterTime < 1.5) {
float t = enterTime / 1.5;
float t1 = enterTime / DURATION;
float t2 = leaveTime / DURATION;
float t = clamp(min(t1, 1. - t2), 0., 1.);

if (t == 0.) {
discard;
}

if (t < 1.) {
uv.x += sin(floor(uv.y * 300.)) * 3. * exp(t * -10.);
}

Expand All @@ -597,14 +608,30 @@ export const shaders: Record<ShaderPreset, string> = {
uniform vec2 offset;
uniform float time;
uniform float enterTime;
uniform float leaveTime;
uniform sampler2D src;

#define DURATION 1.0

void main (void) {
vec2 uv = (gl_FragCoord.xy - offset) / resolution;

if (enterTime < 1.5) {
float t = 1. - enterTime / 1.5;
uv.y = uv.y > t ? uv.y : t;
float t1 = enterTime / DURATION;
float t2 = leaveTime / DURATION;

// Do not render before enter or after leave
if (t1 < 0. || 1. < t2) {
discard;
}

if (0. < t2) {
// Leaving
float t = 1. - t2;
uv.y = uv.y < t ? uv.y : t;
} else if (t1 < 1.) {
// Entering
float t = 1. - t1;
uv.y = uv.y < t ? t : uv.y;
}

gl_FragColor = texture2D(src, uv);
Expand All @@ -616,19 +643,45 @@ export const shaders: Record<ShaderPreset, string> = {
uniform vec2 offset;
uniform float time;
uniform float enterTime;
uniform float leaveTime;
uniform sampler2D src;

#define DURATION 1.0

void main (void) {
vec2 uv = (gl_FragCoord.xy - offset) / resolution;

if (enterTime < 1.5) {
float t = enterTime / 1.5;
float t1 = enterTime / DURATION;
float t2 = leaveTime / DURATION;
float t = clamp(min(t1, 1. - t2), 0., 1.);

if (t == 0.) {
discard;
} else if (t < 1.) {
float b = floor(t * 64.);
uv = (floor(uv * b) + .5) / b;
}

gl_FragColor = texture2D(src, uv);
}
`,
focusTransition: `
precision highp float;
uniform vec2 resolution;
uniform vec2 offset;
uniform float time;
uniform float intersection;
uniform sampler2D src;

void main (void) {
vec2 uv = (gl_FragCoord.xy - offset) / resolution;
float t = smoothstep(0., 1., intersection);

gl_FragColor = mix(
texture2D(src, uv + vec2(1. - t, 0)),
texture2D(src, uv + vec2(-(1. - t), 0)),
0.5
) * intersection;
}
`,
};
129 changes: 129 additions & 0 deletions packages/vfx-js/src/rect.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { expect, describe, test } from "vitest";
import { createRect, getIntersection, growRect, shrinkRect } from "./rect";

describe("createRect", () => {
test("single number", () => {
expect(createRect(1)).toStrictEqual({
top: 1,
right: 1,
bottom: 1,
left: 1,
});
});

test("array", () => {
expect(createRect([1, 2, 3, 4])).toStrictEqual({
top: 1,
right: 2,
bottom: 3,
left: 4,
});
});

test("object", () => {
expect(
createRect({ top: 1, right: 2, bottom: 3, left: 4 }),
).toStrictEqual({
top: 1,
right: 2,
bottom: 3,
left: 4,
});
});
});

describe("growRect", () => {
test("positive values", () => {
const a = {
top: 100,
right: 200,
bottom: 200,
left: 100,
};
const b = createRect(1);
expect(growRect(a, b)).toStrictEqual({
top: 99,
right: 201,
bottom: 201,
left: 99,
});
});
test("negative values", () => {
const a = {
top: 100,
right: 200,
bottom: 200,
left: 100,
};
const b = createRect(-1);
expect(growRect(a, b)).toStrictEqual({
top: 101,
right: 199,
bottom: 199,
left: 101,
});
});
});

describe("shrinkRect", () => {
test("positive values", () => {
const a = {
top: 100,
right: 200,
bottom: 200,
left: 100,
};
const b = createRect(1);
expect(shrinkRect(a, b)).toStrictEqual({
top: 101,
right: 199,
bottom: 199,
left: 101,
});
});
test("negative values", () => {
const a = {
top: 100,
right: 200,
bottom: 200,
left: 100,
};
const b = createRect(-1);
expect(shrinkRect(a, b)).toStrictEqual({
top: 99,
right: 201,
bottom: 201,
left: 99,
});
});
});

describe("getIntersection", () => {
test("no intersection", () => {
const a = createRect([0, 1, 1, 0]);
const b = createRect([0, 2, 1, 1]);
expect(getIntersection(a, b)).toBe(0);
});
test("full intersection", () => {
expect(
getIntersection(createRect([0, 1, 1, 0]), createRect([0, 1, 1, 0])),
).toBe(1);
expect(
getIntersection(
createRect([0, 10, 10, 1]),
createRect([1, 2, 2, 1]),
),
).toBe(1);
});
test("partial intersection", () => {
expect(
getIntersection(createRect([0, 2, 1, 0]), createRect([0, 1, 1, 0])),
).toBe(1); // target is fully covered by container
expect(
getIntersection(createRect([0, 1, 1, 0]), createRect([0, 2, 1, 0])),
).toBe(0.5); // 50% of target is covered by container
expect(
getIntersection(createRect([1, 2, 2, 1]), createRect([0, 2, 2, 0])),
).toBe(0.25); // 25%
});
});
Loading
Loading