Copy files
This commit is contained in:
commit
36f01c6716
22 changed files with 500 additions and 0 deletions
4
src/helper/clamp.ts
Normal file
4
src/helper/clamp.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export const clamp = (value: number, min: number, max: number): number =>
|
||||
Math.min(max, Math.max(min, value));
|
||||
|
||||
export const clamp01 = (value: number): number => Math.min(1, Math.max(0, value));
|
||||
23
src/helper/delta-time-calculator.ts
Normal file
23
src/helper/delta-time-calculator.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
export class DeltaTimeCalculator {
|
||||
private previousTime: DOMHighResTimeStamp | null = null;
|
||||
|
||||
constructor() {
|
||||
document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
|
||||
}
|
||||
|
||||
public getNextDeltaTime(currentTime: DOMHighResTimeStamp): DOMHighResTimeStamp {
|
||||
if (this.previousTime === null) {
|
||||
this.previousTime = currentTime;
|
||||
}
|
||||
|
||||
const delta = currentTime - this.previousTime;
|
||||
this.previousTime = currentTime;
|
||||
return delta;
|
||||
}
|
||||
|
||||
private handleVisibilityChange() {
|
||||
if (!document.hidden) {
|
||||
this.previousTime = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
src/helper/last.ts
Normal file
3
src/helper/last.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export function last<T>(a: Array<T>): T | null {
|
||||
return a.length > 0 ? a[a.length - 1] : null;
|
||||
}
|
||||
1
src/helper/mix.ts
Normal file
1
src/helper/mix.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export const mix = (from: number, to: number, q: number) => from + (to - from) * q;
|
||||
18
src/helper/random.ts
Normal file
18
src/helper/random.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// source:
|
||||
// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript
|
||||
// Mulberry32
|
||||
|
||||
export abstract class Random {
|
||||
private static _seed = 42;
|
||||
|
||||
public static set seed(value: number) {
|
||||
Random._seed = value;
|
||||
}
|
||||
|
||||
public static getRandom(): number {
|
||||
let t = (Random._seed += 0x6d2b79f5);
|
||||
t = Math.imul(t ^ (t >>> 15), t | 1);
|
||||
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||||
}
|
||||
}
|
||||
1
src/helper/to-percent.ts
Normal file
1
src/helper/to-percent.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export const toPercent = (value: number) => `${Math.round(value * 100)}%`;
|
||||
3
src/helper/wait.ts
Normal file
3
src/helper/wait.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export const wait = (ms: number): Promise<void> => {
|
||||
return new Promise<void>((resolve, _) => setTimeout(resolve, ms));
|
||||
};
|
||||
14
src/index.html
Normal file
14
src/index.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<h1>Javascript is required for this website.</h1>
|
||||
</noscript>
|
||||
<div id="overlay"></div>
|
||||
<canvas id="main"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
35
src/index.scss
Normal file
35
src/index.scss
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
html,
|
||||
body,
|
||||
canvas#main {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
|
||||
#overlay {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 0.6em;
|
||||
margin: 0.75em 1em;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
||||
$outline-width: 0.5px;
|
||||
text-shadow: -$outline-width -$outline-width 0 #000,
|
||||
$outline-width -$outline-width 0 #000, -$outline-width $outline-width 0 #000,
|
||||
$outline-width $outline-width 0 #000;
|
||||
|
||||
color: white;
|
||||
white-space: pre;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
font-size: 0.55em;
|
||||
}
|
||||
}
|
||||
|
||||
canvas#main {
|
||||
background-color: hotpink;
|
||||
}
|
||||
}
|
||||
76
src/index.ts
Normal file
76
src/index.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import { glMatrix, vec2, vec3 } from 'gl-matrix';
|
||||
import { compile, Renderer } from 'sdf-2d';
|
||||
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
||||
import './index.scss';
|
||||
import { RainScene } from './scenes/rain-scene';
|
||||
import { Scene } from './scenes/scene';
|
||||
|
||||
glMatrix.setMatrixArrayType(Array);
|
||||
|
||||
const deltaTimeCalculator = new DeltaTimeCalculator();
|
||||
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
|
||||
const overlay = document.querySelector('#overlay') as HTMLDivElement;
|
||||
|
||||
const drawFrame = (
|
||||
renderer: Renderer,
|
||||
scene: Scene,
|
||||
deltaTime: DOMHighResTimeStamp,
|
||||
currentTime: DOMHighResTimeStamp
|
||||
) => {
|
||||
const { width, height } = canvas.getBoundingClientRect();
|
||||
renderer.setViewArea(vec2.fromValues(0, height), vec2.fromValues(width, height));
|
||||
renderer.autoscaleQuality(deltaTime);
|
||||
|
||||
overlay.innerText = JSON.stringify(
|
||||
renderer.insights,
|
||||
(_, v) => (v.toFixed ? Number(v.toFixed(2)) : v),
|
||||
' '
|
||||
);
|
||||
|
||||
scene.animate(currentTime, { width, height });
|
||||
|
||||
scene.drawables.forEach((d) => renderer.addDrawable(d));
|
||||
renderer.renderDrawables();
|
||||
};
|
||||
|
||||
const handleScene = async (scene: Scene) => {
|
||||
const renderer = compile(
|
||||
canvas,
|
||||
scene.descriptors,
|
||||
[vec3.fromValues(0.4, 0.35, 0.6), vec3.fromValues(0, 1, 0), vec3.fromValues(1, 1, 0)],
|
||||
{ tileMultiplier: 8, enableStopwatch: false }
|
||||
);
|
||||
|
||||
let triggerIsOver: () => void;
|
||||
const isOver = new Promise((resolve) => (triggerIsOver = resolve));
|
||||
|
||||
let timeSinceStart = 0;
|
||||
|
||||
const handleFrame = (currentTime: DOMHighResTimeStamp) => {
|
||||
const deltaTime = deltaTimeCalculator.getNextDeltaTime(currentTime);
|
||||
|
||||
drawFrame(renderer, scene, deltaTime, currentTime);
|
||||
|
||||
if ((timeSinceStart += deltaTime) > 200000 * 1000) {
|
||||
triggerIsOver();
|
||||
} else {
|
||||
requestAnimationFrame(handleFrame);
|
||||
}
|
||||
};
|
||||
|
||||
requestAnimationFrame(handleFrame);
|
||||
await isOver;
|
||||
|
||||
renderer.destroy();
|
||||
};
|
||||
|
||||
const scenes = [new RainScene()];
|
||||
|
||||
const main = async () => {
|
||||
let i = 0;
|
||||
for (;;) {
|
||||
await handleScene(scenes[i++ % scenes.length]);
|
||||
}
|
||||
};
|
||||
|
||||
main();
|
||||
62
src/scenes/rain-scene.ts
Normal file
62
src/scenes/rain-scene.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { CircleLight, Drawable, DrawableDescriptor, Flashlight, Tunnel } from 'sdf-2d';
|
||||
import { Random } from '../helper/random';
|
||||
import { Scene } from './scene';
|
||||
|
||||
export class RainScene implements Scene {
|
||||
public droplets: Array<Droplet> = [];
|
||||
|
||||
public constructor() {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
this.droplets.push(new Droplet());
|
||||
}
|
||||
}
|
||||
|
||||
public get descriptors(): Array<DrawableDescriptor> {
|
||||
return [Tunnel.descriptor, CircleLight.descriptor, Flashlight.descriptor];
|
||||
}
|
||||
|
||||
public animate(
|
||||
currentTime: number,
|
||||
viewAreaSize: { width: number; height: number }
|
||||
): void {
|
||||
this.droplets.forEach((d) => d.animate(currentTime, viewAreaSize));
|
||||
}
|
||||
|
||||
public get drawables(): Array<Drawable> {
|
||||
return this.droplets.map((d) => d.drawable);
|
||||
}
|
||||
}
|
||||
|
||||
class Droplet {
|
||||
public readonly drawable: Tunnel;
|
||||
|
||||
private speed = Random.getRandom() * 0.2 + 0.1;
|
||||
private position = vec2.fromValues(Random.getRandom(), Random.getRandom());
|
||||
private length = Random.getRandom() * 30 + 8;
|
||||
|
||||
constructor() {
|
||||
const size = Random.getRandom() * 10 + 5;
|
||||
|
||||
this.drawable = new Tunnel(
|
||||
vec2.create(),
|
||||
vec2.create(),
|
||||
size + Random.getRandom() * 5 + 5,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
public animate(
|
||||
currentTime: number,
|
||||
{ width, height }: { width: number; height: number }
|
||||
) {
|
||||
const heightOffset = 300;
|
||||
vec2.set(
|
||||
this.drawable.from,
|
||||
this.position[0] * width,
|
||||
height - ((this.speed * currentTime) % (height + heightOffset))
|
||||
);
|
||||
|
||||
vec2.add(this.drawable.to, this.drawable.from, vec2.fromValues(0, this.length));
|
||||
}
|
||||
}
|
||||
10
src/scenes/scene.ts
Normal file
10
src/scenes/scene.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { Drawable, DrawableDescriptor } from 'sdf-2d';
|
||||
|
||||
export interface Scene {
|
||||
readonly descriptors: Array<DrawableDescriptor>;
|
||||
animate(
|
||||
currentTime: DOMHighResTimeStamp,
|
||||
viewAreaSize: { width: number; height: number }
|
||||
): void;
|
||||
readonly drawables: Array<Drawable>;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue