From f3c8453782ee69628bddef5b7c5e37947b1a3073 Mon Sep 17 00:00:00 2001 From: schmelczerandras Date: Fri, 27 Nov 2020 12:17:47 +0100 Subject: [PATCH] Improve video UX --- src/data/ad-astra.ts | 7 ++--- src/data/city-simulation.ts | 7 ++--- src/data/forex.ts | 9 ++++-- src/data/leds.ts | 7 ++--- src/page/background/background.scss | 1 + src/page/basics/image/image.html.ts | 8 ++++- src/page/basics/preview/preview.html.ts | 4 +-- src/page/basics/preview/preview.scss | 23 ++------------- src/page/basics/preview/preview.ts | 2 +- src/page/basics/video/video.html.ts | 31 +++++++------------- src/page/basics/video/video.scss | 1 + src/page/basics/video/video.ts | 39 ++++++++++++++++++++----- src/styles.scss | 31 ++++++++++++++++++-- 13 files changed, 101 insertions(+), 69 deletions(-) diff --git a/src/data/ad-astra.ts b/src/data/ad-astra.ts index 87eea44..a6982fe 100644 --- a/src/data/ad-astra.ts +++ b/src/data/ad-astra.ts @@ -1,16 +1,15 @@ import adAstraPoster from '../static/media/ad_astra.jpg?format=jpg'; -import adAstraMp4 from '../static/media/ad_astra_720.mp4'; -import adAstraWebM from '../static/media/ad_astra_720.webm'; +import adAstraMp4 from '../static/media/mp4/ad_astra.mp4'; +import adAstraWebM from '../static/media/webm/ad_astra.webm'; import { GitHub } from './shared'; -import { last } from '../helper/last'; import { Video } from '../page/basics/video/video'; export const adAstraTimelineElement = { title: `Gaming on an ATtiny85`, date: `2020 Spring`, figure: new Video({ - poster: last(adAstraPoster.images)!.path, + poster: adAstraPoster, mp4: adAstraMp4, webm: adAstraWebM, }), diff --git a/src/data/city-simulation.ts b/src/data/city-simulation.ts index 3cb0377..ce5e8ca 100644 --- a/src/data/city-simulation.ts +++ b/src/data/city-simulation.ts @@ -1,15 +1,14 @@ import citySimulationPoster from '../static/media/simulation.jpg?format=jpg'; -import citySimulationWebM from '../static/media/simulation.webm'; -import citySimulationMp4 from '../static/media/simulation.mp4'; +import citySimulationMp4 from '../static/media/mp4/simulation.mp4'; +import citySimulationWebM from '../static/media/webm/simulation.webm'; import { Video } from '../page/basics/video/video'; -import { last } from '../helper/last'; export const citySimulationTimelineElement = { date: `2018 July - August`, title: `City simulation`, figure: new Video({ - poster: last(citySimulationPoster.images)!.path, + poster: citySimulationPoster, mp4: citySimulationMp4, webm: citySimulationWebM, }), diff --git a/src/data/forex.ts b/src/data/forex.ts index ee06a88..d0d0a7c 100644 --- a/src/data/forex.ts +++ b/src/data/forex.ts @@ -1,14 +1,17 @@ -import forexMp4 from '../static/media/forex.mp4'; -import forexWebM from '../static/media/forex.webm'; +import forexMp4 from '../static/media/mp4/forex.mp4'; +import forexWebM from '../static/media/webm/forex.webm'; +import forexPoster from '../static/media/forex.jpg'; + import { Video } from '../page/basics/video/video'; export const forexTimelineElement = { title: `Predicting foreign exchange rates`, date: `2019 Autumn`, figure: new Video({ + poster: forexPoster, mp4: forexMp4, webm: forexWebM, - shouldActLikeGif: true, + invertButton: true, }), description: ` From the animation, we can see that my implementation does a somewhat acceptable job at diff --git a/src/data/leds.ts b/src/data/leds.ts index 0c482f2..a7e2972 100644 --- a/src/data/leds.ts +++ b/src/data/leds.ts @@ -1,15 +1,14 @@ import ledPoster from '../static/media/led.jpg?format=jpg'; -import ledMp4 from '../static/media/led.mp4'; -import ledWebM from '../static/media/led.webm'; +import ledMp4 from '../static/media/mp4/led.mp4'; +import ledWebM from '../static/media/webm/led.webm'; -import { last } from '../helper/last'; import { Video } from '../page/basics/video/video'; export const ledsTimelineElement = { date: `2016 spring`, title: `Lights synchronised to music`, figure: new Video({ - poster: last(ledPoster.images)!.path, + poster: ledPoster, mp4: ledMp4, webm: ledWebM, }), diff --git a/src/page/background/background.scss b/src/page/background/background.scss index e92879e..82105be 100644 --- a/src/page/background/background.scss +++ b/src/page/background/background.scss @@ -7,6 +7,7 @@ top: 0; border-radius: 1000px; transition: background-color var(--transition-time); + transform: translateX(-100%); &:nth-child(odd) { background-color: #fff9e0; diff --git a/src/page/basics/image/image.html.ts b/src/page/basics/image/image.html.ts index 09fab71..09590fc 100644 --- a/src/page/basics/image/image.html.ts +++ b/src/page/basics/image/image.html.ts @@ -16,7 +16,13 @@ export const generate = ({ alt: string; container: boolean; }): html => ` - ${container ? `
` : ''} + ${ + container + ? `
` + : '' + } `
${loading}
- -
${play}
+ +
${play}
`; diff --git a/src/page/basics/preview/preview.scss b/src/page/basics/preview/preview.scss index 1d2b6a0..868324c 100644 --- a/src/page/basics/preview/preview.scss +++ b/src/page/basics/preview/preview.scss @@ -9,27 +9,9 @@ left: 0; top: 0; - * { + iframe { position: absolute; left: 0; - right: 0; - } - - .load-button { - @include image-button(var(--large-icon-size)); - @include absolute-center; - @include square(calc(var(--large-icon-size) + var(--normal-margin) * 2)); - - &:hover svg { - box-shadow: var(--shadow); - } - - svg { - border-radius: 10000px; - backdrop-filter: blur(16px); - transition: transform var(--transition-time), box-shadow var(--transition-time); - stroke: var(--normal-text-color); - } } .loading { @@ -54,9 +36,10 @@ &.loaded { .figure-container, - .load-button { + .start-button { visibility: hidden; } + .loading { visibility: visible; } diff --git a/src/page/basics/preview/preview.ts b/src/page/basics/preview/preview.ts index 36960e9..26abdcd 100644 --- a/src/page/basics/preview/preview.ts +++ b/src/page/basics/preview/preview.ts @@ -14,7 +14,7 @@ export class Preview extends PageElement { super(createElement(generate({ alt }))); this.url += '?portfolioView'; this.attachElementByReplacing('.poster', new Image(posterWebP, posterJpeg, alt)); - this.query('.load-button').addEventListener('click', this.loadContent.bind(this)); + this.query('.start-button').addEventListener('click', this.loadContent.bind(this)); } public setParent(parent: PageElement) { diff --git a/src/page/basics/video/video.html.ts b/src/page/basics/video/video.html.ts index d691a26..c254041 100644 --- a/src/page/basics/video/video.html.ts +++ b/src/page/basics/video/video.html.ts @@ -1,26 +1,17 @@ import './video.scss'; -import { url } from '../../../types/url'; -import { html } from '../../../types/html'; -export const generate = ({ - webm, - mp4, - poster, - shouldActLikeGif, - container, -}: { - webm: url; - mp4: url; - poster?: url; - shouldActLikeGif?: boolean; - container?: boolean; -}): html => ` - ${container === undefined || container ? `
` : ''} -
`; diff --git a/src/page/basics/video/video.scss b/src/page/basics/video/video.scss index e69de29..dba155f 100644 --- a/src/page/basics/video/video.scss +++ b/src/page/basics/video/video.scss @@ -0,0 +1 @@ +@use '../../../style/mixins' as *; diff --git a/src/page/basics/video/video.ts b/src/page/basics/video/video.ts index ec94334..52551dd 100644 --- a/src/page/basics/video/video.ts +++ b/src/page/basics/video/video.ts @@ -2,15 +2,40 @@ import { PageElement } from '../../page-element'; import { createElement } from '../../../helper/create-element'; import { generate } from './video.html'; import { url } from '../../../types/url'; +import { ResponsiveImage } from '../../../types/responsive-image'; + +export interface VideoParameters { + mp4: url; + webm: url; + poster: ResponsiveImage; + invertButton?: boolean; +} export class Video extends PageElement { - public constructor(options: { - poster?: url; - mp4: url; - webm: url; - shouldActLikeGif?: boolean; - container?: boolean; - }) { + private video: HTMLVideoElement; + + public constructor(options: VideoParameters) { super(createElement(generate(options))); + this.video = this.query('video') as HTMLVideoElement; + this.video.addEventListener('click', this.startVideo.bind(this)); + this.query('.start-button').addEventListener('click', this.startVideo.bind(this)); + this.video.addEventListener('pause', this.stopVideo.bind(this)); + } + + private startVideo(e: Event) { + if (this.video.paused) { + this.query('.start-button').style.visibility = 'hidden'; + this.video.play(); + this.video.controls = true; + e.preventDefault(); + } + } + + private stopVideo(e: Event) { + if (this.video.paused) { + this.query('.start-button').style.visibility = 'visible'; + this.video.controls = false; + e.preventDefault(); + } } } diff --git a/src/styles.scss b/src/styles.scss index 617588b..0fed989 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -51,6 +51,26 @@ noscript { @include center-children(); } +.start-button { + @include image-button(var(--large-icon-size)); + @include absolute-center; + @include square(calc(var(--large-icon-size) + var(--normal-margin) * 2)); + + &:hover svg { + box-shadow: var(--shadow); + } + + svg { + border-radius: 10000px; + backdrop-filter: blur(16px); + transition: transform var(--transition-time), box-shadow var(--transition-time); + } + + &.inverted svg { + fill: var(--accent-color); + } +} + .figure-container { font-size: 0; box-shadow: var(--inset-shadow); @@ -59,14 +79,19 @@ noscript { cursor: pointer; position: relative; + * { + pointer-events: auto; + } + img, video, iframe { - pointer-events: auto; - position: relative; z-index: -1; + position: absolute; + top: 0; + left: 0; + height: 100%; width: 100%; - height: auto; } }