From a119a959c46fe82278c5f9425b0cc31d2cd3f500 Mon Sep 17 00:00:00 2001 From: Sean Steimer Date: Tue, 7 May 2024 10:14:45 -0700 Subject: [PATCH 1/6] chore: refactor how autiplay interacts with placeholder, especailly for LCP --- blocks/video/video.css | 8 +++- blocks/video/video.js | 83 +++++++++++++++++++++++------------------- 2 files changed, 51 insertions(+), 40 deletions(-) diff --git a/blocks/video/video.css b/blocks/video/video.css index da920429..7040658c 100644 --- a/blocks/video/video.css +++ b/blocks/video/video.css @@ -1,11 +1,10 @@ .video { - width: unset; text-align: center; max-width: 800px; margin: 32px auto; } -.video.lazy-loading { +.video[data-embed-is-loaded="false"]:not(.placeholder) { /* reserve an approximate space to avoid extensive layout shifts */ aspect-ratio: 16 / 9; } @@ -31,6 +30,11 @@ position: relative; } +.video[data-embed-is-loaded="true"] .video-placeholder { + visibility: hidden; + height: 0; +} + .video .video-placeholder > * { display: flex; align-items: center; diff --git a/blocks/video/video.js b/blocks/video/video.js index c9784d42..6fb36f48 100644 --- a/blocks/video/video.js +++ b/blocks/video/video.js @@ -4,17 +4,17 @@ * https://www.hlx.live/developer/block-collection/video */ -function embedYoutube(url, replacePlaceholder, autoplay) { +function embedYoutube(url, autoplay, background) { const usp = new URLSearchParams(url.search); let suffix = ''; - if (replacePlaceholder || autoplay) { + if (background || autoplay) { const suffixParams = { - autoplay: '1', - mute: autoplay ? '1' : '0', - controls: autoplay ? '0' : '1', - disablekb: autoplay ? '1' : '0', - loop: autoplay ? '1' : '0', - playsinline: autoplay ? '1' : '0', + autoplay: autoplay ? '1' : '0', + mute: background ? '1' : '0', + controls: background ? '0' : '1', + disablekb: background ? '1' : '0', + loop: background ? '1' : '0', + playsinline: background ? '1' : '0', }; suffix = `&${Object.entries(suffixParams).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&')}`; } @@ -32,13 +32,13 @@ function embedYoutube(url, replacePlaceholder, autoplay) { return temp.children.item(0); } -function embedVimeo(url, replacePlaceholder, autoplay) { +function embedVimeo(url, autoplay, background) { const [, video] = url.pathname.split('/'); let suffix = ''; - if (replacePlaceholder || autoplay) { + if (background || autoplay) { const suffixParams = { - autoplay: '1', - background: autoplay ? '1' : '0', + autoplay: autoplay ? '1' : '0', + background: background ? '1' : '0', }; suffix = `?${Object.entries(suffixParams).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&')}`; } @@ -52,14 +52,13 @@ function embedVimeo(url, replacePlaceholder, autoplay) { return temp.children.item(0); } -function getVideoElement(source, replacePlaceholder, autoplay) { +function getVideoElement(source, autoplay, background) { const video = document.createElement('video'); video.setAttribute('controls', ''); video.dataset.loading = 'true'; video.addEventListener('loadedmetadata', () => delete video.dataset.loading); - if (autoplay || replacePlaceholder) { - video.setAttribute('autoplay', ''); - if (autoplay) { + if (autoplay) video.setAttribute('autoplay', ''); + if (background) { video.setAttribute('loop', ''); video.setAttribute('playsinline', ''); video.removeAttribute('controls'); @@ -67,7 +66,6 @@ function getVideoElement(source, replacePlaceholder, autoplay) { video.muted = true; video.play(); }); - } } const sourceEl = document.createElement('source'); @@ -78,50 +76,59 @@ function getVideoElement(source, replacePlaceholder, autoplay) { return video; } -const loadVideoEmbed = (block, link, replacePlaceholder, autoplay) => { - if (block.dataset.embedIsLoaded) { + +const loadVideoEmbed = (block, link, autoplay, background) => { + if (block.dataset.embedIsLoaded === 'true') { return; } const url = new URL(link); const isYoutube = link.includes('youtube') || link.includes('youtu.be'); const isVimeo = link.includes('vimeo'); - const isMp4 = link.includes('.mp4'); - let embedEl; if (isYoutube) { - embedEl = embedYoutube(url, replacePlaceholder, autoplay); + block.append(embedYoutube(url, autoplay, background)); + block.dataset.embedIsLoaded = true; } else if (isVimeo) { - embedEl = embedVimeo(url, replacePlaceholder, autoplay); - } else if (isMp4) { - embedEl = getVideoElement(link, replacePlaceholder, autoplay); + block.append(embedVimeo(url, autoplay, background)); + block.dataset.embedIsLoaded = true; + } else { + const videoEl = getVideoElement(link, autoplay, background); + block.append(videoEl); + videoEl.addEventListener('canplay', () => { + block.dataset.embedIsLoaded = true; + }); } - block.replaceChildren(embedEl); - - block.dataset.embedIsLoaded = true; }; export default async function decorate(block) { const placeholder = block.querySelector('picture'); const link = block.querySelector('a').href; block.textContent = ''; + block.dataset.embedIsLoaded = false; - if (placeholder) { + const autoplay = block.classList.contains('autoplay'); + if (!!placeholder) { + block.classList.add('placeholder'); const wrapper = document.createElement('div'); wrapper.className = 'video-placeholder'; - wrapper.innerHTML = '
'; - wrapper.prepend(placeholder); - wrapper.addEventListener('click', () => { - loadVideoEmbed(block, link, true, false); - }); + wrapper.append(placeholder); + + if (!autoplay) { + wrapper.insertAdjacentHTML('beforeend', + '
'); + wrapper.addEventListener('click', () => { + loadVideoEmbed(block, link, true, false); + }); + } block.append(wrapper); - } else { - block.classList.add('lazy-loading'); + } + + if (!placeholder || (autoplay)) { const observer = new IntersectionObserver((entries) => { if (entries.some((e) => e.isIntersecting)) { observer.disconnect(); - loadVideoEmbed(block, link, false, block.classList.contains('autoplay')); - block.classList.remove('lazy-loading'); + loadVideoEmbed(block, link, !!placeholder || autoplay, autoplay); } }); observer.observe(block); From 9b5613ccf496a71e883c15711e8faa3a6ec859e2 Mon Sep 17 00:00:00 2001 From: Sean Steimer Date: Tue, 7 May 2024 10:23:34 -0700 Subject: [PATCH 2/6] minor code refactoring of video --- blocks/video/video.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/blocks/video/video.js b/blocks/video/video.js index 6fb36f48..da7fbe3b 100644 --- a/blocks/video/video.js +++ b/blocks/video/video.js @@ -59,13 +59,13 @@ function getVideoElement(source, autoplay, background) { video.addEventListener('loadedmetadata', () => delete video.dataset.loading); if (autoplay) video.setAttribute('autoplay', ''); if (background) { - video.setAttribute('loop', ''); - video.setAttribute('playsinline', ''); - video.removeAttribute('controls'); - video.addEventListener('canplay', () => { - video.muted = true; - video.play(); - }); + video.setAttribute('loop', ''); + video.setAttribute('playsinline', ''); + video.removeAttribute('controls'); + video.addEventListener('canplay', () => { + video.muted = true; + video.play(); + }); } const sourceEl = document.createElement('source'); @@ -76,7 +76,6 @@ function getVideoElement(source, autoplay, background) { return video; } - const loadVideoEmbed = (block, link, autoplay, background) => { if (block.dataset.embedIsLoaded === 'true') { return; From 9639d96c62daf49fc5347074920f9dfede569594 Mon Sep 17 00:00:00 2001 From: Sean Steimer Date: Tue, 7 May 2024 10:33:02 -0700 Subject: [PATCH 3/6] chore: change video loading sequence --- blocks/video/video.css | 10 +++------- blocks/video/video.js | 2 -- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/blocks/video/video.css b/blocks/video/video.css index 7040658c..1dd7a4a2 100644 --- a/blocks/video/video.css +++ b/blocks/video/video.css @@ -18,21 +18,17 @@ max-width: 100%; } -.video video[data-loading] { - /* reserve an approximate space to avoid extensive layout shifts */ - width: 100%; - aspect-ratio: 16 / 9; -} - .video .video-placeholder { width: 100%; aspect-ratio: 16 / 9; position: relative; } -.video[data-embed-is-loaded="true"] .video-placeholder { +.video[data-embed-is-loaded="true"] .video-placeholder, +.video[data-embed-is-loaded="false"] .video-placeholder + * { visibility: hidden; height: 0; + width: 0; } .video .video-placeholder > * { diff --git a/blocks/video/video.js b/blocks/video/video.js index da7fbe3b..5a249a91 100644 --- a/blocks/video/video.js +++ b/blocks/video/video.js @@ -55,8 +55,6 @@ function embedVimeo(url, autoplay, background) { function getVideoElement(source, autoplay, background) { const video = document.createElement('video'); video.setAttribute('controls', ''); - video.dataset.loading = 'true'; - video.addEventListener('loadedmetadata', () => delete video.dataset.loading); if (autoplay) video.setAttribute('autoplay', ''); if (background) { video.setAttribute('loop', ''); From 3144dbf95969fce56947b8730cd4fd4bf17d0b28 Mon Sep 17 00:00:00 2001 From: Sean Steimer Date: Tue, 7 May 2024 10:56:38 -0700 Subject: [PATCH 4/6] fix: lint --- blocks/video/video.css | 14 +++++++------- blocks/video/video.js | 8 +++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/blocks/video/video.css b/blocks/video/video.css index 1dd7a4a2..055dc76c 100644 --- a/blocks/video/video.css +++ b/blocks/video/video.css @@ -24,13 +24,6 @@ position: relative; } -.video[data-embed-is-loaded="true"] .video-placeholder, -.video[data-embed-is-loaded="false"] .video-placeholder + * { - visibility: hidden; - height: 0; - width: 0; -} - .video .video-placeholder > * { display: flex; align-items: center; @@ -39,6 +32,13 @@ inset: 0; } +.video[data-embed-is-loaded="true"] .video-placeholder, +.video[data-embed-is-loaded="false"] .video-placeholder + * { + visibility: hidden; + height: 0; + width: 0; +} + .video .video-placeholder picture img { width: 100%; height: 100%; diff --git a/blocks/video/video.js b/blocks/video/video.js index 5a249a91..4e3d1475 100644 --- a/blocks/video/video.js +++ b/blocks/video/video.js @@ -105,15 +105,17 @@ export default async function decorate(block) { block.dataset.embedIsLoaded = false; const autoplay = block.classList.contains('autoplay'); - if (!!placeholder) { + if (placeholder) { block.classList.add('placeholder'); const wrapper = document.createElement('div'); wrapper.className = 'video-placeholder'; wrapper.append(placeholder); if (!autoplay) { - wrapper.insertAdjacentHTML('beforeend', - '
'); + wrapper.insertAdjacentHTML( + 'beforeend', + '
', + ); wrapper.addEventListener('click', () => { loadVideoEmbed(block, link, true, false); }); From 6b650dcd489ca6cee4999dba4eefd19dc06f9076 Mon Sep 17 00:00:00 2001 From: Sean Steimer Date: Tue, 7 May 2024 11:38:07 -0700 Subject: [PATCH 5/6] feat: use prefers redueced motion to control the autoplay --- blocks/video/video.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/blocks/video/video.js b/blocks/video/video.js index 4e3d1475..20fc3d42 100644 --- a/blocks/video/video.js +++ b/blocks/video/video.js @@ -4,6 +4,8 @@ * https://www.hlx.live/developer/block-collection/video */ +const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)'); + function embedYoutube(url, autoplay, background) { const usp = new URLSearchParams(url.search); let suffix = ''; @@ -62,7 +64,7 @@ function getVideoElement(source, autoplay, background) { video.removeAttribute('controls'); video.addEventListener('canplay', () => { video.muted = true; - video.play(); + if (autoplay) video.play(); }); } @@ -123,11 +125,12 @@ export default async function decorate(block) { block.append(wrapper); } - if (!placeholder || (autoplay)) { + if (!placeholder || autoplay) { const observer = new IntersectionObserver((entries) => { if (entries.some((e) => e.isIntersecting)) { observer.disconnect(); - loadVideoEmbed(block, link, !!placeholder || autoplay, autoplay); + const playOnLoad = autoplay && !prefersReducedMotion.matches; + loadVideoEmbed(block, link, playOnLoad, autoplay); } }); observer.observe(block); From 765cb8158c2ffd970963dff032dd486592d6aa84 Mon Sep 17 00:00:00 2001 From: Sean Steimer Date: Tue, 7 May 2024 13:17:21 -0700 Subject: [PATCH 6/6] chore: change video embed loaded to trigger on iframe load event --- blocks/video/video.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/blocks/video/video.js b/blocks/video/video.js index 20fc3d42..f1390836 100644 --- a/blocks/video/video.js +++ b/blocks/video/video.js @@ -86,11 +86,17 @@ const loadVideoEmbed = (block, link, autoplay, background) => { const isVimeo = link.includes('vimeo'); if (isYoutube) { - block.append(embedYoutube(url, autoplay, background)); - block.dataset.embedIsLoaded = true; + const embedWrapper = embedYoutube(url, autoplay, background); + block.append(embedWrapper); + embedWrapper.querySelector('iframe').addEventListener('load', () => { + block.dataset.embedIsLoaded = true; + }); } else if (isVimeo) { - block.append(embedVimeo(url, autoplay, background)); - block.dataset.embedIsLoaded = true; + const embedWrapper = embedVimeo(url, autoplay, background); + block.append(embedWrapper); + embedWrapper.querySelector('iframe').addEventListener('load', () => { + block.dataset.embedIsLoaded = true; + }); } else { const videoEl = getVideoElement(link, autoplay, background); block.append(videoEl);