Copy files

This commit is contained in:
schmelczerandras 2020-09-16 16:03:01 +02:00
commit 36f01c6716
22 changed files with 500 additions and 0 deletions

1
.eslintignore Normal file
View file

@ -0,0 +1 @@
**/*.js

28
.eslintrc.json Normal file
View 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
View file

@ -0,0 +1 @@
*.psd filter=lfs diff=lfs merge=lfs -text

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
dist
node_modules
package-lock.json
.vscode

7
.prettierrc Normal file
View file

@ -0,0 +1,7 @@
{
"trailingComma": "es5",
"printWidth": 90,
"tabWidth": 2,
"singleQuote": true,
"endOfLine": "lf"
}

65
package.json Normal file
View file

@ -0,0 +1,65 @@
{
"name": "decla.red",
"version": "0.0.0",
"description": "![logo](media/declared.png)",
"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
View 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));

View 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
View 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
View file

@ -0,0 +1 @@
export const mix = (from: number, to: number, q: number) => from + (to - from) * q;

18
src/helper/random.ts Normal file
View 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
View file

@ -0,0 +1 @@
export const toPercent = (value: number) => `${Math.round(value * 100)}%`;

3
src/helper/wait.ts Normal file
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
static/declared.psd (Stored with Git LFS) Normal file

Binary file not shown.

14
tsconfig.json Normal file
View 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
View 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'),
},
};