Optimizations
This commit is contained in:
parent
10015a4ebe
commit
3febd3fca8
24 changed files with 215 additions and 99 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { html } from "../model/misc";
|
||||
import { html, ResponsiveImage } from "../model/misc";
|
||||
|
||||
export const createElement = (from: html): HTMLElement => {
|
||||
const element: HTMLElement = document.createElement("div");
|
||||
|
|
@ -88,3 +88,6 @@ export const range = ({
|
|||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const last = <T>(list: Array<T>): T =>
|
||||
list.length > 0 ? list[list.length - 1] : undefined;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { url } from "./misc";
|
||||
import { ResponsiveImage, url } from "./misc";
|
||||
|
||||
interface Anchor {
|
||||
type: "a";
|
||||
|
|
@ -6,11 +6,19 @@ interface Anchor {
|
|||
text: string;
|
||||
}
|
||||
|
||||
interface Video {
|
||||
export type Video = {
|
||||
type: "video";
|
||||
src: url;
|
||||
}
|
||||
mp4: url;
|
||||
webm: url;
|
||||
options?: string;
|
||||
};
|
||||
|
||||
export type TypedContent = Anchor | Video;
|
||||
export type Image = {
|
||||
type: "img";
|
||||
alt: string;
|
||||
image: ResponsiveImage;
|
||||
};
|
||||
|
||||
export type TypedContent = Anchor | Video | Image;
|
||||
|
||||
export type Content = Array<String | TypedContent>;
|
||||
|
|
|
|||
|
|
@ -1,2 +1,16 @@
|
|||
export type url = string;
|
||||
|
||||
export type html = string;
|
||||
|
||||
export type ResponsiveImage = {
|
||||
srcSet: string;
|
||||
src: url;
|
||||
placeholder: string;
|
||||
width: number;
|
||||
height: number;
|
||||
images: Array<{
|
||||
path: url;
|
||||
width: number;
|
||||
height: number;
|
||||
}>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { url } from "./misc";
|
||||
import { Content } from "./content";
|
||||
import { Content, Image, Video } from "./content";
|
||||
|
||||
export interface Portfolio {
|
||||
config: Config;
|
||||
|
|
@ -11,19 +11,18 @@ export interface Portfolio {
|
|||
export interface Config {
|
||||
showMore: string;
|
||||
showLess: string;
|
||||
aPictureOf: string;
|
||||
}
|
||||
|
||||
export interface Header {
|
||||
name: string;
|
||||
picture: url;
|
||||
picture: Image;
|
||||
about: Content;
|
||||
}
|
||||
|
||||
export interface TimelineElement {
|
||||
title: string;
|
||||
date: string;
|
||||
picture: url;
|
||||
figure: Image | Video;
|
||||
description: string;
|
||||
more?: Content;
|
||||
link?: url;
|
||||
|
|
@ -34,8 +33,6 @@ export interface Footer {
|
|||
email: string;
|
||||
cv: url;
|
||||
cvName: string;
|
||||
githubLinkName: string;
|
||||
githubLink: url;
|
||||
lastEditName: string;
|
||||
lastEdit: Date;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import { Header } from "../../model/portfolio";
|
||||
import { html } from "../../model/misc";
|
||||
import { PageContent } from "../content/content";
|
||||
|
||||
import "./about.scss";
|
||||
|
||||
export const generate = (
|
||||
{ name, picture, about }: Header,
|
||||
aPictureOf: string
|
||||
): html => `
|
||||
export const generate = ({ name, picture }: Header): html => `
|
||||
<section id="about">
|
||||
<img alt="${aPictureOf} ${name}" src="${picture}"/>
|
||||
${PageContent.parseTypedContent(picture, true)}
|
||||
<div class="placeholder"></div>
|
||||
<h1>${name}</h1>
|
||||
</section>`;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import { generate } from "./about.html";
|
|||
import { createElement } from "../../framework/helper";
|
||||
|
||||
export class PageHeader extends PageElement {
|
||||
public constructor(header: Header, aPictureOf: string) {
|
||||
const root = createElement(generate(header, aPictureOf));
|
||||
public constructor(header: Header) {
|
||||
const root = createElement(generate(header));
|
||||
const content = new PageContent(header.about);
|
||||
|
||||
super([content]);
|
||||
|
|
|
|||
|
|
@ -11,3 +11,15 @@
|
|||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.image-container {
|
||||
font-size: 0;
|
||||
box-shadow: inset $shadow1, inset $shadow2;
|
||||
pointer-events: none;
|
||||
img {
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: -2;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,44 @@
|
|||
import { Content, TypedContent } from "../../model/content";
|
||||
import "./content.scss";
|
||||
import { PageElement } from "../../framework/page-element";
|
||||
import { createElement } from "../../framework/helper";
|
||||
import { createElement, last } from "../../framework/helper";
|
||||
import { html } from "../../model/misc";
|
||||
|
||||
export class PageContent extends PageElement {
|
||||
private static isTyped(content): content is TypedContent {
|
||||
return (content as TypedContent).type !== undefined;
|
||||
}
|
||||
|
||||
public static parseTypedContent(
|
||||
element: TypedContent,
|
||||
disableInnerShadow?: boolean
|
||||
): html {
|
||||
if (element.type === "a") {
|
||||
return `<a href="${element.href}" rel="noreferrer" target="_blank"> ${element.text} </a>`;
|
||||
}
|
||||
if (element.type === "video") {
|
||||
return `
|
||||
<video ${element.options}>
|
||||
<source src="${element.webm}" type="video/webm"/>
|
||||
<source src="${element.mp4}" type="video/mp4"/>
|
||||
</video>
|
||||
`;
|
||||
}
|
||||
if (element.type === "img") {
|
||||
return `
|
||||
${!disableInnerShadow ? `<div class="image-container">` : ""}
|
||||
<img
|
||||
srcset="${element.image.srcSet}"
|
||||
src="${last(element.image.images).path}"
|
||||
alt="${element.alt}"
|
||||
/>
|
||||
${!disableInnerShadow ? `</div>` : ""}
|
||||
`;
|
||||
}
|
||||
|
||||
throw new Error("Unhandled type.");
|
||||
}
|
||||
|
||||
public constructor(content: Content) {
|
||||
super();
|
||||
|
||||
|
|
@ -15,18 +46,11 @@ export class PageContent extends PageElement {
|
|||
createElement(`
|
||||
<div class="content">
|
||||
${content
|
||||
.map(element => {
|
||||
if (PageContent.isTyped(element)) {
|
||||
if (element.type === "a") {
|
||||
return `<a href="${element.href}" rel="noreferrer" target="_blank"> ${element.text} </a>`;
|
||||
}
|
||||
if (element.type === "video") {
|
||||
return `<video controls><source src="${element.src}" /></video>`;
|
||||
}
|
||||
throw new Error("Unhandled type.");
|
||||
}
|
||||
return `<p>${element}</p>`;
|
||||
})
|
||||
.map(element =>
|
||||
PageContent.isTyped(element)
|
||||
? PageContent.parseTypedContent(element)
|
||||
: `<p>${element}</p>`
|
||||
)
|
||||
.join("\n")}
|
||||
</div>
|
||||
`)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { Footer } from "../../model/portfolio";
|
||||
import { html, url } from "../../model/misc";
|
||||
import { html } from "../../model/misc";
|
||||
import emailIcon from "../../static/icons/at.svg";
|
||||
import cvIcon from "../../static/icons/cv.svg";
|
||||
|
||||
import "./footer.scss";
|
||||
import cvIcon from "../../static/icons/cv.svg";
|
||||
import emailIcon from "../../static/icons/at.svg";
|
||||
|
||||
export const generate = ({
|
||||
title,
|
||||
|
|
@ -11,9 +11,7 @@ export const generate = ({
|
|||
cv,
|
||||
cvName,
|
||||
lastEditName,
|
||||
lastEdit,
|
||||
githubLinkName,
|
||||
githubLink
|
||||
lastEdit
|
||||
}: Footer): html => `
|
||||
<footer id="page-footer">
|
||||
<h2>${title}</h2>
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ footer#page-footer {
|
|||
|
||||
h6 {
|
||||
@include insignificant-font();
|
||||
opacity: 0.4;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { PageImageViewer } from "./image-viewer/image-viewer";
|
|||
import { Page } from "../framework/page";
|
||||
|
||||
export const create = ({ config, header, timeline, footer }: Portfolio) => {
|
||||
const pageHeader = new PageHeader(header, config.aPictureOf);
|
||||
const pageHeader = new PageHeader(header);
|
||||
const pageFooter = new PageFooter(footer);
|
||||
|
||||
const bg = new PageBackground(pageHeader, pageFooter);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { TimelineElement } from "../../../model/portfolio";
|
||||
import { html } from "../../../model/misc";
|
||||
import { PageContent } from "../../content/content";
|
||||
|
||||
import "./timeline-element.scss";
|
||||
|
||||
export const generate = (
|
||||
{ date, title, picture, description, more, link }: TimelineElement,
|
||||
{ date, title, figure, description, more, link }: TimelineElement,
|
||||
showMore: string,
|
||||
showLess: string
|
||||
): html => `
|
||||
|
|
@ -13,9 +15,7 @@ export const generate = (
|
|||
</div>
|
||||
<div class="card">
|
||||
<h2>${title}</h2>
|
||||
<div class="image-container">
|
||||
<img src="${picture}" alt="${picture}"/>
|
||||
</div>
|
||||
${PageContent.parseTypedContent(figure)}
|
||||
<p class="description">${description}</p>
|
||||
${
|
||||
more
|
||||
|
|
|
|||
|
|
@ -75,18 +75,6 @@
|
|||
@include sub-title-font();
|
||||
}
|
||||
|
||||
.image-container {
|
||||
font-size: 0;
|
||||
box-shadow: inset $shadow1, inset $shadow2;
|
||||
pointer-events: none;
|
||||
img {
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: -2;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
font-style: italic;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { Portfolio } from "./model/portfolio";
|
||||
|
||||
import me from "./static/media/me.jpg";
|
||||
import forex from "./static/media/forex.gif";
|
||||
import forexMP4 from "./static/media/forex.mp4";
|
||||
import forexWEBM from "./static/media/forex.webm";
|
||||
import myNotes from "./static/media/my-notes.jpg";
|
||||
import processSimulator from "./static/media/process-simulator.jpg";
|
||||
import processSimulatorInput from "./static/media/process-simulator-input.jpg";
|
||||
|
|
@ -11,17 +12,21 @@ import platform from "./static/media/platform.png";
|
|||
import photos from "./static/media/photos.jpg";
|
||||
import led from "./static/media/led.jpg";
|
||||
import cv from "./static/cv/andras_schmelczer_cv_2020_01.pdf";
|
||||
import ledVideo from "./static/media/led720.mp4";
|
||||
import ledMP4 from "./static/media/led.mp4";
|
||||
import ledWEBM from "./static/media/led.webm";
|
||||
|
||||
export const portfolio: Portfolio = {
|
||||
config: {
|
||||
showMore: `Show details`,
|
||||
showLess: `Show less`,
|
||||
aPictureOf: `a picture of`
|
||||
showLess: `Show less`
|
||||
},
|
||||
header: {
|
||||
name: `András Schmelczer`,
|
||||
picture: me,
|
||||
picture: {
|
||||
type: `img`,
|
||||
image: me,
|
||||
alt: `a picture of me`
|
||||
},
|
||||
about: [
|
||||
`I have always been fascinated by the engineering feats that surround us.
|
||||
When I realized that someday I might be able to contribute to these achievements,
|
||||
|
|
@ -34,7 +39,12 @@ export const portfolio: Portfolio = {
|
|||
{
|
||||
title: `Predicting foreign exchange rates`,
|
||||
date: `2019 Autumn`,
|
||||
picture: forex,
|
||||
figure: {
|
||||
type: `video`,
|
||||
options: `autoplay loop muted playsinline`,
|
||||
webm: forexWEBM,
|
||||
mp4: forexMP4
|
||||
},
|
||||
description: `From the animation we can see that my algorithm does a somewhat acceptable job at
|
||||
predicting (blue graph) the EUR/USD rates (green graph).`,
|
||||
more: [
|
||||
|
|
@ -49,7 +59,11 @@ export const portfolio: Portfolio = {
|
|||
{
|
||||
date: `2019 November`,
|
||||
title: `My Notes`,
|
||||
picture: myNotes,
|
||||
figure: {
|
||||
type: `img`,
|
||||
image: myNotes,
|
||||
alt: `two screenshots of the application`
|
||||
},
|
||||
description: `A minimalist note organizer and editor powered by Markwon.`,
|
||||
more: [
|
||||
{
|
||||
|
|
@ -65,7 +79,11 @@ export const portfolio: Portfolio = {
|
|||
{
|
||||
date: `2018 October - November`,
|
||||
title: `Simulating the cooling system of a nuclear facility`,
|
||||
picture: processSimulator,
|
||||
figure: {
|
||||
type: `img`,
|
||||
image: processSimulator,
|
||||
alt: `a screenshot of the simulator`
|
||||
},
|
||||
description: `Dynamically calculating the temperatures and flow velocities
|
||||
in a fluid based cooling system based on a simple model.`,
|
||||
more: [
|
||||
|
|
@ -79,7 +97,11 @@ export const portfolio: Portfolio = {
|
|||
{
|
||||
date: `2018 October - November`,
|
||||
title: `Graph editing application`,
|
||||
picture: processSimulatorInput,
|
||||
figure: {
|
||||
type: `img`,
|
||||
image: processSimulatorInput,
|
||||
alt: `a picture of the simulator's UI`
|
||||
},
|
||||
description: `An intuitive editor to create and edit input files for the nuclear facility simulator.`,
|
||||
more: [
|
||||
`Nodes can be moved with drag&drop gestures. Editing the parameters of elements
|
||||
|
|
@ -91,7 +113,11 @@ export const portfolio: Portfolio = {
|
|||
{
|
||||
date: `2018 July - August`,
|
||||
title: `City simulation`,
|
||||
picture: citySimulation,
|
||||
figure: {
|
||||
type: `img`,
|
||||
image: citySimulation,
|
||||
alt: `a picture of a low-poly city`
|
||||
},
|
||||
description: `Simulating a city where car crashes are more frequent than usual.`,
|
||||
more: [
|
||||
`Through a REST API the state of the traffic lights can be changed.
|
||||
|
|
@ -109,7 +135,11 @@ export const portfolio: Portfolio = {
|
|||
{
|
||||
date: `2018 June`,
|
||||
title: `Photo color grader`,
|
||||
picture: color,
|
||||
figure: {
|
||||
type: `img`,
|
||||
image: color,
|
||||
alt: `a picture of the app`
|
||||
},
|
||||
description: `An innovative (at least I thought so) color grader web application.`,
|
||||
more: [
|
||||
`The most noteworthy feature of this application is the color selector UI.
|
||||
|
|
@ -126,7 +156,11 @@ export const portfolio: Portfolio = {
|
|||
date: `2017 autumn`,
|
||||
|
||||
title: `Platform game`,
|
||||
picture: platform,
|
||||
figure: {
|
||||
type: `img`,
|
||||
image: platform,
|
||||
alt: `a picture of the app`
|
||||
},
|
||||
description: `A 3D game written in C with the help of SDL 1.2 (I haven't heard of GPU programming at the time).`,
|
||||
more: [
|
||||
`The maps are randomly generated and fully destroyable.
|
||||
|
|
@ -137,14 +171,22 @@ export const portfolio: Portfolio = {
|
|||
{
|
||||
date: `2016 summer`,
|
||||
title: `Photos`,
|
||||
picture: photos,
|
||||
figure: {
|
||||
type: `img`,
|
||||
image: photos,
|
||||
alt: `a picture of the website`
|
||||
},
|
||||
description: `A simple web page where you can view my photos.`,
|
||||
link: `schmelczer.dev/photos`
|
||||
},
|
||||
{
|
||||
date: `2016 spring`,
|
||||
title: `Lights synchronised to music`,
|
||||
picture: led,
|
||||
figure: {
|
||||
type: `img`,
|
||||
image: led,
|
||||
alt: `a picture from the video`
|
||||
},
|
||||
description: `A full stack application with a built-in
|
||||
music player which music controls the color of some RGB LED strips.`,
|
||||
more: [
|
||||
|
|
@ -154,7 +196,7 @@ export const portfolio: Portfolio = {
|
|||
A quite simple frontend for accessing the music player and changing
|
||||
the settings also got built using vanilla web development technologies.`,
|
||||
`Below is a video showing the system in work.`,
|
||||
{ type: `video`, src: ledVideo }
|
||||
{ type: `video`, mp4: ledMP4, webm: ledWEBM, options: "controls" }
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
@ -163,9 +205,7 @@ export const portfolio: Portfolio = {
|
|||
cv,
|
||||
email: `andras@schmelczer.dev`,
|
||||
cvName: `Curriculum vitae`,
|
||||
githubLinkName: `view source`,
|
||||
githubLink: `https://github.com/schmelczerandras/timeline`,
|
||||
lastEditName: `Last modified on `,
|
||||
lastEdit: new Date(2019, 11, 27) // months are 0 indexed
|
||||
lastEdit: new Date(2019, 11, 29) // months are 0 indexed
|
||||
}
|
||||
};
|
||||
|
|
|
|||
BIN
src/static/media/forex.mp4
Normal file
BIN
src/static/media/forex.mp4
Normal file
Binary file not shown.
BIN
src/static/media/forex.webm
Normal file
BIN
src/static/media/forex.webm
Normal file
Binary file not shown.
BIN
src/static/media/led.webm
Normal file
BIN
src/static/media/led.webm
Normal file
Binary file not shown.
|
|
@ -1,10 +1,12 @@
|
|||
// https://google-webfonts-helper.herokuapp.com/fonts/montserrat?subsets=latin
|
||||
// add font-display: swap;
|
||||
|
||||
/* lato-regular - latin */
|
||||
@font-face {
|
||||
font-family: "Lato";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url("../static/fonts/lato/lato-v16-latin-regular.eot"); /* IE9 Compat Modes */
|
||||
src: local("Lato Regular"), local("Lato-Regular"),
|
||||
url("../static/fonts/lato/lato-v16-latin-regular.eot?#iefix")
|
||||
|
|
@ -23,6 +25,7 @@
|
|||
font-family: "Lato";
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url("../static/fonts/lato/lato-v16-latin-italic.eot"); /* IE9 Compat Modes */
|
||||
src: local("Lato Italic"), local("Lato-Italic"),
|
||||
url("../static/fonts/lato/lato-v16-latin-italic.eot?#iefix")
|
||||
|
|
@ -42,6 +45,7 @@
|
|||
font-family: "Montserrat";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url("../static/fonts/montserrat/montserrat-v14-latin-regular.eot"); /* IE9 Compat Modes */
|
||||
src: local("Montserrat Regular"), local("Montserrat-Regular"),
|
||||
url("../static/fonts/montserrat/montserrat-v14-latin-regular.eot?#iefix")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue