Dockerize

This commit is contained in:
schmelczerandras 2020-09-20 11:25:33 +02:00
parent 0048aa191e
commit d9623a3274
10 changed files with 243 additions and 137 deletions

5
.dockerignore Normal file
View file

@ -0,0 +1,5 @@
Dockerfile
node_modules
dist
package-lock.json
.*

28
Dockerfile Normal file
View file

@ -0,0 +1,28 @@
FROM schmelczera/error-pages as build-error-pages
RUN python build.py 403 404 50x
FROM node:latest as build-webpage
WORKDIR /home/node
COPY src src
COPY static static
COPY package.json custom.d.ts tsconfig.json webpack.config.js ./
RUN npm install
RUN npm run build
FROM nginx:alpine
HEALTHCHECK --interval=1m --timeout=10s CMD curl --fail http://localhost/ || exit 1
WORKDIR /usr/share/nginx/html
RUN rm -rf *
COPY --from=build-webpage /home/node/dist .
COPY --from=build-error-pages /home/python/built errors
RUN find . -type f | xargs gzip -k9 &&\
chmod -R 555 .
COPY nginx-config /etc/nginx/

53
nginx-config/nginx.conf Normal file
View file

@ -0,0 +1,53 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log off;
sendfile on;
sendfile_max_chunk 1m;
tcp_nopush on;
keepalive_timeout 65;
gzip on;
gzip_static on;
gzip_vary on;
gzip_min_length 10240;
gzip_proxied any;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location ~* \.(jpg|jpeg|png|ico)$ {
expires 30d;
}
error_page 403 /403.html;
error_page 404 /404.html;
error_page 500 501 502 503 504 /50x.html;
location ~ ^/(403|404|50x).html$ {
root /usr/share/nginx/html/errors;
internal;
}
}
}

View file

@ -1,13 +1,13 @@
{ {
"name": "sdf-2d-demo", "name": "sdf-2d-demo",
"version": "0.0.0", "version": "0.0.0",
"description": "![logo](media/declared.png)", "description": "Some simple demos to showcase the possibilities of this library.",
"private": true, "private": true,
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "webpack-dev-server --mode development", "start": "webpack-dev-server --mode development",
"lint": "npx eslint --fix \"src/**/*.ts\" && npx prettier --write \"src/**/*.ts\"", "lint": "npx eslint --fix \"src/**/*.ts\" && npx prettier --write \"src/**/*.ts\"",
"build": "webpack --mode production && find dist -type f -not -name '*.html' | xargs rm && sed -i 's/^\\/\\/#.*.map//' dist/index.html" "build": "webpack --mode production && find dist -type f -not -regex \"dist\\/.*\\.\\(html\\|png\\|ico\\|svg\\|jpg\\)\" | xargs rm && sed -i 's/^\\/\\/#.*.map//' dist/index.html"
}, },
"keywords": [], "keywords": [],
"author": "András Schmelczer <andras@schmelczer.dev> (https://schmelczer.dev/)", "author": "András Schmelczer <andras@schmelczer.dev> (https://schmelczer.dev/)",
@ -21,25 +21,25 @@
"defaults" "defaults"
], ],
"sideEffects": [ "sideEffects": [
"*.scss" "*.scss",
"*.png",
"*.jpg",
"*.ico",
"*.svg"
], ],
"optimization": { "optimization": {
"usedExports": true "usedExports": true
}, },
"devDependencies": { "devDependencies": {
"@types/gl-matrix": "^2.4.5",
"@typescript-eslint/eslint-plugin": "^3.10.1", "@typescript-eslint/eslint-plugin": "^3.10.1",
"@typescript-eslint/parser": "^3.10.1", "@typescript-eslint/parser": "^3.10.1",
"autoprefixer": "^9.8.6", "autoprefixer": "^9.8.6",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.5.2", "css-loader": "^3.5.2",
"cssnano": "^4.1.10",
"eslint": "^7.9.0", "eslint": "^7.9.0",
"eslint-config-prettier": "^6.11.0", "eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.22.0", "eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^3.1.4", "eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-unused-imports": "^0.1.3", "eslint-plugin-unused-imports": "^0.1.3",
"gl-matrix": "^3.3.0",
"html-webpack-inline-source-plugin": "0.0.10", "html-webpack-inline-source-plugin": "0.0.10",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.9.0", "mini-css-extract-plugin": "^0.9.0",
@ -48,12 +48,10 @@
"prettier": "^2.1.2", "prettier": "^2.1.2",
"raw-loader": "^4.0.1", "raw-loader": "^4.0.1",
"resolve-url-loader": "^3.1.1", "resolve-url-loader": "^3.1.1",
"responsive-loader": "^1.2.0",
"sass": "^1.26.3", "sass": "^1.26.3",
"sass-loader": "^9.0.3", "sass-loader": "^9.0.3",
"sdf-2d": "^0.0.0", "sdf-2d": "^0.1.0-alpha",
"source-map-loader": "^1.1.0", "source-map-loader": "^1.1.0",
"style-loader": "^1.1.4",
"svg-url-loader": "^6.0.0", "svg-url-loader": "^6.0.0",
"terser-webpack-plugin": "^2.3.8", "terser-webpack-plugin": "^2.3.8",
"ts-loader": "^8.0.3", "ts-loader": "^8.0.3",

View file

@ -0,0 +1,4 @@
export const removeUnnecessaryOutlines = () =>
(document.onclick = (e) => {
(e.target as HTMLElement)?.blur();
});

View file

@ -2,31 +2,53 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>SDF-2D demo</title> <title>SDF-2D demo</title>
<meta property="og:image:width" content="1500" />
<meta property="og:image:height" content="785" />
<meta property="og:url" content="https://sdf2d.schmelczer.dev" />
<meta property="og:image" content="https://sdf2d.schmelczer.dev/og-image.jpg" />
<meta name="theme-color" content="#103783" />
<meta <meta
name="description" name="description"
content="Some simple demos to showcase the possibilities of this library." content="Some simple demos to showcase the possibilities of the sdf-2d library library. Click on the title to find out more."
/> />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="static/favicons/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="static/favicons/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="static/favicons/favicon-16x16.png"
/>
</head> </head>
<body> <body>
<noscript>Javascript is required for this website.</noscript> <noscript>Javascript is required for this website.</noscript>
<button id="info"> <a id="info" href="https://github.com/schmelczerandras/sdf-2d" target="_BLANK">
<a href="https://github.com/schmelczerandras/sdf-2d" target="_BLANK"> SDF-2D
SDF-2D<img src="static/logo-white.svg" alt="logo" /> <img src="static/logo-white.svg" alt="logo" />
</a> </a>
</button> <code id="overlay"></code>
<button id="toggle-text"></button>
<button id="remove-clutter">Hide text</button>
<div id="overlay"></div>
<canvas id="main"></canvas> <canvas id="main"></canvas>
<div id="errors-container"> <div id="errors-container">
<div id="errors"> <div id="errors">
<h1>Error</h1> <h1>Encountered an error</h1>
<p id="error-text"></p> <p id="error-text"></p>
</div> </div>
</div> </div>

View file

@ -1,103 +0,0 @@
$bg: linear-gradient(90deg, #103783, #9bafd9);
html,
body,
canvas#main {
height: 100%;
width: 100%;
overflow: hidden;
}
html {
@media (max-width: 800px) {
font-size: 0.7rem;
}
background: $bg;
}
* {
margin: 0;
box-sizing: border-box;
font-family: Helvetica, Tahoma, sans-serif;
color: white;
}
body {
position: relative;
a {
text-decoration: none;
}
button {
cursor: pointer;
border: 2px solid white;
border-radius: 6px;
padding: 0.2em 0.5rem;
background: none;
}
#info,
#overlay,
#remove-clutter {
margin: 1rem 1.25rem;
position: absolute;
}
#info {
left: 0;
font-size: 2.5rem;
a {
display: flex;
justify-content: center;
align-items: center;
img {
$size: 80px;
width: $size;
height: $size;
padding-left: 16px;
}
}
}
#overlay {
font-size: 0.75rem;
right: 0;
white-space: pre;
font-family: 'Lucida Console', Monaco, monospace;
}
#remove-clutter {
bottom: 0;
font-size: 1.25rem;
}
#errors-container {
display: none;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
text-align: center;
justify-content: center;
align-items: center;
#errors {
width: min(500px, 90vw);
height: min(500px, 90vw);
h1 {
font-size: 3rem;
padding: 2rem;
}
p {
text-align: left;
font-size: 1.25rem;
}
}
}
}

View file

@ -1,26 +1,40 @@
import { glMatrix } from 'gl-matrix'; import { glMatrix } from 'gl-matrix';
import '../static/favicons/apple-touch-icon.png';
import '../static/favicons/favicon-16x16.png';
import '../static/favicons/favicon-32x32.png';
import '../static/favicons/favicon.ico';
//import '../static/og-image.jpg';
import { DeltaTimeCalculator } from './helper/delta-time-calculator'; import { DeltaTimeCalculator } from './helper/delta-time-calculator';
import './index.scss'; import { removeUnnecessaryOutlines } from './helper/remove-unnecessary-outlines';
import { RainScene } from './scenes/rain/rain-scene'; import { BlobScene } from './scenes/blob-scene';
import { Scene } from './scenes/scene'; import { Scene } from './scenes/scene';
import { TunnelScene } from './scenes/tunnel-scene'; import './styles/index.scss';
const scenes = [TunnelScene, RainScene]; const scenes = [/*TunnelScene, RainScene*/ BlobScene];
const sceneIntervalInSeconds = 8; const sceneIntervalInSeconds = 80000;
glMatrix.setMatrixArrayType(Array); glMatrix.setMatrixArrayType(Array);
removeUnnecessaryOutlines();
const deltaTimeCalculator = new DeltaTimeCalculator(); const deltaTimeCalculator = new DeltaTimeCalculator();
const canvas = document.querySelector('canvas') as HTMLCanvasElement; const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const info = document.querySelector('#info'); const info = document.querySelector('#info') as HTMLDivElement;
const errorText = document.querySelector('#error-text') as HTMLParamElement; const errorText = document.querySelector('#error-text') as HTMLParamElement;
const errors = document.querySelector('#errors') as HTMLDivElement;
const errorsContainer = document.querySelector('#errors-container') as HTMLDivElement; const errorsContainer = document.querySelector('#errors-container') as HTMLDivElement;
const button = document.querySelector('#remove-clutter'); const toggleButton = document.querySelector('#toggle-text');
const overlay = document.querySelector('#overlay') as HTMLDivElement; const overlay = document.querySelector('#overlay') as HTMLDivElement;
button.addEventListener('click', () => let textVisible = false;
[info, button, overlay].forEach((e) => e.remove()) const handleTextToggle = () => {
); [info, overlay, errors].forEach(
(e) => (e.style.visibility = textVisible ? 'hidden' : 'inherit')
);
textVisible = !textVisible;
toggleButton.innerHTML = textVisible ? 'Hide text' : 'Show text';
};
toggleButton.addEventListener('click', handleTextToggle);
handleTextToggle();
const handleScene = async (SceneConstructor: new () => Scene) => { const handleScene = async (SceneConstructor: new () => Scene) => {
const scene = new SceneConstructor(); const scene = new SceneConstructor();
@ -44,7 +58,6 @@ const handleScene = async (SceneConstructor: new () => Scene) => {
requestAnimationFrame(handleFrame); requestAnimationFrame(handleFrame);
await isOver; await isOver;
scene.destroy(); scene.destroy();
}; };
@ -57,7 +70,7 @@ const main = async () => {
} catch (e) { } catch (e) {
console.error(e); console.error(e);
errorText.innerText = e; errorText.innerText = e;
errorsContainer.style.display = 'flex'; errorsContainer.style.visibility = 'visible';
} }
}; };

76
src/scenes/blob-scene.ts Normal file
View file

@ -0,0 +1,76 @@
import { vec3 } from 'gl-matrix';
import { Circle, CircleLight, compile, InvertedTunnel, Renderer } from 'sdf-2d';
import { Scene } from './scene';
export class BlobScene implements Scene {
private renderer: Renderer;
private canvas: HTMLCanvasElement;
private overlay: HTMLDivElement;
private tunnels: Array<InvertedTunnel> = [];
private lights: Array<CircleLight> = [];
public async initialize(
canvas: HTMLCanvasElement,
overlay: HTMLDivElement
): Promise<void> {
this.canvas = canvas;
this.overlay = overlay;
this.renderer = await compile(
canvas,
[
{
...InvertedTunnel.descriptor,
shaderCombinationSteps: [0, 1, 2, 4, 8, 12],
},
Circle.descriptor,
{
...CircleLight.descriptor,
shaderCombinationSteps: [1, 2, 3, 4, 5, 6, 7],
},
],
[vec3.fromValues(0.4, 1, 0.6), vec3.fromValues(1, 1, 0), vec3.fromValues(0.3, 1, 1)]
);
this.renderer.setRuntimeSettings({
ambientLight: vec3.fromValues(0.35, 0.1, 0.45),
shadowLength: 550,
});
}
private deltaSinceStart = 0;
public drawNextFrame(
currentTime: DOMHighResTimeStamp,
deltaTime: DOMHighResTimeStamp
): void {
const { width, height } = this.canvas.getBoundingClientRect();
this.deltaSinceStart += deltaTime;
this.renderer.setViewArea([0, height], [width, height]);
this.renderer.autoscaleQuality(deltaTime);
this.overlay.innerText = JSON.stringify(
this.renderer.insights,
(_, v) => (v.toFixed ? Number(v.toFixed(2)) : v),
' '
);
const q = (this.deltaSinceStart % 5000) / 2500;
console.log(q);
[
new Circle([width / 2, -width / 4], width / 2),
new CircleLight([q * width, Math.sin(q * Math.PI) * height], [1, 0.8, 0], 1),
new CircleLight(
[(q - 1) * width, Math.sin((q - 1) * Math.PI) * height],
[0, 0.8, 1],
1
),
].forEach((d) => this.renderer.addDrawable(d));
this.renderer.renderDrawables();
}
public destroy(): void {
this.renderer.destroy();
}
}

View file

@ -23,7 +23,7 @@ module.exports = {
filename: '[name].[contenthash].js', filename: '[name].[contenthash].js',
path: PATHS.bundles, path: PATHS.bundles,
}, },
devtool: isDevelopment ? 'source-map' : '', devtool: isDevelopment ? 'source-map' : 'null',
watchOptions: { watchOptions: {
aggregateTimeout: 600, aggregateTimeout: 600,
ignored: /node_modules/, ignored: /node_modules/,
@ -100,6 +100,16 @@ module.exports = {
noquotes: true, noquotes: true,
}, },
}, },
{
test: /\.(ico|png|jpg)$/i,
use: {
loader: 'file-loader',
query: {
outputPath: '/',
name: '[name].[ext]',
},
},
},
{ {
test: /\.js$/, test: /\.js$/,
enforce: 'pre', enforce: 'pre',