Add improvements
This commit is contained in:
parent
8a33293647
commit
05f4e08ad1
19 changed files with 185 additions and 76 deletions
24
package.json
24
package.json
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
48
src/helper/handle-full-screen.ts
Normal file
48
src/helper/handle-full-screen.ts
Normal 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);
|
||||||
|
};
|
||||||
|
|
@ -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, {
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
17
src/index.ts
17
src/index.ts
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
export const settings = {
|
export const settings = {
|
||||||
sceneTimeInMilliseconds: 7000,
|
sceneTimeInMilliseconds: 8000,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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
7
static/maximize.svg
Normal 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
7
static/minimize.svg
Normal 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 |
|
Before Width: | Height: | Size: 615 KiB After Width: | Height: | Size: 615 KiB |
|
|
@ -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"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue