Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
arahmitz committed Jul 1, 2024
2 parents e6ac805 + 951fc06 commit 152b8a2
Show file tree
Hide file tree
Showing 8 changed files with 359 additions and 7 deletions.
10 changes: 10 additions & 0 deletions assets/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,13 @@ form select {
html {
font-family: ChakraPetch;
}

.litevideo{
min-width: 600px;
}

@media screen and (max-width: 720px) {
.litevideo{
min-width: 400px;
}
}
96 changes: 96 additions & 0 deletions assets/lib/lite-vimeo-embed/lite-vimeo-embed.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
lite-vimeo {
background-color: #000;
position: relative;
display: block;
contain: content;
background-position: center center;
background-size: cover;
cursor: pointer;
max-width: 720px;
min-width: 480px;
}

/* gradient */
lite-vimeo::before {
content: attr(data-title);
display: block;
position: absolute;
top: 0;
/* Pixel-perfect port of YT's gradient PNG, using https://github.com/bluesmoon/pngtocss plus optimizations */
background-image: linear-gradient(180deg, rgb(0 0 0 / 67%) 0%, rgb(0 0 0 / 54%) 14%, rgb(0 0 0 / 15%) 54%, rgb(0 0 0 / 5%) 72%, rgb(0 0 0 / 0%) 94%);
height: 99px;
width: 100%;
font-family: "Youtube Noto",Roboto,Arial,Helvetica,sans-serif;
color: hsl(0deg 0% 93.33%);
text-shadow: 0 0 2px rgba(0,0,0,.5);
font-size: 18px;
padding: 25px 20px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
box-sizing: border-box;
}

lite-vimeo:hover::before {
color: white;
}

/* responsive iframe with a 16:9 aspect ratio
thanks https://css-tricks.com/responsive-iframes/
*/
lite-vimeo::after {
content: "";
display: block;
padding-bottom: calc(100% / (16 / 9));
}
lite-vimeo > iframe {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
border: 0;
}

/* play button */
lite-vimeo > .lty-playbtn {
display: block;
/* Make the button element cover the whole area for a large hover/click target… */
width: 100%;
height: 100%;
/* …but visually it's still the same size */
background: no-repeat center/68px 48px;
/* YT's actual play button svg */
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 48"><path d="M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55c-2.93.78-4.63 3.26-5.42 6.19C.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z" fill="red"/><path d="M45 24 27 14v20" fill="white"/></svg>');
position: absolute;
cursor: pointer;
z-index: 1;
filter: grayscale(100%);
transition: filter .1s cubic-bezier(0, 0, 0.2, 1);
border: 0;
}

lite-vimeo:hover > .lty-playbtn,
lite-vimeo .lty-playbtn:focus {
filter: none;
}

/* Post-click styles */
lite-vimeo.lyt-activated {
cursor: unset;
}
lite-vimeo.lyt-activated::before,
lite-vimeo.lyt-activated > .lty-playbtn {
opacity: 0;
pointer-events: none;
}

.lyt-visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
223 changes: 223 additions & 0 deletions assets/lib/lite-vimeo-embed/lite-vimeo-embed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
const style = document.head.appendChild(document.createElement('style'));
style.textContent = /*css*/`
lite-vimeo {
aspect-ratio: 16 / 9;
background-color: #000;
position: relative;
display: block;
contain: content;
background-position: center center;
background-size: cover;
cursor: pointer;
}
lite-vimeo > iframe {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
border: 0;
}
lite-vimeo > .ltv-playbtn {
font-size: 10px;
padding: 0;
width: 6.5em;
height: 4em;
background: rgba(23, 35, 34, .75);
z-index: 1;
opacity: .8;
border-radius: .5em;
transition: opacity .2s ease-out, background .2s ease-out;
outline: 0;
border: 0;
cursor: pointer;
}
lite-vimeo:hover > .ltv-playbtn {
background-color: rgb(0, 173, 239);
opacity: 1;
}
/* play button triangle */
lite-vimeo > .ltv-playbtn::before {
content: '';
border-style: solid;
border-width: 10px 0 10px 20px;
border-color: transparent transparent transparent #fff;
}
lite-vimeo > .ltv-playbtn,
lite-vimeo > .ltv-playbtn::before {
position: absolute;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
/* Post-click styles */
lite-vimeo.ltv-activated {
cursor: unset;
}
lite-vimeo.ltv-activated::before,
lite-vimeo.ltv-activated > .ltv-playbtn {
opacity: 0;
pointer-events: none;
}
`;

/**
* Ported from https://github.com/paulirish/lite-youtube-embed
*
* A lightweight vimeo embed. Still should feel the same to the user, just MUCH faster to initialize and paint.
*
* Thx to these as the inspiration
* https://storage.googleapis.com/amp-vs-non-amp/youtube-lazy.html
* https://autoplay-youtube-player.glitch.me/
*
* Once built it, I also found these:
* https://github.com/ampproject/amphtml/blob/master/extensions/amp-youtube (👍👍)
* https://github.com/Daugilas/lazyYT
* https://github.com/vb/lazyframe
*/
class LiteVimeo extends (globalThis.HTMLElement ?? class {}) {
/**
* Begin pre-connecting to warm up the iframe load
* Since the embed's network requests load within its iframe,
* preload/prefetch'ing them outside the iframe will only cause double-downloads.
* So, the best we can do is warm up a few connections to origins that are in the critical path.
*
* Maybe `<link rel=preload as=document>` would work, but it's unsupported: http://crbug.com/593267
* But TBH, I don't think it'll happen soon with Site Isolation and split caches adding serious complexity.
*/
static _warmConnections() {
if (LiteVimeo.preconnected) return;
LiteVimeo.preconnected = true;

// The iframe document and most of its subresources come right off player.vimeo.com
addPrefetch('preconnect', 'https://player.vimeo.com');
// Images
addPrefetch('preconnect', 'https://i.vimeocdn.com');
// Files .js, .css
addPrefetch('preconnect', 'https://f.vimeocdn.com');
// Metrics
addPrefetch('preconnect', 'https://fresnel.vimeocdn.com');
}

connectedCallback() {
this.videoId = this.getAttribute('videoid');

/**
* Lo, the vimeo placeholder image! (aka the thumbnail, poster image, etc)
* We have to use the Vimeo API.
*/
let { width, height } = getThumbnailDimensions(this.getBoundingClientRect());
let devicePixelRatio = window.devicePixelRatio || 1;
if (devicePixelRatio >= 2) devicePixelRatio *= .75;
width = Math.round(width * devicePixelRatio);
height = Math.round(height * devicePixelRatio);

fetch(`https://vimeo.com/api/v2/video/${this.videoId}.json`)
.then(response => response.json())
.then(data => {
let thumbnailUrl = data[0].thumbnail_large;
thumbnailUrl = thumbnailUrl.replace(/-d_[\dx]+$/i, `-d_${width}x${height}`);
this.style.backgroundImage = `url("${thumbnailUrl}")`;
});

let playBtnEl = this.querySelector('.ltv-playbtn');
// A label for the button takes priority over a [playlabel] attribute on the custom-element
this.playLabel = (playBtnEl && playBtnEl.textContent.trim()) || this.getAttribute('playlabel') || 'Play video';

if (!playBtnEl) {
playBtnEl = document.createElement('button');
playBtnEl.type = 'button';
playBtnEl.setAttribute('aria-label', this.playLabel);
playBtnEl.classList.add('ltv-playbtn');
this.append(playBtnEl);
}
playBtnEl.removeAttribute('href');

// On hover (or tap), warm up the TCP connections we're (likely) about to use.
this.addEventListener('pointerover', LiteVimeo._warmConnections, {
once: true
});

// Once the user clicks, add the real iframe and drop our play button
// TODO: In the future we could be like amp-youtube and silently swap in the iframe during idle time
// We'd want to only do this for in-viewport or near-viewport ones: https://github.com/ampproject/amphtml/pull/5003
this.addEventListener('click', this.addIframe);
}

addIframe() {
if (this.classList.contains('ltv-activated')) return;
this.classList.add('ltv-activated');

const iframeEl = document.createElement('iframe');
iframeEl.width = 640;
iframeEl.height = 360;
// No encoding necessary as [title] is safe. https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#:~:text=Safe%20HTML%20Attributes%20include
iframeEl.title = this.playLabel;
iframeEl.allow = 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture';
// AFAIK, the encoding here isn't necessary for XSS, but we'll do it only because this is a URL
// https://stackoverflow.com/q/64959723/89484
iframeEl.src = `https://player.vimeo.com/video/${encodeURIComponent(this.videoId)}?autoplay=1`;
this.append(iframeEl);

// Set focus for a11y
iframeEl.addEventListener('load', iframeEl.focus, { once: true });
}
}

if (globalThis.customElements && !globalThis.customElements.get('lite-vimeo')) {
globalThis.customElements.define('lite-vimeo', LiteVimeo);
}

/**
* Add a <link rel={preload | preconnect} ...> to the head
*/
function addPrefetch(kind, url, as) {
const linkElem = document.createElement('link');
linkElem.rel = kind;
linkElem.href = url;
if (as) {
linkElem.as = as;
}
linkElem.crossorigin = true;
document.head.append(linkElem);
}

/**
* Get the thumbnail dimensions to use for a given player size.
*
* @param {Object} options
* @param {number} options.width The width of the player
* @param {number} options.height The height of the player
* @return {Object} The width and height
*/
function getThumbnailDimensions({ width, height }) {
let roundedWidth = width;
let roundedHeight = height;

// If the original width is a multiple of 320 then we should
// not round up. This is to keep the native image dimensions
// so that they match up with the actual frames from the video.
//
// For example 640x360, 960x540, 1280x720, 1920x1080
//
// Round up to nearest 100 px to improve cacheability at the
// CDN. For example, any width between 601 pixels and 699
// pixels will render the thumbnail at 700 pixels width.
if (roundedWidth % 320 !== 0) {
roundedWidth = Math.ceil(width / 100) * 100;
roundedHeight = Math.round((roundedWidth / width) * height);
}

return {
width: roundedWidth,
height: roundedHeight
};
}
13 changes: 6 additions & 7 deletions config/_default/menus.en.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,14 @@
weight = "40"

[[main]]
name = "Contact"
pageRef = "contact"
name = "Talks"
pageRef = "Talks"
weight = "50"

#[[main]]
# name = "Talks"
# pageRef = "Talks"
# weight = "60"

[[main]]
name = "Contact"
pageRef = "contact"
weight = "60"

# -- Footer Menu --
# The footer menu is displayed at the bottom of the page, just before
Expand Down
6 changes: 6 additions & 0 deletions content/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ title: "Welcome to Adam Madej Animations!"
description: "Adam Madej's personal blog about gameplay animation"
---

<!-- Include the custom element script -->
<script type="module" src="https://cdn.jsdelivr.net/npm/lite-vimeo-embed/+esm"></script>

<lite-vimeo class="litevideo" videoid="772649237" style="background-image: url('https://i.vimeocdn.com/video/772649237.webp?mw=1600&mh=900&q=70');">
<div class="ltv-playbtn"></div>
</lite-vimeo>
Binary file added content/talks/featured.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions content/talks/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
+++
title = 'Talks'
languageCode = 'en-us'
draft = false
showAuthor = false
showDate = false
showReadingTime = false
showZenmode = false
sharingLinks = false
heroStyle = "background"
+++

## Animacyjne Dojo (PL)
![Animacyjne Dojo Cover](/images/talks/animacyjnedojo_cover.jpg "[Presentation available here](https://docs.google.com/presentation/d/e/2PACX-1vSADucq-Mkmv9_flTBaHDkf3mogJBdUehcg0T6xx7BUkQURv_w4KIVLAtIQUPArO64mrpnpCvUndw5F/pub?start=false&loop=false&delayms=3000)")

*Animacyjne Dojo* is a presentation I gave for Digital Night Studio at Lodz University of Technology, focusing on gameplay animation. It provides an overview of the fundamental principles from a gameplay animator's perspective,
addresing daily tasks and unique challanges. The presentation covers essential animation exeercises such as *the bouncing ball* and *tails/squirrel*, offering tips and tricks for each. It concludes with resources and guidance for those
looking to learn animation and start their careers in the field. (*Presentation in Polish*)
Binary file added static/images/talks/animacyjnedojo_cover.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 152b8a2

Please sign in to comment.