diff --git a/src/helper/mix-colors.ts b/src/helper/mix-colors.ts deleted file mode 100644 index e63712a..0000000 --- a/src/helper/mix-colors.ts +++ /dev/null @@ -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(''); diff --git a/src/helper/mix.ts b/src/helper/mix.ts new file mode 100644 index 0000000..a7ba2bd --- /dev/null +++ b/src/helper/mix.ts @@ -0,0 +1,2 @@ +export const mix = (from: number, to: number, q: number): number => + from * (1 - q) + to * q; diff --git a/src/page/background/background.ts b/src/page/background/background.ts index e94ec4c..29f7cde 100644 --- a/src/page/background/background.ts +++ b/src/page/background/background.ts @@ -12,12 +12,11 @@ import { OptionalEvent } from '../../events/optional-event'; import { OnPageThemeChangedEvent } from '../../events/concrete-events/on-page-theme-changed-event'; export class PageBackground extends PageElement { - public static readonly BLOB_SPACING = 325; - public static readonly MIN_BLOB_COUNT = 30; - public static readonly PERSPECTIVE = 5; - public static readonly Z_MIN = 10; - public static readonly Z_MAX = 30; - public static readonly ANIMATION_TIME = 250; + public static readonly blobSpacing = 325; + public static readonly minBlobCount = 30; + public static readonly perspective = 5; + public static readonly zMin = 10; + public static readonly zMax = 30; private backgroundSize: Vec2; private scrollPosition = 0; @@ -39,7 +38,7 @@ export class PageBackground extends PageElement { public handleOnLoadEvent(event: OnLoadEvent): OptionalEvent { this.parent = event.parent; - this.bindListeners(); + requestAnimationFrame(this.draw.bind(this)); return super.handleOnLoadEvent(event); } @@ -49,18 +48,10 @@ export class PageBackground extends PageElement { return super.handleOnPageThemeChangedEvent(event); } - private bindListeners() { - addEventListener('load', e => { - this.resize(); - this.createBlobs(); - this.redraw(e.timeStamp); - }); - } - private createBlobs() { const requiredBlobCount = Math.max( - PageBackground.MIN_BLOB_COUNT, - (this.backgroundSize.x * this.backgroundSize.y) / PageBackground.BLOB_SPACING ** 2 + PageBackground.minBlobCount, + (this.backgroundSize.x * this.backgroundSize.y) / PageBackground.blobSpacing ** 2 ); while (requiredBlobCount > this.blobs.length) { @@ -68,11 +59,6 @@ export class PageBackground extends PageElement { } } - private resize() { - this.resizeCanvas(); - this.resizeBackground(); - } - private resizeCanvas() { this.canvas.width = this.canvas.clientWidth; this.canvas.height = this.canvas.clientHeight; @@ -96,8 +82,8 @@ export class PageBackground extends PageElement { Math.max( 0, offset - - ((blob.z - PageBackground.Z_MIN) / - (PageBackground.Z_MAX - PageBackground.Z_MIN)) * + ((blob.z - PageBackground.zMin) / + (PageBackground.zMax - PageBackground.zMin)) * offset * q ); @@ -120,8 +106,10 @@ export class PageBackground extends PageElement { return [this.start, ...this.inBetween, this.end].map(e => e.htmlRoot); } - private redraw(timestamp: DOMHighResTimeStamp) { + private draw(timestamp: DOMHighResTimeStamp) { + this.resizeCanvas(); this.resizeBackground(); + this.createBlobs(); this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); 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 { @@ -152,12 +140,12 @@ export class PageBackground extends PageElement { } 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)); } 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); } diff --git a/src/page/background/blob.ts b/src/page/background/blob.ts index 5a1f5ec..e9bf25d 100644 --- a/src/page/background/blob.ts +++ b/src/page/background/blob.ts @@ -1,15 +1,18 @@ import { Vec2 } from './vec2'; import { Vec3 } from './vec3'; -import { mixColors } from '../../helper/mix-colors'; + import { Random } from '../../helper/random'; import { Animation } from './animation'; import { PageBackground } from './background'; +import { mix } from '../../helper/mix'; export class Blob { - private static readonly DARK_COLORS = ['#2c477a']; - private static readonly LIGHT_COLORS = ['#fff9e0', '#ffd6d6']; - private static readonly ROTATION = (-20 / 180) * Math.PI; - private static readonly CREATOR_RANDOM = new Random(51); + private static readonly darkColors = [new Vec3(44, 71, 122)]; + private static readonly lightColors = [ + new Vec3(255, 249, 224), + new Vec3(255, 214, 214), + ]; + private static readonly creatorRandom = new Random(51); private static colorPickerRandom = new Random(132); private static isDarkThemed = false; @@ -19,44 +22,43 @@ export class Blob { Blob.isDarkThemed = isDarkThemed; } - public readonly z = Blob.CREATOR_RANDOM.randomInInterval( - PageBackground.Z_MIN, - PageBackground.Z_MAX + public readonly z = Blob.creatorRandom.randomInInterval( + PageBackground.zMin, + PageBackground.zMax ); - private color: Animation; + private color: Animation; - private readonly positionQ = new Vec2( - Blob.CREATOR_RANDOM.next, - Blob.CREATOR_RANDOM.next - ); + private readonly positionQ = new Vec2(Blob.creatorRandom.next, Blob.creatorRandom.next); private _positionScale = 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() { + this.opacity = + 1 - (this.z - PageBackground.zMin) / (PageBackground.zMax - PageBackground.zMin); + this.decideColor(); } public decideColor() { - const target = mixColors( - Blob.isDarkThemed ? '#242638' : '#ffffff', - Blob.colorPickerRandom.choose( - Blob.isDarkThemed ? Blob.DARK_COLORS : Blob.LIGHT_COLORS - ), - (this.z - PageBackground.Z_MIN) / (PageBackground.Z_MAX - PageBackground.Z_MIN) + const target = Blob.colorPickerRandom.choose( + Blob.isDarkThemed ? Blob.darkColors : Blob.lightColors ); - this.color = new Animation( + this.color = new Animation( this.color ? this.color.value : target, target, - 250, - (f, t, q) => mixColors(f, t, 1 - q) + 125, + (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) { - this.color?.step(value); + public step(deltaTime: number) { + this.color?.step(deltaTime); } public get topLeft(): Vec3 { @@ -82,14 +84,15 @@ export class Blob { ctx.save(); ctx.translate(position.x, position.y); - ctx.rotate(Blob.ROTATION); + ctx.rotate((-20 / 180) * Math.PI); ctx.beginPath(); 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.closePath(); - ctx.fillStyle = this.color.value; + const { x, y, z } = this.color.value; + ctx.fillStyle = `rgba(${x}, ${y}, ${z}, ${this.opacity})`; ctx.fill(); ctx.restore();