Skip to content

Instantly share code, notes, and snippets.

@isaacl
Last active March 9, 2023 23:35
Show Gist options
  • Save isaacl/d25e88efae01d95599972e250f056ac8 to your computer and use it in GitHub Desktop.
Save isaacl/d25e88efae01d95599972e250f056ac8 to your computer and use it in GitHub Desktop.
<template>
<div
@pointerover.once="warmConnections"
@click="isActive = true"
:class="{ activated: isActive }"
:style="{ backgroundImage: `url(${posterUrl})` }"
>
<button v-if="!isActive" type="button" :title="playlabel" />
<iframe
v-else
width="560"
height="315"
frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
:src="`https://www.youtube-nocookie.com/embed/${encodedVideoId}?${paramString}`"
></iframe>
</div>
</template>
<script lang="ts">
import LiteYTEmbedUtils from './LiteYTEmbedUtils';
export default {
name: 'lite-youtube',
props: {
videoid: {
type: String,
required: true,
},
playlabel: {
type: String,
default: 'Play',
},
params: String,
},
data() {
return {
isActive: false,
};
},
computed: {
encodedVideoId() {
return encodeURIComponent(this.videoid);
},
posterUrl() {
return `https://i.ytimg.com/vi/${this.encodedVideoId}/hqdefault.jpg`;
},
paramString() {
const parsedParams = new URLSearchParams(this.params);
parsedParams.set('autoplay', '1');
return parsedParams.toString();
},
},
methods: {
warmConnections() {
LiteYTEmbedUtils.warmConnections();
},
},
created() {
LiteYTEmbedUtils.addPrefetch('preload', this.posterUrl, 'image');
},
};
</script>
<style scoped>
div {
background-color: #000;
position: relative;
display: block;
contain: content;
background-position: center center;
background-size: cover;
cursor: pointer;
max-width: 720px;
}
/* gradient */
div::before {
content: '';
display: block;
position: absolute;
top: 0;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==);
background-position: top;
background-repeat: repeat-x;
height: 60px;
padding-bottom: 50px;
width: 100%;
transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
}
/* responsive iframe with a 16:9 aspect ratio
thanks https://css-tricks.com/responsive-iframes/
*/
div::after {
content: '';
display: block;
padding-bottom: calc(100% / (16 / 9));
}
div > iframe {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
/* play button */
div > button {
width: 68px;
height: 48px;
position: absolute;
transform: translate3d(-50%, -50%, 0);
top: 50%;
left: 50%;
z-index: 1;
background-color: transparent;
/* YT's actual play button svg */
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 68 48"><path fill="%23f00" fill-opacity="0.8" d="M66.52,7.74c-0.78-2.93-2.49-5.41-5.42-6.19C55.79,.13,34,0,34,0S12.21,.13,6.9,1.55 C3.97,2.33,2.27,4.81,1.48,7.74C0.06,13.05,0,24,0,24s0.06,10.95,1.48,16.26c0.78,2.93,2.49,5.41,5.42,6.19 C12.21,47.87,34,48,34,48s21.79-0.13,27.1-1.55c2.93-0.78,4.64-3.26,5.42-6.19C67.94,34.95,68,24,68,24S67.94,13.05,66.52,7.74z"></path><path d="M 45,24 27,14 27,34" fill="%23fff"></path></svg>');
filter: grayscale(100%);
transition: filter 0.1s cubic-bezier(0, 0, 0.2, 1);
border: none;
}
div:hover > button,
div button:focus {
filter: none;
}
/* Post-click styles */
div.activated {
cursor: unset;
}
div.activated::before {
opacity: 0;
pointer-events: none;
}
</style>
export default class LiteYTEmbedUtils {
static preconnected = false;
static addPrefetch(kind: string, url: string, as?: string) {
const linkElem = document.createElement('link');
linkElem.rel = kind;
linkElem.href = url;
if (as) {
linkElem.as = as;
}
document.head.append(linkElem);
}
/**
* 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 (this.preconnected) return;
// The iframe document and most of its subresources come right off youtube.com
this.addPrefetch(
'preconnect',
'https://www.youtube-nocookie.com',
);
// The botguard script is fetched off from google.com
this.addPrefetch('preconnect', 'https://www.google.com');
// Not certain if these ad related domains are in the critical path. Could verify with domain-specific throttling.
this.addPrefetch(
'preconnect',
'https://googleads.g.doubleclick.net',
);
this.addPrefetch(
'preconnect',
'https://static.doubleclick.net',
);
this.preconnected = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment