Optimizations

This commit is contained in:
Schmelczer András 2019-12-29 17:13:20 +01:00
parent 10015a4ebe
commit 3febd3fca8
24 changed files with 215 additions and 99 deletions

View file

@ -2,18 +2,22 @@
<dictionary name="Schme">
<words>
<w>contenthash</w>
<w>cybersecurity</w>
<w>ffffff</w>
<w>gifsicle</w>
<w>imagemin</w>
<w>jpegtran</w>
<w>lato</w>
<w>markwon</w>
<w>mozjpeg</w>
<w>noquotes</w>
<w>opacify</w>
<w>optipng</w>
<w>pngquant</w>
<w>raleway</w>
<w>screenshot</w>
<w>transparentize</w>
<w>webm</w>
<w>webp</w>
</words>
</dictionary>

28
.idea/workspace.xml generated
View file

@ -2,28 +2,30 @@
<project version="4">
<component name="ChangeListManager">
<list default="true" id="8edc47ab-1265-4111-9771-536b24cc9310" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/src/static/media/forex.mp4" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/static/media/forex.webm" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/static/media/led.mp4" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/static/media/led.webm" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/dictionaries/Schme.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dictionaries/Schme.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/custom.d.ts" beforeDir="false" afterPath="$PROJECT_DIR$/custom.d.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/src/index.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/about/about.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/about/about.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/background/background.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/background/background.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/background/background.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/background/background.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/background/blob.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/background/blob.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/framework/helper.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/framework/helper.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/model/content.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/model/content.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/model/misc.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/model/misc.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/model/portfolio.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/model/portfolio.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/about/about.html.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/about/about.html.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/about/about.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/about/about.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/content/content.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/content/content.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/content/content.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/content/content.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/footer/footer.html.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/footer/footer.html.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/footer/footer.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/footer/footer.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/image-viewer/image-viewer.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/image-viewer/image-viewer.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/timeline/timeline-element/timeline-element.html.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/timeline/timeline-element/timeline-element.html.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/timeline/timeline-element/timeline-element.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/timeline/timeline-element/timeline-element.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/timeline/timeline-element/timeline-element.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/timeline/timeline-element/timeline-element.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/page/timeline/timeline.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/timeline/timeline.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/style/a.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/style/a.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/portfolio.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/portfolio.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/static/media/led720.mp4" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/src/style/fonts.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/style/fonts.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/style/mixins.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/style/mixins.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/style/vars.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/style/vars.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/styles.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/styles.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/webpack.config.js" beforeDir="false" afterPath="$PROJECT_DIR$/webpack.config.js" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
@ -120,7 +122,7 @@
<workItem from="1577348003642" duration="4876000" />
<workItem from="1577451432189" duration="11000" />
<workItem from="1577469180612" duration="18000" />
<workItem from="1577526744211" duration="38046000" />
<workItem from="1577526744211" duration="44201000" />
</task>
<servers />
</component>

27
custom.d.ts vendored
View file

@ -1,34 +1,47 @@
declare module "*.svg" {
const content: string;
import { url } from "src/model/misc";
const content: url;
export default content;
}
declare module "*.png" {
const content: string;
import { ResponsiveImage } from "src/model/misc";
const content: ResponsiveImage;
export default content;
}
declare module "*.jpg" {
const content: string;
import { ResponsiveImage } from "src/model/misc";
const content: ResponsiveImage;
export default content;
}
declare module "*.jpeg" {
const content: string;
import { ResponsiveImage } from "src/model/misc";
const content: ResponsiveImage;
export default content;
}
declare module "*.gif" {
const content: string;
import { url } from "src/model/misc";
const content: url;
export default content;
}
declare module "*.mp4" {
const content: string;
import { url } from "src/model/misc";
const content: url;
export default content;
}
declare module "*.webm" {
import { url } from "src/model/misc";
const content: url;
export default content;
}
declare module "*.pdf" {
const content: string;
import { url } from "src/model/misc";
const content: url;
export default content;
}

View file

@ -4,7 +4,6 @@
"description": "An easily configurable portfolio.",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --mode development",
"build": "webpack"
},
@ -35,7 +34,9 @@
"optimize-css-assets-webpack-plugin": "^5.0.3",
"prettier": "^1.19.1",
"resolve-url-loader": "^3.1.1",
"responsive-loader": "^1.2.0",
"sass-loader": "^8.0.0",
"sharp": "^0.23.4",
"style-loader": "^1.0.2",
"svg-url-loader": "^3.0.3",
"terser-webpack-plugin": "^2.3.1",

View file

@ -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;

View file

@ -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>;

View file

@ -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;
}>;
};

View file

@ -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;
}

View file

@ -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>`;

View file

@ -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]);

View file

@ -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;
}
}

View file

@ -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>
`)

View file

@ -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>

View file

@ -43,7 +43,7 @@ footer#page-footer {
h6 {
@include insignificant-font();
opacity: 0.4;
opacity: 0.6;
}
}
}

View file

@ -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);

View file

@ -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

View file

@ -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;
}

View file

@ -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

Binary file not shown.

BIN
src/static/media/forex.webm Normal file

Binary file not shown.

BIN
src/static/media/led.webm Normal file

Binary file not shown.

View file

@ -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")

View file

@ -35,7 +35,17 @@ module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|mp4)$/i,
test: /\.(jpe?g|png)$/i,
loader: "responsive-loader",
options: {
adapter: require("responsive-loader/sharp"),
outputPath: "static/",
sizes: [300, 600, 1200, 2000],
placeholder: false
}
},
{
test: /\.(webm|mp4)$/i,
use: [
{
loader: "file-loader",
@ -49,7 +59,7 @@ module.exports = {
disable: !isProduction,
mozjpeg: {
progressive: true,
quality: 65
quality: 45
},
optipng: {
enabled: true
@ -61,9 +71,8 @@ module.exports = {
gifsicle: {
interlaced: false
},
// the webp option will enable WEBP
webp: {
quality: 75
quality: 65
}
}
}