Skip to content

Instantly share code, notes, and snippets.

@ryandejaegher
Last active January 10, 2024 19:45
Show Gist options
  • Select an option

  • Save ryandejaegher/e3f4b406d50140d3d2e2d2065e9cf52f to your computer and use it in GitHub Desktop.

Select an option

Save ryandejaegher/e3f4b406d50140d3d2e2d2065e9cf52f to your computer and use it in GitHub Desktop.
Knockout Video #web-component #squarespace
{
"scripts": [],
"styles": []
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
Yes
<video-text
video-src="https://static1.squarespace.com/static/5ff1ec940400f40890097046/t/602aa4aeef4fdf139f943ab1/1613407407624/Girl+Surfer+Observes+the+Surfers+in+the+Sea.mp4"
>
<h2 slot="text">SURF SURF</h2>
<h2 slot="text">WAVES</h2>
<h2 slot="text">ALL DAY</h2>
</video-text>
<div class="container"></div>
<div class="big-block"></div>
<!--Based off https://codepen.io/jonathanedempsey/pen/YzyRaKj-->
<div class="block">
<div class="overlay">
<video
src="https://static1.squarespace.com/static/5ff1ec940400f40890097046/t/602aa4aeef4fdf139f943ab1/1613407407624/Girl+Surfer+Observes+the+Surfers+in+the+Sea.mp4"
autoplay
></video>
</div>
<div class="text-container">
<h1>SURF LIFE</h1>
</div>
</div>
</body>
</html>
// TODO Add Intersection Observer for video so it only loads when necessary
// TODO If video is in view load and play
// TODO If video is out of view pause
// TODO What if the video doesn't play on mobile?
// TODO Add slot for text
// TODO Add att
// TODO Add ability to customize overlay color
// TODO Add ability to remove overlay
// TODO Add ability to add border
// TODO Add ability to
(function () {
// Create the template for the component
const template = document.createElement('template');
template.innerHTML = `
<style>
:host {
display: block;
height: 100%;
}
:host([invert]) ::slotted(*) {
color: white;
}
:host([invert]) .text-container {
background: black;
mix-blend-mode:multiply;
}
.block {
height: 100%;
position: relative;
}
.video-overlay {
width: 100%;
height: 100%;
overflow: hidden;
// background: hsla(200,80%,50%,1);
}
.text-container {
position: absolute;
background: white;
width:100%;
height: 100%;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
mix-blend-mode: screen;
overflow: hidden;
}
::slotted(*) {
margin: 0;
color: black;
z-index: 2;
// border: 8px solid black;
// padding: 40px;
// border-radius: 4px;
}
video {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
// mix-blend-mode: overlay;
}
</style>
<div class="block">
<div class="video-overlay">
<video data-src autoplay loop playsinline></video>
</div>
<div class="text-container">
<slot name="text"></slot>
</div>
</div>
`
class VideoText extends HTMLElement {
// Always call super first in constrcutor
constructor() {
super();
// Create shadow root
this.attachShadow({
mode: 'open'
});
// Insert the template into the shadow Root/DOM
this.shadowRoot.append(template.content.cloneNode(true));
this._video = this.shadowRoot.querySelector('video');
this._text = this.shadowRoot.querySelector('.text-container');
// this.observerCallback = this.observerCallback.bind(this);
this.observer = new IntersectionObserver(this.observerCallback, {
rootMargin: "0px 0px 0px 0px",
threshold:[0,0.25,.4,.5]
})
}
loadVideo() {
if(this._video.src === '') {
this._video.src = this._video.dataset.src
console.log(this._video.src);
}
}
pauseVideo() {
this._video.pause()
console.log('video paused')
}
playVideo() {
this._video.play()
console.log('video play')
}
fadeIn() {
this._video.animate([{
opacity: 1
}], {
duration: 700,
fill: 'forwards'
})
console.log('fade in')
}
fadeOut() {
this._video.animate([{
opacity: 0
}], {
duration: 700,
fill: 'forwards'
})
console.log('fade out')
}
observerCallback = (entries) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
this.loadVideo();
console.log(entry.intersectionRatio)
}
if(entry.intersectionRatio > 0.1) {
this.playVideo()
} else {
this.pauseVideo()
}
if(entry.intersectionRatio >= 0.5) {
this.fadeIn();
} else {
this.fadeOut();
}
})
}
get text() {
return this.getAttribute('text')
}
set text(newValue) {
this.setAttribute('text', newValue)
}
get video() {
return this.getAttribute('video-src');
}
set video(newValue) {
this.setAttribute('video-src', newValue)
}
// Web Component lifecycle callbacks
// Runs as soon as the component is used in document
connectedCallback() {
console.log('I have been added to the document')
this._video.dataset.src = this.video;
this.observer.observe(this);
}
// Runs as soon as the component is removed or disconnected from the document
disconnectedCallback() {
console.log('I have been removed')
}
// Runs when the component is moved within the document
adoptedCallback() {
console.log('I have moved')
}
// Runs when one of the documents observed elements is changed in some way
attributeChangedCallback(name, oldValue, newValue) {
console.log('My attributes have changed')
console.log(`${name} changed from ${oldValue} to ${newValue}`)
}
// You need to list the attributes you want to observe in order for the attributeChangedCallback to work
// This should return an array containing the names of the attributes you want to observe
static get observedAttributes() {
return ['video-src']
}
}
window.customElements.define('video-text', VideoText);
})();
*,
*:before,
*:after {
box-sizing: border-box;
}
html,body {
width: 100%;
height: 100%;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
.big-block {
height: 100vh;
}
.flex {
display: flex;
}
.flex-col {
flex-direction: column;
}
.block {
height: 100%;
position: relative;
}
.overlay {
width: 100%;
height: 100%;
overflow: hidden;
background: hsla(200,80%,50%,1);
}
.text-container {
position: absolute;
background: white;
width:100%;
height: 100%;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
mix-blend-mode: screen;
overflow: hidden;
}
h1 {
margin: 0;
font-weight: 800;
font-style: italic;
font-size: 15vw;
color: black;
border: 16px solid black;
padding: 40px;
border-radius: 8px;
}
h2 {
font-weight: 900;
font-style: italic;
font-size: 160px;
margin: 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment