Skip to content

Commit 43e7bb4

Browse files
committed
feat: add Deferred Lighting example
1 parent 5c442cb commit 43e7bb4

File tree

107 files changed

+7318
-1353
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+7318
-1353
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
node_modules
2-
.idea
2+
.idea
3+
dist
4+
output

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Make sure that you have [Git LFS][git-lfs] installed and that you cloned the pro
1515
```
1616
4. Run one of the available examples, for instance:
1717
```shell
18-
npm run animating-with-code
18+
npm run deferred-lighting
1919
```
2020
Take a look at [`package.json`](./package.json) for the list of available examples.
2121

examples/animating-with-code/.gitignore

-1
This file was deleted.

examples/animating-with-code/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
"serve": "vite"
88
},
99
"dependencies": {
10-
"@motion-canvas/core": "*"
10+
"@motion-canvas/core": "^11.1.0"
1111
},
1212
"devDependencies": {
13-
"@motion-canvas/ui": "*",
14-
"@motion-canvas/vite-plugin": "*",
13+
"@motion-canvas/ui": "^11.1.0",
14+
"@motion-canvas/vite-plugin": "^11.1.0",
1515
"vite": "^3.0.0"
1616
}
1717
}

examples/animating-with-code/src/components/Mouse.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export interface MouseConfig extends ShapeConfig {
1111
@KonvaNode()
1212
export class Mouse extends Shape {
1313
@getset(1)
14-
public press: GetSet<MouseConfig['press'], this>;
14+
public declare press: GetSet<MouseConfig['press'], this>;
1515

1616
private isSelecting = false;
1717
private selectionStart: Vector2d;

examples/animating-with-code/src/components/Timeline.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ interface TimelineConfig extends ShapeConfig {
1414
@KonvaNode({centroid: false})
1515
export class Timeline extends Shape {
1616
@getset(100)
17-
public frames: GetSet<TimelineConfig['frames'], this>;
17+
public declare frames: GetSet<TimelineConfig['frames'], this>;
1818
@getset(10)
19-
public density: GetSet<TimelineConfig['density'], this>;
19+
public declare density: GetSet<TimelineConfig['density'], this>;
2020
@getset(0)
21-
public playhead: GetSet<TimelineConfig['playhead'], this>;
21+
public declare playhead: GetSet<TimelineConfig['playhead'], this>;
2222

2323
public constructor(config: ShapeConfig) {
2424
super(config);
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "@examples/deferred-lighting",
3+
"private": true,
4+
"description": "Source code for the Deferred Lighting video",
5+
"license": "MIT",
6+
"scripts": {
7+
"serve": "vite"
8+
},
9+
"dependencies": {
10+
"@motion-canvas/core": "^12.3.0",
11+
"@motion-canvas/2d": "^12.5.0"
12+
},
13+
"devDependencies": {
14+
"@motion-canvas/ui": "^12.5.0",
15+
"@motion-canvas/vite-plugin": "^12.6.0",
16+
"vite": "^3.0.0"
17+
}
18+
}
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {createSignal} from '@motion-canvas/core/lib/signals';
2+
import {loadImage} from '@motion-canvas/core/lib/media';
3+
import {Color, Vector2} from '@motion-canvas/core/lib/types';
4+
5+
export class Sampler {
6+
private static imageLookup: Record<string, HTMLImageElement> = {};
7+
private static async getImageFor(url: string) {
8+
if (url in this.imageLookup) {
9+
return this.imageLookup[url];
10+
}
11+
12+
const image = await loadImage(url);
13+
this.imageLookup[url] = image;
14+
return image;
15+
}
16+
17+
private readonly context: CanvasRenderingContext2D;
18+
private image: HTMLImageElement;
19+
private url = createSignal<string | null>(null);
20+
21+
public constructor() {
22+
const canvas = document.createElement('canvas');
23+
this.context = canvas.getContext('2d', {willReadFrequently: true});
24+
}
25+
26+
async setUrl(value: string) {
27+
this.image = await Sampler.getImageFor(value);
28+
this.context.canvas.width = this.image.naturalWidth;
29+
this.context.canvas.height = this.image.naturalHeight;
30+
this.context.drawImage(this.image, 0, 0);
31+
this.url(value);
32+
}
33+
34+
public getColorAtPoint(position: Vector2) {
35+
const data = this.context.getImageData(position.x, position.y, 1, 1).data;
36+
return new Color({
37+
r: data[0],
38+
g: data[1],
39+
b: data[2],
40+
a: data[3] / 255,
41+
});
42+
}
43+
}
44+
45+
export async function createSampler(url: string): Promise<Sampler> {
46+
const sampler = new Sampler();
47+
await sampler.setUrl(url);
48+
return sampler;
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:869f850de17b4eb0b52d83b445b40762d0b7abf3b8a23eaaaa066e607ae9bb31
3+
size 9248123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import {
2+
computed,
3+
initial,
4+
signal,
5+
vector2Signal,
6+
} from '@motion-canvas/2d/lib/decorators';
7+
import {Vector2, Vector2Signal} from '@motion-canvas/core/lib/types';
8+
import {VectorBase, VectorBaseProps} from './VectorBase';
9+
import {SimpleSignal} from '@motion-canvas/core/lib/signals';
10+
11+
export interface ArcVectorProps extends VectorBaseProps {
12+
counter?: boolean;
13+
x?: number;
14+
y?: number;
15+
position?: Vector2;
16+
from?: number;
17+
to?: number;
18+
radius?: number;
19+
}
20+
21+
export class ArcVector extends VectorBase {
22+
@initial(false)
23+
@signal()
24+
public declare readonly counter: SimpleSignal<boolean, this>;
25+
26+
@initial(0)
27+
@signal()
28+
public declare readonly from: SimpleSignal<number, this>;
29+
30+
@initial(90)
31+
@signal()
32+
public declare readonly to: SimpleSignal<number, this>;
33+
34+
@initial(100)
35+
@signal()
36+
public declare readonly radius: SimpleSignal<number, this>;
37+
38+
@vector2Signal()
39+
public declare readonly position: Vector2Signal<this>;
40+
41+
@computed()
42+
public length(): number {
43+
const to = this.to();
44+
const from = this.from();
45+
46+
let angle = to - from;
47+
angle %= 360;
48+
if (angle < 0) {
49+
angle += 360;
50+
}
51+
52+
const radius = this.radius();
53+
54+
return (Math.PI * radius * radius * angle) / 360;
55+
}
56+
57+
public constructor(props: ArcVectorProps) {
58+
super(props);
59+
}
60+
61+
@computed()
62+
public center(): Vector2 {
63+
const angle = (this.to() - this.from()) / 2;
64+
return Vector2.fromRadians((angle / 180) * Math.PI).scale(this.radius());
65+
}
66+
67+
public override localToParent(): DOMMatrix {
68+
return new DOMMatrix([1, 0, 0, 1, this.position.x(), this.position.y()]);
69+
}
70+
71+
protected override draw(context: CanvasRenderingContext2D) {
72+
context.save();
73+
this.applyStyle(context);
74+
const startArrow = this.startArrow();
75+
const endArrow = this.endArrow();
76+
const startOffset = this.startOffset();
77+
const endOffset = this.endOffset();
78+
let start = this.start();
79+
let end = this.end();
80+
81+
if (end < start) {
82+
[end, start] = [start, end];
83+
}
84+
85+
const counter = this.counter();
86+
const radius = this.radius();
87+
let from = (this.from() / 180) * Math.PI;
88+
let to = (this.to() / 180) * Math.PI;
89+
90+
from += counter ? startOffset / -radius : startOffset / radius;
91+
to -= counter ? endOffset / -radius : endOffset / radius;
92+
93+
let angle = to - from;
94+
to = from + angle * end;
95+
from = from + angle * start;
96+
angle = to - from;
97+
98+
if (angle !== 0) {
99+
const arrowSize = Math.min(
100+
this.arrowSize(),
101+
(Math.abs(angle) * radius) / 2,
102+
);
103+
let arrowDistance = arrowSize / radius;
104+
if (counter) {
105+
arrowDistance *= -1;
106+
}
107+
108+
context.beginPath();
109+
context.arc(
110+
0,
111+
0,
112+
radius,
113+
from + (startArrow ? arrowDistance : 0),
114+
to - (endArrow ? arrowDistance : 0),
115+
counter,
116+
);
117+
context.stroke();
118+
119+
if (startArrow) {
120+
const direction = Vector2.fromRadians(from).scale(radius);
121+
const arrow = Vector2.fromRadians(
122+
from + arrowDistance / 2,
123+
).perpendicular.scale(counter ? arrowSize : -arrowSize);
124+
125+
this.drawArrow(context, direction, arrow);
126+
}
127+
128+
if (endArrow) {
129+
const direction = Vector2.fromRadians(to).scale(radius);
130+
const arrow = Vector2.fromRadians(
131+
to - arrowDistance / 2,
132+
).perpendicular.scale(counter ? -arrowSize : arrowSize);
133+
134+
this.drawArrow(context, direction, arrow);
135+
}
136+
}
137+
138+
context.restore();
139+
super.draw(context);
140+
}
141+
}

0 commit comments

Comments
 (0)