diff --git a/src/data/portfolio.ts b/src/data/portfolio.ts index d2cf93c..1df6a35 100644 --- a/src/data/portfolio.ts +++ b/src/data/portfolio.ts @@ -1,4 +1,3 @@ -import { Background } from '../page/background/background'; import { Contact } from '../page/contact/contact.html'; import { Header } from '../page/header/header'; import { ImageViewer } from '../page/image-viewer/image-viewer'; @@ -26,7 +25,6 @@ import { towers } from './projects/towers'; import { CV, Email, GitHubLink, LinkedIn } from './shared'; const main = new Main( - new Background(1, 1), new Header({ name: 'AndrĂ¡s Schmelczer', image: me, diff --git a/src/page/background/background.html.ts b/src/page/background/background.html.ts deleted file mode 100644 index ba45421..0000000 --- a/src/page/background/background.html.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { html } from '../../types/html'; -import './background.scss'; - -export const generate = (): html => ` -
-`; diff --git a/src/page/background/background.scss b/src/page/background/background.scss deleted file mode 100644 index cc4b7d3..0000000 --- a/src/page/background/background.scss +++ /dev/null @@ -1,30 +0,0 @@ -@use '../../style/mixins' as *; - -#background { - transform-style: preserve-3d; - - @media print { - display: none; - } - - > div { - position: absolute; - width: 140px; - border-radius: 1000px; - transition: background-color var(--transition-time); - - &:nth-child(odd) { - background-color: #fff9e0; - } - - &:nth-child(even) { - background-color: #ffd6d6; - } - } -} - -@include in-dark-mode { - #background > div { - background-color: #2c477a; - } -} diff --git a/src/page/background/background.ts b/src/page/background/background.ts deleted file mode 100644 index f29e2ba..0000000 --- a/src/page/background/background.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { getHeight } from '../../helper/get-height'; -import { mix } from '../../helper/mix'; -import { Random } from '../../helper/random'; -import { sum } from '../../helper/sum'; -import { PageElement } from '../page-element'; -import { generate } from './background.html'; - -export class Background extends PageElement { - private static readonly perspective = 5; - private static readonly zMin = 6; - private static readonly zMax = 50; - private static readonly minHeight = 360; - private static readonly maxHeight = 740; - private static readonly minBlobCount = 30; - private static readonly blobCountScaler = 0.05; - private static readonly stableSeed = 51; - - private random = new Random(); - private stableRandom = new Random(); - private blobs: Array = []; - private windowHeight = 0; - private contentHeight = 0; - - public constructor( - private readonly topOffsetElementCount: number, - private readonly bottomOffsetElementCount: number - ) { - super(generate()); - } - - protected initialize() { - super.initialize(); - this.drawIfNecessary(); - } - - private maintainBlobCount() { - const targetCount = Math.max( - Background.minBlobCount, - Math.ceil(window.innerWidth * Background.blobCountScaler) - ); - const deltaCount = targetCount - this.blobs.length; - - for (let i = 0; i < deltaCount; i++) { - const blob = this.createBlob(); - this.blobs.push(blob); - this.htmlRoot.appendChild(blob); - } - for (let i = 0; i < -deltaCount; i++) { - const blob = this.blobs.pop(); - this.htmlRoot.removeChild(blob!); - } - } - - private createBlob(): HTMLElement { - const blob = document.createElement('div'); - const z = this.random.inInterval(Background.zMin, Background.zMax); - const endXSpan = - ((1 / Background.perspective) * (Background.zMax + Background.perspective)) / 2; - - const x = this.random.inInterval( - mix(0, -(endXSpan - 0.5), z / Background.zMax), - mix(1, 1 + endXSpan - 0.5, z / Background.zMax) - ); - - blob.style.left = `${x * 100}%`; - blob.style.zIndex = (-z).toFixed(0); - blob.style.opacity = ( - 1 - - (z - Background.zMin) / (Background.zMax - Background.zMin) - ).toString(); - blob.style.height = `${this.random.inInterval( - Background.minHeight, - Background.maxHeight - )}px`; - - return blob; - } - - private drawIfNecessary() { - const siblings = this.getSiblings(); - const currentContentHeight = sum(siblings.map(getHeight)); - - if ( - window.innerHeight !== this.windowHeight || - currentContentHeight !== this.contentHeight - ) { - this.windowHeight = window.innerHeight; - this.contentHeight = currentContentHeight; - this.maintainBlobCount(); - - this.randomizeBlobs( - sum(siblings.slice(0, this.topOffsetElementCount).map(getHeight)), - sum(siblings.slice(-this.bottomOffsetElementCount).map(getHeight)) - ); - } - - requestAnimationFrame(this.drawIfNecessary.bind(this)); - } - - private getSiblings(): Array { - return Array.prototype.slice - .call(this.htmlRoot.parentElement!.childNodes) - .filter((n: HTMLElement) => n !== this.htmlRoot); - } - - private randomizeBlobs(topOffset: number, bottomOffset: number) { - this.stableRandom.seed = Background.stableSeed; - this.blobs.forEach((b) => { - const z = -parseFloat(b.style.zIndex); - const y = this.getRandomYInSafeArea( - z, - topOffset, - bottomOffset, - parseFloat(b.style.height) - ); - - b.style.transform = `translate3D(0, ${y}px, ${-z}px) rotate(-20deg)`; - }); - } - - private getRandomYInSafeArea( - z: number, - topOffset: number, - bottomOffset: number, - height: number - ): number { - const farTop = -( - ((this.windowHeight / 2 - topOffset) / Background.perspective) * - (Background.zMax + Background.perspective) - - this.windowHeight / 2 - ); - - const farBottom = Math.min( - ((this.windowHeight / 2 - bottomOffset) / Background.perspective) * - (Background.zMax + Background.perspective) - - this.windowHeight / 2 + - this.contentHeight, - this.contentHeight - height - ); - - return this.stableRandom.inInterval( - mix(topOffset, farTop, z / Background.zMax), - farBottom - ); - } -} diff --git a/src/page/main/main.html.ts b/src/page/main/main.html.ts index 72540b1..217b3b4 100644 --- a/src/page/main/main.html.ts +++ b/src/page/main/main.html.ts @@ -1,6 +1,6 @@ import { html } from '../../types/html'; import './main.scss'; -export const generate = (): html => ` -
+export const generate = (perspective: number): html => ` +
`; diff --git a/src/page/main/main.scss b/src/page/main/main.scss index 059476b..2dbb8cc 100644 --- a/src/page/main/main.scss +++ b/src/page/main/main.scss @@ -4,7 +4,6 @@ main { height: 100%; overflow-x: hidden; overflow-y: scroll; - perspective: 5px; scroll-behavior: smooth; // chrome scrolling does not work on PC without this @@ -22,4 +21,30 @@ main { border-radius: var(--border-radius); } } + + > .blob { + top: 0; + position: absolute; + width: 140px; + border-radius: 1000px; + transition: background-color var(--transition-time); + + &:nth-child(odd) { + background-color: #fff9e0; + } + + &:nth-child(even) { + background-color: #ffd6d6; + } + + @media print { + display: none; + } + } +} + +@include in-dark-mode { + main > .blob { + background-color: #2c477a; + } } diff --git a/src/page/main/main.ts b/src/page/main/main.ts index c1a2f3c..8b0e3cd 100644 --- a/src/page/main/main.ts +++ b/src/page/main/main.ts @@ -1,13 +1,142 @@ +import { getHeight } from '../../helper/get-height'; +import { mix } from '../../helper/mix'; +import { Random } from '../../helper/random'; +import { sum } from '../../helper/sum'; import { PageElement } from '../page-element'; import { generate } from './main.html'; export class Main extends PageElement { + private static readonly perspective = 5; + private static readonly zMin = 6; + private static readonly zMax = 50; + private static readonly minHeight = 360; + private static readonly maxHeight = 740; + private static readonly minBlobCount = 30; + private static readonly blobCountScaler = 0.05; + private static readonly stableSeed = 51; + + private readonly topOffsetElementCount = 1; + private readonly bottomOffsetElementCount = 1; + + private random = new Random(); + private stableRandom = new Random(); + private blobs: Array = []; + private windowHeight = 0; + private contentHeight = 0; + constructor(...children: Array) { const actualChildren = children.map((c) => c instanceof PageElement ? c : new PageElement(c) ); - super(generate(), actualChildren); + super(generate(Main.perspective), actualChildren); actualChildren.forEach((c) => this.attachElement(c)); } + + protected initialize() { + super.initialize(); + this.drawIfNecessary(); + } + + private maintainBlobCount() { + const targetCount = Math.max( + Main.minBlobCount, + Math.ceil(window.innerWidth * Main.blobCountScaler) + ); + const deltaCount = targetCount - this.blobs.length; + + for (let i = 0; i < deltaCount; i++) { + const blob = this.createBlob(); + this.blobs.push(blob); + this.htmlRoot.appendChild(blob); + } + for (let i = 0; i < -deltaCount; i++) { + const blob = this.blobs.pop(); + this.htmlRoot.removeChild(blob!); + } + } + + private createBlob(): HTMLElement { + const blob = document.createElement('div'); + blob.className = 'blob'; + const z = this.random.inInterval(Main.zMin, Main.zMax); + const endXSpan = ((1 / Main.perspective) * (Main.zMax + Main.perspective)) / 2; + + const x = this.random.inInterval( + mix(0, -(endXSpan - 0.5), z / Main.zMax), + mix(1, 1 + endXSpan - 0.5, z / Main.zMax) + ); + + blob.style.left = `${x * 100}%`; + blob.style.zIndex = (-z).toFixed(0); + blob.style.opacity = (1 - (z - Main.zMin) / (Main.zMax - Main.zMin)).toString(); + blob.style.height = `${this.random.inInterval(Main.minHeight, Main.maxHeight)}px`; + + return blob; + } + + private drawIfNecessary() { + const siblings = this.getSiblings(); + const currentContentHeight = sum(siblings.map(getHeight)); + + if ( + window.innerHeight !== this.windowHeight || + currentContentHeight !== this.contentHeight + ) { + this.windowHeight = window.innerHeight; + this.contentHeight = currentContentHeight; + this.maintainBlobCount(); + + this.randomizeBlobs( + sum(siblings.slice(0, this.topOffsetElementCount).map(getHeight)), + sum(siblings.slice(-this.bottomOffsetElementCount).map(getHeight)) + ); + } + + requestAnimationFrame(this.drawIfNecessary.bind(this)); + } + + private getSiblings(): Array { + return Array.prototype.slice + .call(this.htmlRoot.childNodes) + .filter((n: HTMLElement) => !n.classList.contains('blob')); + } + + private randomizeBlobs(topOffset: number, bottomOffset: number) { + this.stableRandom.seed = Main.stableSeed; + this.blobs.forEach((b) => { + const z = -parseFloat(b.style.zIndex); + const y = this.getRandomYInSafeArea( + z, + topOffset, + bottomOffset, + parseFloat(b.style.height) + ); + + b.style.transform = `translate3D(0, ${y}px, ${-z}px) rotate(-20deg)`; + }); + } + + private getRandomYInSafeArea( + z: number, + topOffset: number, + bottomOffset: number, + height: number + ): number { + const farTop = -( + ((this.windowHeight / 2 - topOffset) / Main.perspective) * + (Main.zMax + Main.perspective) - + this.windowHeight / 2 + ); + + const farBottom = Math.min( + ((this.windowHeight / 2 - bottomOffset) / Main.perspective) * + (Main.zMax + Main.perspective) - + this.windowHeight / 2 + + this.contentHeight, + this.contentHeight - height + ); + + return this.stableRandom.inInterval(mix(topOffset, farTop, z / Main.zMax), farBottom); + } }