Add improvements

This commit is contained in:
schmelczerandras 2020-10-31 20:42:44 +01:00
parent 8a33293647
commit 05f4e08ad1
19 changed files with 185 additions and 76 deletions

View file

@ -6,7 +6,7 @@
"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": "rm -rf dist/* && webpack --mode production && find dist -type f -not -regex \"dist\\/.*\\.\\(html\\|png\\|ico\\|svg\\|jpg\\|txt\\)\" | xargs rm && sed -i 's/^\\/\\/#.*.map//' dist/index.html" "build": "webpack --mode production"
}, },
"keywords": [], "keywords": [],
"author": "András Schmelczer <andras@schmelczer.dev> (https://schmelczer.dev/)", "author": "András Schmelczer <andras@schmelczer.dev> (https://schmelczer.dev/)",
@ -27,6 +27,7 @@
"@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",
"eslint": "^7.9.0", "eslint": "^7.9.0",
"eslint-config-prettier": "^6.11.0", "eslint-config-prettier": "^6.11.0",
@ -34,24 +35,25 @@
"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", "gl-matrix": "^3.3.0",
"html-webpack-inline-source-plugin": "0.0.10", "html-webpack-inline-source-plugin": "^1.0.0-beta.2",
"html-webpack-plugin": "^3.2.0", "html-webpack-inline-svg-plugin": "^2.3.0",
"html-webpack-plugin": "^4.5.0",
"mini-css-extract-plugin": "^0.9.0", "mini-css-extract-plugin": "^0.9.0",
"optimize-css-assets-webpack-plugin": "^5.0.4", "optimize-css-assets-webpack-plugin": "^5.0.4",
"postcss-loader": "^3.0.0", "postcss-loader": "^3.0.0",
"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",
"sass": "^1.26.3", "sass": "^1.27.0",
"sass-loader": "^9.0.3", "sass-loader": "^8.0.2",
"sdf-2d": "^0.6.3", "sdf-2d": "^0.7.0",
"source-map-loader": "^1.1.0", "source-map-loader": "^1.1.1",
"svg-url-loader": "^6.0.0", "svg-url-loader": "^6.0.0",
"terser-webpack-plugin": "^2.3.8", "ts-config-webpack-plugin": "^2.0.0",
"ts-loader": "^8.0.3", "ts-loader": "^8.0.3",
"typescript": "^3.9.7", "typescript": "^4.0.3",
"webpack": "^4.44.1", "webpack": "^4.43.0",
"webpack-cli": "^3.3.11", "webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3" "webpack-dev-server": "^3.11.0"
} }
} }

View file

@ -8,10 +8,12 @@ export const extractInsights = (
fps?: number; fps?: number;
renderScale?: number; renderScale?: number;
lightScale?: number; lightScale?: number;
version?: string;
} => ({ } => ({
fps: insights?.fps, fps: insights?.fps,
vendor: insights?.vendor, vendor: insights?.vendor,
renderer: insights?.renderer, renderer: insights?.renderer,
renderScale: insights?.renderPasses.distance.renderScale, renderScale: insights?.renderPasses.distance.renderScale,
lightScale: insights?.renderPasses.lights.renderScale, lightScale: insights?.renderPasses.lights.renderScale,
version: insights?.sdf2dVersion,
}); });

View file

@ -0,0 +1,48 @@
export const handleFullScreen = (
minimizeButton: HTMLElement,
maximizeButton: HTMLElement,
target: HTMLElement
) => {
if (!document.fullscreenEnabled) {
minimizeButton.style.visibility = 'hidden';
maximizeButton.style.visibility = 'hidden';
return;
}
const isInFullScreen = (): boolean => document.fullscreenElement !== null;
const showButtons = () => {
minimizeButton.style.visibility = isInFullScreen() ? 'visible' : 'hidden';
maximizeButton.style.visibility = isInFullScreen() ? 'hidden' : 'visible';
};
showButtons();
let currentWindowHeight = innerHeight;
const followToggle = () => {
showButtons();
currentWindowHeight = innerHeight;
};
const triggerToggle = async () => {
await (isInFullScreen() ? document.exitFullscreen() : target.requestFullscreen());
followToggle();
};
addEventListener('keydown', (e) => {
if (e.key === 'F11') {
triggerToggle();
e.preventDefault();
}
});
addEventListener('resize', () => {
if (isInFullScreen && currentWindowHeight > innerHeight) {
followToggle();
}
});
maximizeButton.addEventListener('click', triggerToggle);
minimizeButton.addEventListener('click', triggerToggle);
};

View file

@ -2,8 +2,12 @@ const baseUri = 'https://insights.decla.red';
const type = 'sdf-2d-demo'; const type = 'sdf-2d-demo';
export const handleInsights = async (initialData: any): Promise<(data: any) => void> => { export const handleInsights = async (initialData: any): Promise<(data: any) => void> => {
try {
const sessionId = await createSession(initialData); const sessionId = await createSession(initialData);
return (data) => createFrame(sessionId, data); return (data) => createFrame(sessionId, data);
} catch {
return () => null;
}
}; };
const createSession = async (data: any): Promise<string> => { const createSession = async (data: any): Promise<string> => {
@ -12,8 +16,8 @@ const createSession = async (data: any): Promise<string> => {
return sessionId; return sessionId;
}; };
const createFrame = async (sessionId: string, data: any): Promise<unknown> => const createFrame = (sessionId: string, data: any): Promise<unknown> =>
await sendPostRequest(`${baseUri}/${type}/sessions/${sessionId}`, data); sendPostRequest(`${baseUri}/${type}/sessions/${sessionId}`, data).catch();
const sendPostRequest = async (uri: string, data: any): Promise<Response> => const sendPostRequest = async (uri: string, data: any): Promise<Response> =>
await fetch(uri, { await fetch(uri, {

View file

@ -33,14 +33,8 @@
<h1>Javascript is required for this website.</h1> <h1>Javascript is required for this website.</h1>
</noscript> </noscript>
<a id="info" href="https://github.com/schmelczerandras/sdf-2d" target="_BLANK"> <main id="canvas-container">
<p>SDF-2D</p> <canvas></canvas>
<img width="64" height="64" src="logo-white.svg" alt="logo" />
</a>
<code id="overlay"></code>
<button id="toggle-text">Loading…</button>
<canvas id="main"></canvas>
<div id="errors-container"> <div id="errors-container">
<div id="errors"> <div id="errors">
@ -48,5 +42,32 @@
<p id="error-text"></p> <p id="error-text"></p>
</div> </div>
</div> </div>
<img
src="../static/minimize.svg"
alt="minimize"
id="minimize"
class="full-screen-control"
/>
<img
src="../static/maximize.svg"
alt="maximize"
id="maximize"
class="full-screen-control"
/>
</main>
<a id="info" href="https://github.com/schmelczerandras/sdf-2d" target="_BLANK">
<p>SDF-2D</p>
<img
class="logo"
width="64"
height="64"
src="../static/logo-white.svg"
alt="logo"
/>
</a>
<code id="overlay"></code>
<button id="toggle-text">Loading…</button>
</body> </body>
</html> </html>

View file

@ -6,10 +6,12 @@ import '../static/favicons/favicon-32x32.png';
import '../static/favicons/favicon.ico'; import '../static/favicons/favicon.ico';
import '../static/logo-white.svg'; import '../static/logo-white.svg';
import '../static/no-change/404.html'; import '../static/no-change/404.html';
import '../static/no-change/og-image.png';
import '../static/no-change/robots.txt'; import '../static/no-change/robots.txt';
import '../static/og-image.png';
import { extractInsights } from './helper/extract-insights'; import { extractInsights } from './helper/extract-insights';
import { handleFullScreen } from './helper/handle-full-screen';
import { handleInsights } from './helper/handle-insights'; import { handleInsights } from './helper/handle-insights';
import { Random } from './helper/random';
import { removeUnnecessaryOutlines } from './helper/remove-unnecessary-outlines'; import { removeUnnecessaryOutlines } from './helper/remove-unnecessary-outlines';
import { BlobScene } from './scenes/blob/blob-scene'; import { BlobScene } from './scenes/blob/blob-scene';
import { MetaballScene } from './scenes/metaball/metaball-scene'; import { MetaballScene } from './scenes/metaball/metaball-scene';
@ -18,14 +20,18 @@ import { TunnelScene } from './scenes/tunnel-scene';
import './styles/index.scss'; import './styles/index.scss';
const scenes = [TunnelScene, MetaballScene, RainScene, BlobScene]; const scenes = [TunnelScene, MetaballScene, RainScene, BlobScene];
Random.seed = 2;
glMatrix.setMatrixArrayType(Array); glMatrix.setMatrixArrayType(Array);
removeUnnecessaryOutlines(); removeUnnecessaryOutlines();
const canvas = document.querySelector('canvas') as HTMLCanvasElement; const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const canvasContainer = document.querySelector('#canvas-container') as HTMLCanvasElement;
const errorText = document.querySelector('#error-text') as HTMLParamElement; const errorText = document.querySelector('#error-text') as HTMLParamElement;
const errorsContainer = document.querySelector('#errors-container') as HTMLDivElement; const errorsContainer = document.querySelector('#errors-container') as HTMLDivElement;
const toggleButton = document.querySelector('#toggle-text'); const toggleButton = document.querySelector('#toggle-text');
const minimizeButton = document.querySelector('#minimize') as HTMLElement;
const maximizeButton = document.querySelector('#maximize') as HTMLElement;
const overlay = document.querySelector('#overlay') as HTMLDivElement; const overlay = document.querySelector('#overlay') as HTMLDivElement;
let textVisible = true; let textVisible = true;
@ -43,18 +49,25 @@ toggleButton.addEventListener('click', handleTextToggle);
handleTextToggle(); handleTextToggle();
const startInsightsSession = async (): Promise<(data: any) => unknown> => { const startInsightsSession = async (): Promise<(data: any) => unknown> => {
const { vendor, renderer } = extractInsights((await compile(canvas, [])).insights); const dummyRenderer = await compile(document.createElement('canvas'), []);
const { vendor, renderer, version } = extractInsights(dummyRenderer.insights);
dummyRenderer.destroy();
return await handleInsights({ return await handleInsights({
vendor, vendor,
renderer, renderer,
referrer: document.referrer, referrer: document.referrer,
connection: (navigator as any)?.connection?.effectiveType, connection: (navigator as any)?.connection?.effectiveType,
devicePixelRatio: devicePixelRatio, devicePixelRatio: devicePixelRatio,
height: innerHeight,
width: innerWidth,
version,
}); });
}; };
const main = async () => { const main = async () => {
const sendFramePromise = startInsightsSession(); const sendFramePromise = startInsightsSession();
handleFullScreen(minimizeButton, maximizeButton, canvasContainer);
try { try {
let i = 0; let i = 0;

View file

@ -32,7 +32,6 @@ export class BlobScene implements Scene {
Blob.descriptor, Blob.descriptor,
], ],
this.drawNextFrame.bind(this), this.drawNextFrame.bind(this),
{},
{ {
ambientLight: rgb255(89, 25, 115), ambientLight: rgb255(89, 25, 115),
enableHighDpiRendering: true, enableHighDpiRendering: true,
@ -65,6 +64,8 @@ export class BlobScene implements Scene {
const q = (currentTime % 8000) / 4300; const q = (currentTime % 8000) / 4300;
this.blob.animate(currentTime); this.blob.animate(currentTime);
this.blob.position = [width / 2, -length / 4 + length / 2];
[ [
new Circle([width / 2, -length / 4], length / 2), new Circle([width / 2, -length / 4], length / 2),
this.blob, this.blob,

View file

@ -120,8 +120,6 @@ export class Blob extends Drawable {
Math.sin((q > 1 ? q - 1 : 0) * Math.PI) * 10 Math.sin((q > 1 ? q - 1 : 0) * Math.PI) * 10
) )
); );
this.position = this.center;
} }
public minDistance(target: vec2): number { public minDistance(target: vec2): number {

View file

@ -43,7 +43,6 @@ export class MetaballScene implements Scene {
}, },
], ],
this.drawNextFrame.bind(this), this.drawNextFrame.bind(this),
{},
{ {
ambientLight: rgb(0.1, 0.1, 0.3), ambientLight: rgb(0.1, 0.1, 0.3),
enableHighDpiRendering: true, enableHighDpiRendering: true,

View file

@ -1,8 +1,8 @@
import { ReadonlyVec2, vec2 } from 'gl-matrix'; import { ReadonlyVec2, vec2 } from 'gl-matrix';
import { DropletFactory, rgb } from 'sdf-2d'; import { DropletFactory, rgb255 } from 'sdf-2d';
import { Random } from '../../helper/random'; import { Random } from '../../helper/random';
export const Droplet = DropletFactory(rgb(0.3, 1, 1)); export const Droplet = DropletFactory(rgb255(122, 122, 255));
export class DropletWrapper { export class DropletWrapper {
public readonly drawable: InstanceType<typeof Droplet>; public readonly drawable: InstanceType<typeof Droplet>;

View file

@ -7,8 +7,8 @@ import { Droplet, DropletWrapper } from './droplet';
export class RainScene implements Scene { export class RainScene implements Scene {
private droplets: Array<DropletWrapper> = []; private droplets: Array<DropletWrapper> = [];
private light1: CircleLight = new CircleLight(vec2.create(), rgb255(184, 41, 255), 2); private light1: CircleLight = new CircleLight(vec2.create(), rgb255(184, 41, 255), 1.5);
private light2: CircleLight = new CircleLight(vec2.create(), rgb255(255, 31, 109), 2); private light2: CircleLight = new CircleLight(vec2.create(), rgb255(255, 31, 109), 1.5);
private overlay: HTMLDivElement; private overlay: HTMLDivElement;
public insights?: any; public insights?: any;
@ -32,8 +32,8 @@ export class RainScene implements Scene {
}, },
], ],
this.drawNextFrame.bind(this), this.drawNextFrame.bind(this),
{},
{ {
backgroundColor: rgb(0.5, 0.5, 0.5),
ambientLight: rgb(0.2, 0.2, 0.2), ambientLight: rgb(0.2, 0.2, 0.2),
enableHighDpiRendering: true, enableHighDpiRendering: true,
} }

View file

@ -70,12 +70,12 @@ export class TunnelScene implements Scene {
} }
public async run(canvas: HTMLCanvasElement, overlay: HTMLDivElement): Promise<void> { public async run(canvas: HTMLCanvasElement, overlay: HTMLDivElement): Promise<void> {
const noiseTexture = await renderNoise([1024, 1], 15, 1); const noiseTexture = await renderNoise([1024, 1], 15, 0.5);
this.canvas = canvas; this.canvas = canvas;
this.overlay = overlay; this.overlay = overlay;
for (let i = 0; i < 200; i++) { for (let i = 0; i < 30; i++) {
this.generateTunnel(); this.generateTunnel();
} }
@ -92,8 +92,8 @@ export class TunnelScene implements Scene {
}, },
], ],
this.drawNextFrame.bind(this), this.drawNextFrame.bind(this),
{ lightPenetrationRatio: 0.5 },
{ {
lightPenetrationRatio: 0.5,
isWorldInverted: true, isWorldInverted: true,
enableHighDpiRendering: true, enableHighDpiRendering: true,
ambientLight: rgb(0.35, 0.1, 0.45), ambientLight: rgb(0.35, 0.1, 0.45),
@ -123,7 +123,7 @@ export class TunnelScene implements Scene {
const height = renderer.canvasSize.y; const height = renderer.canvasSize.y;
this.deltaSinceStart += deltaTime; this.deltaSinceStart += deltaTime;
const startX = this.deltaSinceStart / 3; const startX = this.deltaSinceStart / 4;
const endX = startX + width; const endX = startX + width;
renderer.setViewArea(vec2.fromValues(startX, height), vec2.fromValues(width, height)); renderer.setViewArea(vec2.fromValues(startX, height), vec2.fromValues(width, height));

View file

@ -1,3 +1,3 @@
export const settings = { export const settings = {
sceneTimeInMilliseconds: 7000, sceneTimeInMilliseconds: 8000,
}; };

View file

@ -11,8 +11,7 @@ $breakpoint: 800px;
} }
html, html,
body, body {
canvas#main {
height: 100%; height: 100%;
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
@ -50,7 +49,7 @@ body {
padding-top: 2px; padding-top: 2px;
} }
img { .logo {
@include square(64px); @include square(64px);
padding-left: 8px; padding-left: 8px;
} }
@ -58,12 +57,33 @@ body {
@media (max-width: $breakpoint) { @media (max-width: $breakpoint) {
font-size: 2rem; font-size: 2rem;
img { .logo {
@include square(40px); @include square(40px);
} }
} }
} }
#canvas-container {
&,
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.full-screen-control {
position: absolute;
right: 0;
bottom: 0;
box-sizing: content-box;
@include square(40px);
padding: 16px;
cursor: pointer;
}
}
#overlay { #overlay {
right: 0; right: 0;
white-space: pre-wrap; white-space: pre-wrap;

7
static/maximize.svg Normal file
View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 24 24" stroke-width="1.5" stroke="#FFFFFF" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M4 8v-2a2 2 0 0 1 2 -2h2" />
<path d="M4 16v2a2 2 0 0 0 2 2h2" />
<path d="M16 4h2a2 2 0 0 1 2 2v2" />
<path d="M16 20h2a2 2 0 0 0 2 -2v-2" />
</svg>

After

Width:  |  Height:  |  Size: 399 B

7
static/minimize.svg Normal file
View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 24 24" stroke-width="1.5" stroke="#FFFFFF" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M15 19v-2a2 2 0 0 1 2 -2h2" />
<path d="M15 5v2a2 2 0 0 0 2 2h2" />
<path d="M5 15h2a2 2 0 0 1 2 2v2" />
<path d="M5 9h2a2 2 0 0 0 2 -2v-2" />
</svg>

After

Width:  |  Height:  |  Size: 399 B

View file

Before

Width:  |  Height:  |  Size: 615 KiB

After

Width:  |  Height:  |  Size: 615 KiB

Before After
Before After

View file

@ -9,6 +9,8 @@
"experimentalDecorators": true, "experimentalDecorators": true,
"moduleResolution": "Node", "moduleResolution": "Node",
"module": "es6", "module": "es6",
"lib": ["es2016", "dom"] "lib": ["es2016", "dom"],
} "skipLibCheck": true
},
"exclude": ["node_modules"]
} }

View file

@ -1,13 +1,15 @@
const path = require('path'); const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin').CleanWebpackPlugin;
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin'); const TsConfigWebpackPlugin = require('ts-config-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin'); const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
const HtmlWebpackInlineSVGPlugin = require('html-webpack-inline-svg-plugin');
const Sass = require('sass'); const Sass = require('sass');
const PATHS = { const PATHS = {
entryPoint: path.resolve(__dirname, 'src/index.ts'), entryPoint: path.resolve(__dirname, 'src/index.ts'),
entryHtml: path.resolve(__dirname, 'src/index.html'),
bundles: path.resolve(__dirname, 'dist'), bundles: path.resolve(__dirname, 'dist'),
}; };
@ -17,10 +19,9 @@ module.exports = {
}, },
target: 'web', target: 'web',
output: { output: {
filename: '[name].[contenthash].js',
path: PATHS.bundles, path: PATHS.bundles,
}, },
devtool: 'source-map', //devtool: 'source-map',
watchOptions: { watchOptions: {
aggregateTimeout: 600, aggregateTimeout: 600,
ignored: /node_modules/, ignored: /node_modules/,
@ -29,21 +30,12 @@ module.exports = {
host: '0.0.0.0', host: '0.0.0.0',
disableHostCheck: true, disableHostCheck: true,
}, },
optimization: {
minimize: true,
usedExports: true,
minimizer: [
new TerserJSPlugin({
sourceMap: true,
test: /\.js$/,
}),
new OptimizeCSSAssetsPlugin({}),
],
},
plugins: [ plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin(),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
xhtml: true, xhtml: true,
template: './src/index.html', template: PATHS.entryHtml,
minify: { minify: {
collapseWhitespace: true, collapseWhitespace: true,
removeComments: true, removeComments: true,
@ -54,11 +46,11 @@ module.exports = {
}, },
inlineSource: '.(js|css)$', inlineSource: '.(js|css)$',
}), }),
new HtmlWebpackInlineSourcePlugin(), new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
new MiniCssExtractPlugin({ new HtmlWebpackInlineSVGPlugin({
filename: '[name].[contenthash].css', inlineAll: true,
chunkFilename: '[id].[contenthash].css',
}), }),
new TsConfigWebpackPlugin(),
], ],
module: { module: {
rules: [ rules: [
@ -84,14 +76,7 @@ module.exports = {
], ],
}, },
{ {
test: /\.ts$/, test: /\.(ico|png)$/,
use: {
loader: 'ts-loader',
},
exclude: /node_modules/,
},
{
test: /\.(ico|png|jpg)$/,
use: { use: {
loader: 'file-loader', loader: 'file-loader',
query: { query: {
@ -101,7 +86,7 @@ module.exports = {
}, },
}, },
{ {
test: /no-change.*(html|txt)$/i, test: /no-change.*$/i,
use: { use: {
loader: 'file-loader', loader: 'file-loader',
query: { query: {