Refactor background
This commit is contained in:
parent
caacb5b3e5
commit
f8764f83c8
4 changed files with 48 additions and 88 deletions
|
|
@ -1,33 +0,0 @@
|
||||||
export type hex = string;
|
|
||||||
export type rgb = [number, number, number];
|
|
||||||
|
|
||||||
export const mixColors = (hexColorA: hex, hexColorB: hex, quantityA: number): hex => {
|
|
||||||
const colorA = hexToRGB(hexColorA);
|
|
||||||
const colorB = hexToRGB(hexColorB);
|
|
||||||
|
|
||||||
const mixedColor: rgb = [
|
|
||||||
mix(colorA[0], colorB[0], quantityA),
|
|
||||||
mix(colorA[1], colorB[1], quantityA),
|
|
||||||
mix(colorA[2], colorB[2], quantityA),
|
|
||||||
];
|
|
||||||
|
|
||||||
return rgbToHex(mixedColor);
|
|
||||||
};
|
|
||||||
|
|
||||||
const hexToRGB = (hex: hex): rgb => {
|
|
||||||
const [r1, r2, g1, g2, b1, b2] = normalizeHex(hex);
|
|
||||||
return [parseInt(r1 + r2, 16), parseInt(g1 + g2, 16), parseInt(b1 + b2, 16)];
|
|
||||||
};
|
|
||||||
|
|
||||||
const normalizeHex = (hex: hex): hex => {
|
|
||||||
hex = hex.trim();
|
|
||||||
if (hex[0] === '#') {
|
|
||||||
hex = hex.substr(1);
|
|
||||||
}
|
|
||||||
return hex;
|
|
||||||
};
|
|
||||||
|
|
||||||
const mix = (a: number, b: number, q: number): number => a * q + b * (1 - q);
|
|
||||||
|
|
||||||
const rgbToHex = (rgb: rgb): hex =>
|
|
||||||
'#' + rgb.map(n => (n < 16 ? '0' : '') + Math.round(n).toString(16)).join('');
|
|
||||||
2
src/helper/mix.ts
Normal file
2
src/helper/mix.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const mix = (from: number, to: number, q: number): number =>
|
||||||
|
from * (1 - q) + to * q;
|
||||||
|
|
@ -12,12 +12,11 @@ import { OptionalEvent } from '../../events/optional-event';
|
||||||
import { OnPageThemeChangedEvent } from '../../events/concrete-events/on-page-theme-changed-event';
|
import { OnPageThemeChangedEvent } from '../../events/concrete-events/on-page-theme-changed-event';
|
||||||
|
|
||||||
export class PageBackground extends PageElement {
|
export class PageBackground extends PageElement {
|
||||||
public static readonly BLOB_SPACING = 325;
|
public static readonly blobSpacing = 325;
|
||||||
public static readonly MIN_BLOB_COUNT = 30;
|
public static readonly minBlobCount = 30;
|
||||||
public static readonly PERSPECTIVE = 5;
|
public static readonly perspective = 5;
|
||||||
public static readonly Z_MIN = 10;
|
public static readonly zMin = 10;
|
||||||
public static readonly Z_MAX = 30;
|
public static readonly zMax = 30;
|
||||||
public static readonly ANIMATION_TIME = 250;
|
|
||||||
|
|
||||||
private backgroundSize: Vec2;
|
private backgroundSize: Vec2;
|
||||||
private scrollPosition = 0;
|
private scrollPosition = 0;
|
||||||
|
|
@ -39,7 +38,7 @@ export class PageBackground extends PageElement {
|
||||||
|
|
||||||
public handleOnLoadEvent(event: OnLoadEvent): OptionalEvent {
|
public handleOnLoadEvent(event: OnLoadEvent): OptionalEvent {
|
||||||
this.parent = event.parent;
|
this.parent = event.parent;
|
||||||
this.bindListeners();
|
requestAnimationFrame(this.draw.bind(this));
|
||||||
return super.handleOnLoadEvent(event);
|
return super.handleOnLoadEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,18 +48,10 @@ export class PageBackground extends PageElement {
|
||||||
return super.handleOnPageThemeChangedEvent(event);
|
return super.handleOnPageThemeChangedEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bindListeners() {
|
|
||||||
addEventListener('load', e => {
|
|
||||||
this.resize();
|
|
||||||
this.createBlobs();
|
|
||||||
this.redraw(e.timeStamp);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private createBlobs() {
|
private createBlobs() {
|
||||||
const requiredBlobCount = Math.max(
|
const requiredBlobCount = Math.max(
|
||||||
PageBackground.MIN_BLOB_COUNT,
|
PageBackground.minBlobCount,
|
||||||
(this.backgroundSize.x * this.backgroundSize.y) / PageBackground.BLOB_SPACING ** 2
|
(this.backgroundSize.x * this.backgroundSize.y) / PageBackground.blobSpacing ** 2
|
||||||
);
|
);
|
||||||
|
|
||||||
while (requiredBlobCount > this.blobs.length) {
|
while (requiredBlobCount > this.blobs.length) {
|
||||||
|
|
@ -68,11 +59,6 @@ export class PageBackground extends PageElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private resize() {
|
|
||||||
this.resizeCanvas();
|
|
||||||
this.resizeBackground();
|
|
||||||
}
|
|
||||||
|
|
||||||
private resizeCanvas() {
|
private resizeCanvas() {
|
||||||
this.canvas.width = this.canvas.clientWidth;
|
this.canvas.width = this.canvas.clientWidth;
|
||||||
this.canvas.height = this.canvas.clientHeight;
|
this.canvas.height = this.canvas.clientHeight;
|
||||||
|
|
@ -96,8 +82,8 @@ export class PageBackground extends PageElement {
|
||||||
Math.max(
|
Math.max(
|
||||||
0,
|
0,
|
||||||
offset -
|
offset -
|
||||||
((blob.z - PageBackground.Z_MIN) /
|
((blob.z - PageBackground.zMin) /
|
||||||
(PageBackground.Z_MAX - PageBackground.Z_MIN)) *
|
(PageBackground.zMax - PageBackground.zMin)) *
|
||||||
offset *
|
offset *
|
||||||
q
|
q
|
||||||
);
|
);
|
||||||
|
|
@ -120,8 +106,10 @@ export class PageBackground extends PageElement {
|
||||||
return [this.start, ...this.inBetween, this.end].map(e => e.htmlRoot);
|
return [this.start, ...this.inBetween, this.end].map(e => e.htmlRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private redraw(timestamp: DOMHighResTimeStamp) {
|
private draw(timestamp: DOMHighResTimeStamp) {
|
||||||
|
this.resizeCanvas();
|
||||||
this.resizeBackground();
|
this.resizeBackground();
|
||||||
|
this.createBlobs();
|
||||||
|
|
||||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
const deltaTime = this.getDeltaTime(timestamp);
|
const deltaTime = this.getDeltaTime(timestamp);
|
||||||
|
|
@ -142,7 +130,7 @@ export class PageBackground extends PageElement {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
requestAnimationFrame(timestamp => this.redraw(timestamp));
|
requestAnimationFrame(this.draw.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDeltaTime(timestamp: DOMHighResTimeStamp): number {
|
private getDeltaTime(timestamp: DOMHighResTimeStamp): number {
|
||||||
|
|
@ -152,12 +140,12 @@ export class PageBackground extends PageElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertFrom3Dto2D(p: Vec3): Vec2 {
|
private convertFrom3Dto2D(p: Vec3): Vec2 {
|
||||||
const m = PageBackground.PERSPECTIVE / (PageBackground.PERSPECTIVE + p.z);
|
const m = PageBackground.perspective / (PageBackground.perspective + p.z);
|
||||||
return new Vec2(m * (p.z / 2 + p.x), m * (p.z / 2 + p.y - this.scrollPosition));
|
return new Vec2(m * (p.z / 2 + p.x), m * (p.z / 2 + p.y - this.scrollPosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertFrom2Dto3D(p: Vec2, z: number, scrollPosition = 0): Vec2 {
|
private convertFrom2Dto3D(p: Vec2, z: number, scrollPosition = 0): Vec2 {
|
||||||
const m = 1 + z / PageBackground.PERSPECTIVE;
|
const m = 1 + z / PageBackground.perspective;
|
||||||
return new Vec2(p.x * m - z / 2, p.y * m - z / 2 + scrollPosition);
|
return new Vec2(p.x * m - z / 2, p.y * m - z / 2 + scrollPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
import { Vec2 } from './vec2';
|
import { Vec2 } from './vec2';
|
||||||
import { Vec3 } from './vec3';
|
import { Vec3 } from './vec3';
|
||||||
import { mixColors } from '../../helper/mix-colors';
|
|
||||||
import { Random } from '../../helper/random';
|
import { Random } from '../../helper/random';
|
||||||
import { Animation } from './animation';
|
import { Animation } from './animation';
|
||||||
import { PageBackground } from './background';
|
import { PageBackground } from './background';
|
||||||
|
import { mix } from '../../helper/mix';
|
||||||
|
|
||||||
export class Blob {
|
export class Blob {
|
||||||
private static readonly DARK_COLORS = ['#2c477a'];
|
private static readonly darkColors = [new Vec3(44, 71, 122)];
|
||||||
private static readonly LIGHT_COLORS = ['#fff9e0', '#ffd6d6'];
|
private static readonly lightColors = [
|
||||||
private static readonly ROTATION = (-20 / 180) * Math.PI;
|
new Vec3(255, 249, 224),
|
||||||
private static readonly CREATOR_RANDOM = new Random(51);
|
new Vec3(255, 214, 214),
|
||||||
|
];
|
||||||
|
private static readonly creatorRandom = new Random(51);
|
||||||
|
|
||||||
private static colorPickerRandom = new Random(132);
|
private static colorPickerRandom = new Random(132);
|
||||||
private static isDarkThemed = false;
|
private static isDarkThemed = false;
|
||||||
|
|
@ -19,44 +22,43 @@ export class Blob {
|
||||||
Blob.isDarkThemed = isDarkThemed;
|
Blob.isDarkThemed = isDarkThemed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly z = Blob.CREATOR_RANDOM.randomInInterval(
|
public readonly z = Blob.creatorRandom.randomInInterval(
|
||||||
PageBackground.Z_MIN,
|
PageBackground.zMin,
|
||||||
PageBackground.Z_MAX
|
PageBackground.zMax
|
||||||
);
|
);
|
||||||
private color: Animation<string>;
|
private color: Animation<Vec3>;
|
||||||
|
|
||||||
private readonly positionQ = new Vec2(
|
private readonly positionQ = new Vec2(Blob.creatorRandom.next, Blob.creatorRandom.next);
|
||||||
Blob.CREATOR_RANDOM.next,
|
|
||||||
Blob.CREATOR_RANDOM.next
|
|
||||||
);
|
|
||||||
private _positionScale = new Vec2(0, 0);
|
private _positionScale = new Vec2(0, 0);
|
||||||
private _positionOffset = new Vec2(0, 0);
|
private _positionOffset = new Vec2(0, 0);
|
||||||
|
private opacity: number;
|
||||||
|
|
||||||
private readonly _size = new Vec2(140, Blob.CREATOR_RANDOM.randomInInterval(260, 740));
|
private readonly _size = new Vec2(140, Blob.creatorRandom.randomInInterval(260, 740));
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
|
this.opacity =
|
||||||
|
1 - (this.z - PageBackground.zMin) / (PageBackground.zMax - PageBackground.zMin);
|
||||||
|
|
||||||
this.decideColor();
|
this.decideColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public decideColor() {
|
public decideColor() {
|
||||||
const target = mixColors(
|
const target = Blob.colorPickerRandom.choose(
|
||||||
Blob.isDarkThemed ? '#242638' : '#ffffff',
|
Blob.isDarkThemed ? Blob.darkColors : Blob.lightColors
|
||||||
Blob.colorPickerRandom.choose(
|
|
||||||
Blob.isDarkThemed ? Blob.DARK_COLORS : Blob.LIGHT_COLORS
|
|
||||||
),
|
|
||||||
(this.z - PageBackground.Z_MIN) / (PageBackground.Z_MAX - PageBackground.Z_MIN)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.color = new Animation<string>(
|
this.color = new Animation<Vec3>(
|
||||||
this.color ? this.color.value : target,
|
this.color ? this.color.value : target,
|
||||||
target,
|
target,
|
||||||
250,
|
125,
|
||||||
(f, t, q) => mixColors(f, t, 1 - q)
|
(f, t, q) => {
|
||||||
|
return new Vec3(mix(f.x, t.x, q), mix(f.y, t.y, q), mix(f.z, t.z, q));
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public step(value) {
|
public step(deltaTime: number) {
|
||||||
this.color?.step(value);
|
this.color?.step(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get topLeft(): Vec3 {
|
public get topLeft(): Vec3 {
|
||||||
|
|
@ -82,14 +84,15 @@ export class Blob {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
|
|
||||||
ctx.translate(position.x, position.y);
|
ctx.translate(position.x, position.y);
|
||||||
ctx.rotate(Blob.ROTATION);
|
ctx.rotate((-20 / 180) * Math.PI);
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(0, size.x / 2, size.x / 2, Math.PI, 0);
|
ctx.arc(0, size.x / 2, size.x / 2, Math.PI, 0);
|
||||||
ctx.arc(0, size.y - size.x / 2, size.x / 2, 0, Math.PI);
|
ctx.arc(0, size.y - size.x / 2, size.x / 2, 0, Math.PI);
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
|
|
||||||
ctx.fillStyle = this.color.value;
|
const { x, y, z } = this.color.value;
|
||||||
|
ctx.fillStyle = `rgba(${x}, ${y}, ${z}, ${this.opacity})`;
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue