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);
+ }
}