diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..d021f04 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/vagrant.xml b/.idea/vagrant.xml new file mode 100644 index 0000000..a5aa786 --- /dev/null +++ b/.idea/vagrant.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 0000000..8230eb2 --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,9 @@ +{ + "rules": { + "at-rule-no-unknown": [ + true, { + "ignoreAtRules": ["use", "mixin", "include"] + } + ] + } +} diff --git a/src/dummy.scss b/src/dummy.scss new file mode 100644 index 0000000..d13cc97 --- /dev/null +++ b/src/dummy.scss @@ -0,0 +1,8 @@ +@use "styles"; +@use "page/about/about"; +@use "page/background/background"; +@use "page/content/content"; +@use "page/footer/footer"; +@use "page/image-viewer/image-viewer"; +@use "page/timeline/timeline"; +@use "page/timeline/timeline-element/timeline-element"; diff --git a/src/framework/page.ts b/src/framework/page.ts index 02be160..f70a3ca 100644 --- a/src/framework/page.ts +++ b/src/framework/page.ts @@ -4,15 +4,22 @@ import { PageEventType } from "./page-event"; export class Page extends PageElement { public constructor( children: Array, - private rootElement: HTMLElement + private rootElement: HTMLElement, + isRootPage = false ) { super(children); this.setElement(rootElement); - this.broadcastEvent( - { type: PageEventType.eventBroadcasterChanged, data: this }, - this - ); + if (isRootPage) { + this.broadcastEvent( + { type: PageEventType.eventBroadcasterChanged, data: this }, + this + ); + } + rootElement.append(...children.map(e => e.getElement()).filter(e => e)); - this.broadcastEvent({ type: PageEventType.onLoad }, this); + + if (isRootPage) { + this.broadcastEvent({ type: PageEventType.onLoad }, this); + } } } diff --git a/src/page/about/about.html.ts b/src/page/about/about.html.ts index e87aa1c..93d7fa8 100644 --- a/src/page/about/about.html.ts +++ b/src/page/about/about.html.ts @@ -7,8 +7,10 @@ export const generate = ( aPictureOf: string ): html => `
-
- ${aPictureOf} ${name} -

${name}

-
+
+
+ ${aPictureOf} ${name} +

${name}

+
+
`; diff --git a/src/page/about/about.scss b/src/page/about/about.scss index 6aed5e1..b73a1f1 100644 --- a/src/page/about/about.scss +++ b/src/page/about/about.scss @@ -2,26 +2,42 @@ @import "../../style/vars"; #about { - margin-top: $normal-margin - $small-margin; - padding: $small-margin; + width: 100%; + backdrop-filter: blur($blur-radius); + padding: $normal-margin; - header { - @include center-children(); + .container { + width: $body-width; + margin: auto; - h1, - img { - @include title-font(); + header { + @include center-children(); + + h1, + img { + @include title-font(); + } + + img { + @include square(4ch); + border-radius: 100%; + margin-right: 1.5ex; + cursor: pointer; + } + + @media (max-width: $breakpoint-width) { + flex-direction: column; + img { + margin: 0 0 $small-margin 0; + } + h1 { + text-align: center; + } + } } - img { - @include square(4ch); - border-radius: 100%; - margin-right: 1.5ex; - cursor: pointer; + * { + text-align: justify; } } - - * { - text-align: justify; - } } diff --git a/src/page/about/about.ts b/src/page/about/about.ts index 1d7c751..578d31f 100644 --- a/src/page/about/about.ts +++ b/src/page/about/about.ts @@ -10,8 +10,8 @@ export class PageHeader extends PageElement { const root = createElement(generate(header, aPictureOf)); const content = new PageContent(header.about); - root.appendChild(content.getElement()); super([content]); this.setElement(root); + this.query(".container").appendChild(content.getElement()); } } diff --git a/src/page/background/background.html.ts b/src/page/background/background.html.ts index d7350f4..9f154df 100644 --- a/src/page/background/background.html.ts +++ b/src/page/background/background.html.ts @@ -5,6 +5,7 @@ export const generate = ( count: number, color?: () => string, height?: () => number, + isAnimated?: (index) => boolean, transform?: () => string ): html => `
@@ -13,8 +14,12 @@ export const generate = ( ? new Array(count) .fill(0, 0, count) .map( - _ => - `
` + (_, i) => ` +
+ ` ) .join("") : "" diff --git a/src/page/background/background.scss b/src/page/background/background.scss index b56468e..cfd25c5 100644 --- a/src/page/background/background.scss +++ b/src/page/background/background.scss @@ -9,11 +9,25 @@ transform-style: preserve-3d; overflow: hidden; + transition: height $slow-transition-time, width $slow-transition-time; + div { border-radius: 10000px; position: absolute; left: 0; top: 0; width: 160px; + + &.animated { + animation: fade-in 1s linear forwards; + @keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + } } } diff --git a/src/page/background/background.ts b/src/page/background/background.ts index a9b2f18..b3740cb 100644 --- a/src/page/background/background.ts +++ b/src/page/background/background.ts @@ -13,6 +13,9 @@ export class PageBackground extends PageElement { private colors = ["#fff9e077", "#ffd6d677"]; private blobSize = 150; // with margin private perspective = 5; + private currentRealHeight = 0; + private currentRealWidth = 0; + private currentBlobCount = 0; public constructor() { super(); @@ -39,6 +42,7 @@ export class PageBackground extends PageElement { siblings.map(c => { const computedStyle = window.getComputedStyle(c); return ( + // ignores margin collapse c.clientHeight + parseInt(computedStyle.marginTop) + parseInt(computedStyle.marginBottom) + @@ -48,34 +52,45 @@ export class PageBackground extends PageElement { }) ); - const random = randomFactory(42); + if (height > this.currentRealHeight || width > this.currentRealWidth) { + this.currentRealHeight = height; + this.currentRealWidth = width; - const count = Math.round((width * height) / this.blobSize ** 2); + const random = randomFactory(44); - const randomWithKnownZ = (z: number, bound: number): number => { - const l = (bound * (this.perspective + z)) / this.perspective; - return randomInInterval(-(l / 2 - bound / 2), l / 2 + bound / 2, random); - }; + const count = Math.round((width * height) / this.blobSize ** 2); - this.setElement( - createElement( - generate( - count, - () => choose(this.colors, random), - () => randomInInterval(160, 750, random), - () => { - const z = randomInInterval(-12, -25, random); - return ` + const randomWithKnownZ = (z: number, bound: number): number => { + const l = (bound * (this.perspective + z)) / this.perspective; + return randomInInterval( + -(l / 2 - bound / 2), + l / 2 + bound / 2, + random + ); + }; + + this.setElement( + createElement( + generate( + count, + () => choose(this.colors, random), + () => randomInInterval(160, 750, random), + i => i >= this.currentBlobCount, + () => { + const z = randomInInterval(-12, -25, random); + return ` translateX(${randomWithKnownZ(-z, width)}px) translateY(${randomWithKnownZ(-z, height)}px) translateZ(${z}px) rotate(-20deg) `; - } + } + ) ) - ) - ); + ); + this.currentBlobCount = count; + } this.getElement().style.width = `${width}px`; this.getElement().style.height = `${height}px`; } diff --git a/src/page/content/content.scss b/src/page/content/content.scss index a442235..52ea1a3 100644 --- a/src/page/content/content.scss +++ b/src/page/content/content.scss @@ -1,10 +1,10 @@ -@import "../../style/vars"; +@use "../../style/vars"; .content { - margin-top: $small-margin; + margin-top: vars.$small-margin; * { - margin-top: $line-height; + margin-top: vars.$line-height; } p { diff --git a/src/page/footer/footer.scss b/src/page/footer/footer.scss index 9e7a8f7..a79d4f4 100644 --- a/src/page/footer/footer.scss +++ b/src/page/footer/footer.scss @@ -7,7 +7,7 @@ footer#page-footer { margin: $small-margin auto 0 auto; padding: $normal-margin $normal-margin $line-height $normal-margin; width: 100%; - backdrop-filter: blur(5px); + backdrop-filter: blur($blur-radius); h2 { @include title-font(); diff --git a/src/page/image-viewer/image-viewer.scss b/src/page/image-viewer/image-viewer.scss index 003a496..80b54c5 100644 --- a/src/page/image-viewer/image-viewer.scss +++ b/src/page/image-viewer/image-viewer.scss @@ -4,7 +4,6 @@ #image-viewer { @include center-children(); display: none; - position: fixed; width: 100%; height: 100%; @@ -22,6 +21,7 @@ #cancel { @include square($icon-size); position: absolute; + box-sizing: content-box; padding: $normal-margin; right: 0; top: 0; diff --git a/src/page/image-viewer/image-viewer.ts b/src/page/image-viewer/image-viewer.ts index 24f0f3a..0991ba1 100644 --- a/src/page/image-viewer/image-viewer.ts +++ b/src/page/image-viewer/image-viewer.ts @@ -8,9 +8,8 @@ export class PageImageViewer extends PageElement { public constructor() { super(); const root = createElement(generate()); - (root.querySelector("#cancel") as HTMLElement).onclick = () => - PageImageViewer.hide(root); this.setElement(root); + this.query("#cancel").onclick = () => PageImageViewer.hide(root); } protected handleEvent(event: PageEvent, parent: PageElement) { diff --git a/src/page/index.ts b/src/page/index.ts index 2adbaba..9a7df0d 100644 --- a/src/page/index.ts +++ b/src/page/index.ts @@ -8,15 +8,21 @@ import { Page } from "../framework/page"; export const create = ({ config, header, timeline, footer }: Portfolio) => { document.title = header.name; - new Page( [ - new PageBackground(), - new PageHeader(header, config.aPictureOf), - new PageTimeline(timeline, config.showMore, config.showLess), - new PageFooter(footer), - new PageImageViewer() + new PageImageViewer(), + new Page( + [ + new PageBackground(), + new PageHeader(header, config.aPictureOf), + new PageTimeline(timeline, config.showMore, config.showLess), + new PageFooter(footer) + ], + document.body.querySelector("main"), + false + ) ], - document.body.querySelector("main") + document.body, + true ); }; diff --git a/src/page/timeline/timeline-element/timeline-element.scss b/src/page/timeline/timeline-element/timeline-element.scss index 4456755..ce08625 100644 --- a/src/page/timeline/timeline-element/timeline-element.scss +++ b/src/page/timeline/timeline-element/timeline-element.scss @@ -23,7 +23,7 @@ @include square($icon-size); position: absolute; top: 33%; - left: -0.5 * $icon-size - (1.5 * $line-width); + left: calc(-0.5 * #{$icon-size} - (1.5 * #{$line-width})); border: $line-width solid $normal-text-color; border-radius: 100%; background: $background; diff --git a/src/page/timeline/timeline-element/timeline-element.ts b/src/page/timeline/timeline-element/timeline-element.ts index eea1056..ee03938 100644 --- a/src/page/timeline/timeline-element/timeline-element.ts +++ b/src/page/timeline/timeline-element/timeline-element.ts @@ -37,21 +37,31 @@ export class PageTimelineElement extends PageElement { this.more.style.height = "0"; PageTimelineElement.show(showMore); PageTimelineElement.hide(showLess); + this.notifyOfHeightChange(); } else { - this.openMoreToFullHeight(); PageTimelineElement.hide(showMore); PageTimelineElement.show(showLess); + this.openMoreToFullHeight(); } this.isOpen = !this.isOpen; - this.eventBroadcaster?.broadcastEvent({ - type: PageEventType.onBodyDimensionsChanged - }); + } + + private notifyOfHeightChange(change: number = null) { + const notify = () => + this.eventBroadcaster?.broadcastEvent({ + type: PageEventType.onBodyDimensionsChanged, + data: change + }); + notify(); + setTimeout(notify, 350); } private static hide(element: HTMLElement) { element.style.opacity = "0"; - setTimeout(() => (element.style.visibility = "hidden"), 350); + setTimeout(() => { + element.style.visibility = "hidden"; + }, 350); } private static show(element: HTMLElement) { @@ -61,6 +71,7 @@ export class PageTimelineElement extends PageElement { private openMoreToFullHeight() { this.more.style.height = `${this.more.scrollHeight.toString()}px`; + this.notifyOfHeightChange(); } private handleResize() { diff --git a/src/style/vars.scss b/src/style/vars.scss index 14b6db0..3b6aec6 100644 --- a/src/style/vars.scss +++ b/src/style/vars.scss @@ -13,21 +13,30 @@ $scrollbar-color: #ffd6d6; $fast-transition-time: 220ms; $slow-transition-time: 350ms; -$line-width: 3px; +$line-width: var(--line-width); $border-radius: 5px; $breakpoint-width: 900px; -$normal-margin: 45px; -$small-margin: 25px; +$normal-margin: var(--normal-margin); +$small-margin: var(--small-margin); $line-height: 2ch; -$icon-size: 25px; -$body-width: 765px; +$blur-radius: 6px; +$icon-size: var(--icon-size); +$body-width: var(--body-width); -@media (max-width: $breakpoint-width) { - $line-width: 2px; - $normal-margin: 25px; - $small-margin: 20px; - $icon-size: 20px; - $body-width: 85%; +:root { + --line-width: 3px; + --normal-margin: 45px; + --small-margin: 25px; + --icon-size: 25px; + --body-width: 765px; + + @media (max-width: $breakpoint-width) { + --line-width: 2px; + --normal-margin: 35px; + --small-margin: 15px; + --icon-size: 20px; + --body-width: 85%; + } }