Copy files
This commit is contained in:
commit
36f01c6716
22 changed files with 500 additions and 0 deletions
1
.eslintignore
Normal file
1
.eslintignore
Normal file
|
|
@ -0,0 +1 @@
|
|||
**/*.js
|
||||
28
.eslintrc.json
Normal file
28
.eslintrc.json
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2020": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 11,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["unused-imports", "@typescript-eslint", "prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"no-unused-vars": "off",
|
||||
"unused-imports/no-unused-imports-ts": "error",
|
||||
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off"
|
||||
}
|
||||
}
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
*.psd filter=lfs diff=lfs merge=lfs -text
|
||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
dist
|
||||
node_modules
|
||||
package-lock.json
|
||||
.vscode
|
||||
7
.prettierrc
Normal file
7
.prettierrc
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 90,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
65
package.json
Normal file
65
package.json
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"name": "decla.red",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --mode development",
|
||||
"lint": "npx eslint --fix \"src/**/*.ts\" && npx prettier --write \"src/**/*.ts\"",
|
||||
"build": "webpack && find dist -type f -not -name '*.html' | xargs rm"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "András Schmelczer",
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
"autoprefixer": {}
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults"
|
||||
],
|
||||
"sideEffects": [
|
||||
"*.scss"
|
||||
],
|
||||
"optimization": {
|
||||
"usedExports": true
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/gl-matrix": "^2.4.5",
|
||||
"@typescript-eslint/eslint-plugin": "^3.10.1",
|
||||
"@typescript-eslint/parser": "^3.10.1",
|
||||
"autoprefixer": "^9.8.6",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"css-loader": "^3.5.2",
|
||||
"cssnano": "^4.1.10",
|
||||
"eslint": "^7.9.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-import": "^2.22.0",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"eslint-plugin-unused-imports": "^0.1.3",
|
||||
"gl-matrix": "^3.3.0",
|
||||
"html-webpack-inline-source-plugin": "0.0.10",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"image-webpack-loader": "^6.0.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.4",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"prettier": "^2.1.2",
|
||||
"raw-loader": "^4.0.1",
|
||||
"resolve-url-loader": "^3.1.1",
|
||||
"responsive-loader": "^1.2.0",
|
||||
"sass": "^1.26.3",
|
||||
"sass-loader": "^9.0.3",
|
||||
"sdf-2d": "^0.0.0",
|
||||
"sharp": "^0.25.4",
|
||||
"style-loader": "^1.1.4",
|
||||
"svg-url-loader": "^6.0.0",
|
||||
"terser-webpack-plugin": "^2.3.8",
|
||||
"ts-loader": "^8.0.3",
|
||||
"typescript": "^3.9.7",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-dev-server": "^3.10.3"
|
||||
}
|
||||
}
|
||||
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>;
|
||||
}
|
||||
BIN
static/declared.png
Normal file
BIN
static/declared.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
static/declared.psd
(Stored with Git LFS)
Normal file
BIN
static/declared.psd
(Stored with Git LFS)
Normal file
Binary file not shown.
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": false,
|
||||
"target": "es5",
|
||||
"downlevelIteration": true,
|
||||
"allowJs": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "Node",
|
||||
"module": "commonjs",
|
||||
"lib": ["es2015", "dom"]
|
||||
}
|
||||
}
|
||||
127
webpack.config.js
Normal file
127
webpack.config.js
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
const TerserJSPlugin = require('terser-webpack-plugin');
|
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
|
||||
const Sass = require('sass');
|
||||
|
||||
const isProduction = process.env.NODE_ENV == 'production';
|
||||
const isDevelopment = !isProduction;
|
||||
|
||||
module.exports = {
|
||||
watchOptions: {
|
||||
ignored: /node_modules/,
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
devServer: {
|
||||
host: '0.0.0.0',
|
||||
disableHostCheck: true,
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserJSPlugin({
|
||||
sourceMap: isDevelopment,
|
||||
cache: true,
|
||||
test: /\.ts$/i,
|
||||
terserOptions: {
|
||||
ecma: 5,
|
||||
warnings: true,
|
||||
parse: {},
|
||||
compress: { defaults: true },
|
||||
mangle: true,
|
||||
module: false,
|
||||
output: null,
|
||||
toplevel: true,
|
||||
nameCache: null,
|
||||
ie8: false,
|
||||
keep_classnames: false,
|
||||
keep_fnames: false,
|
||||
safari10: false,
|
||||
},
|
||||
}),
|
||||
new OptimizeCSSAssetsPlugin({}),
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
xhtml: true,
|
||||
template: './src/index.html',
|
||||
minify: {
|
||||
collapseWhitespace: true,
|
||||
removeComments: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
useShortDoctype: true,
|
||||
},
|
||||
inlineSource: '.(js|css)$',
|
||||
}),
|
||||
new HtmlWebpackInlineSourcePlugin(),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '[name].[contenthash].css',
|
||||
chunkFilename: '[id].[contenthash].css',
|
||||
}),
|
||||
],
|
||||
entry: {
|
||||
index: './src/index.ts',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(glsl)$/,
|
||||
use: {
|
||||
loader: 'raw-loader',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.ico$/i,
|
||||
use: {
|
||||
loader: 'file-loader',
|
||||
query: {
|
||||
outputPath: '/',
|
||||
name: '[name].[ext]',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.scss$/i,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
'css-loader',
|
||||
'postcss-loader',
|
||||
{
|
||||
loader: 'resolve-url-loader',
|
||||
options: {
|
||||
keepQuery: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sourceMap: true,
|
||||
implementation: Sass,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.ts$/,
|
||||
use: {
|
||||
loader: 'ts-loader',
|
||||
},
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js', '.glsl'],
|
||||
},
|
||||
output: {
|
||||
filename: '[name].[contenthash].js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue