Problem: I am building 3 a column layout, where some columns must stay sticky to the top of the viewport. There is a section where I want to place the same video as background for all 3 columns and I want to achieve following effect: Leave middle column 'sticky-top' and allow the left and right column to scroll up.
I already tried a solution with <Canvas>, but there are a couple of problems. Canvases can not render frames without a blurry effect and it doesn't look good in webkit browsers (for firefox it seems to work better). Here is *(in case jsFiddle doesn't play video correctly, probably you need to insert Bootstrap's CDN links) a sample solution I am aiming for. I have tried to insert videos in the left and right column and adjust margins so that all three videos give feeling if they were one whole, but there I have another problem. On first run all videos play almost synchronously, but after while they flow out from sync, which looses effect of wholeness. Here is Solution with 3 background videos.
```
<div class="row g-0">
<div class="o-cont-m o-col-3-c container-left">
<canvas id="canvas-left" class="o-cont-m"></canvas>
</div>
<div class="o-cont-m o-col-3-c container-middle sticky-top">
<video id="video-bg-middle" loop muted autoplay playsinline>
<!-- <source src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" type="video/mp4"> -->
<source src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4" type="video/mp4">
<!-- <source src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4" type="video/mp4"> -->
<!-- <source src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4" type="video/mp4"> -->
<!-- <source src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4" type="video/mp4"> -->
<!-- <source src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4" type="video/mp4"> -->
Your browser does not support the video tag.s
</video>
</div>
<div class="o-cont-m o-col-4-c container-right">
<canvas id="canvas-right" class="o-cont-m"></canvas>
</div>
<div class="o-cont-m o-col-3-c" style="background-color:grey"></div>
<div class="o-cont-m o-col-3-c col-transparent sticky-top"></div>
<div class="o-cont-m o-col-4-c" style="background-color:grey"></div>
<div class="o-cont-m o-col-3-c" style="background-color:tomato"></div>
<div class="o-cont-m o-col-3-c col-transparent sticky-top" ></div>
<div class="o-cont-m o-col-4-c" style="background-color:tomato
"></div>
</div>
```
.o-col-3-c {
flex:0 0 auto;
width:30%;
}
.o-col-4-c {
flex:0 0 auto;
width:40%;
}
.o-cont-m {
block-size:100vh;
}
.col-transparent{
background: rgba(0, 0, 0, .5);
}
video{
width:100vw;
height:100%;
display:block;
object-fit:fill;
}
.container-left,
.container-middle,
.container-right {
overflow:hidden;
}
#canvas-left,
#canvas-right {
width: 100vw;
height: 100vh;
display: block;
}
```
const middleCol = document.querySelector('.container-middle');
const middleColOffsetLeft = middleCol.offsetLeft;
console.log(middleColOffsetLeft);
const rightCol = document.querySelector('.container-right');
const rightColOffsetLeft = rightCol.offsetLeft;
console.log(rightColOffsetLeft);
const bgVideo = document.getElementById('video-bg-middle');
bgVideo.style.marginLeft = `-${middleColOffsetLeft}px`
const canvasLeft = document.getElementById("canvas-left");
const canvasRight = document.getElementById("canvas-right");
canvasRight.style.marginLeft = `-${rightColOffsetLeft}px`;
const ctx = canvasLeft.getContext("2d");
const ctx1 = canvasRight.getContext("2d");
function drawFrames() {
if (!bgVideo.paused && !bgVideo.ended) {
ctx.drawImage(bgVideo, 0, 0, canvasLeft.width, canvasLeft.height);
ctx1.drawImage(bgVideo, 0, 0, canvasRight.width, canvasRight.height);
}
requestAnimationFrame(drawFrames); // Call drawFrames again on the next animation frame
}
bgVideo.addEventListener('play', () => {
drawFrames();
});
```
<!--THIS IS HTML FOR SOLUTION WITH VIDEOS-->
<div class="row g-0">
<div class="o-cont-m o-col-3-c container-left overfllow-hidden">
<video id="video-bg-left" loop muted autoplay playsinline>
<source src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4" type="video/mp4">
Your browser does not support the video tag.s
</video>
</div>
<div class="o-cont-m o-col-3-c container-middle sticky-top overfllow-hidden">
<video id="video-bg-middle" loop muted autoplay playsinline>
<source src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4" type="video/mp4">
Your browser does not support the video tag.s
</video>
</div>
<div class="o-cont-m o-col-4-c container-right overfllow-hidden">
<video id="video-bg-right" loop muted autoplay playsinline>
<source src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4" type="video/mp4">
Your browser does not support the video tag.s
</video>
</div>
<div class="o-cont-m o-col-3-c" style="background-color:grey"></div>
<div class="o-cont-m o-col-3-c col-transparent sticky-top"></div>
<div class="o-cont-m o-col-4-c" style="background-color:grey"></div>
<div class="o-cont-m o-col-3-c" style="background-color:tomato"></div>
<div class="o-cont-m o-col-3-c col-transparent sticky-top" ></div>
<div class="o-cont-m o-col-4-c" style="background-color:tomato"></div>
</div>
````
/*CSS STAYS SAME*/
.o-col-3-c {
flex:0 0 auto;
width:30%;
}
.o-col-4-c {
flex:0 0 auto;
width:40%;
}
.o-cont-m {
block-size:100vh;
}
.col-transparent{
background: rgba(0, 0, 0, .5);
}
video{
width:100vw;
height:100%;
display:block;
object-fit:fill;
}
.container-left,
.container-middle,
.container-right {
overflow:hidden;
}
#video-bg-left,
#video-bg-middle {
width: 100vw;
height: 100vh;
display: block;
}
```
/*JS FOR SOLUTION WITH 3 DIFFERENT VIDEOS*/
const updateLeftMargin = () => {
const middleCol = document.querySelector('.container-middle');
const rightCol = document.querySelector('.container-right');
const middleColOffsetLeft = middleCol.offsetLeft;
const rightColOffsetLeft = rightCol.offsetLeft;
const updateBackground = (elementId, marginLeft, width) => {
const element = document.getElementById(elementId);
if (element) {
if (marginLeft !== null) {
element.style.marginLeft = marginLeft < 0 ? `${marginLeft}px` : `-${marginLeft}px`;
}
if (width !== null) {
element.style.width = `${width}px`;
}
}
};
updateBackground('video-bg-middle', middleColOffsetLeft, null);
updateBackground('video-bg-right', -rightColOffsetLeft, null);
};
updateLeftMargin();
window.addEventListener('resize', updateLeftMargin);
Canvases can actually do the job. The blurry effect will appear if the canvas got initialized with a smaller size than
the video, so increasing the size of the canvas (e.g. width="1920" height="1080" for HD) will also sharpen the
appearance. The canvas might not look 100% identical to the <video>
so we can do a workaround by hidding the visibility of the video and replace it with an additional <canvas>
. By drawing to all those canvases at a time there also won't be any issue regarding sync.
Note: my css is not perfect but the left part is showing it pretty well
const video = document.getElementById('video');
const canvas = document.querySelectorAll('canvas');
const canvasContexts = [];
canvas.forEach(canvas => canvasContexts.push(canvas.getContext(
'2d', { alpha: false }
)));
function draw() {
canvasContexts.forEach(context => {
context.drawImage(video, 0, 0, context.canvas.width, context.canvas.height);
});
if ('requestVideoFrameCallback' in HTMLVideoElement.prototype) {
video.requestVideoFrameCallback(() => draw());
} else {
window.requestAnimationFrame(() => draw());
}
}
video.addEventListener('play', () => {
draw();
});
body {
margin: 0;
}
canvas {
block-size: 100%;
inline-size: 100vw;
}
.canvas-right {
translate: -59.5%;
}
.overflow-hidden {
overflow: hidden;
}
.hidden-video {
position: fixed;
visibility: hidden;
}
.video-wrapper {
position: fixed;
inline-size: 100%;
z-index: -1
}
.row {
display: flex;
}
.col-3 {
inline-size: 30%;
}
.col-4 {
inline-size: 40%;
}
.div-filler {
block-size: 100vh;
background: purple;
}
<main>
<div style="position: relative">
<div class="video-wrapper">
<video class="hidden-video" id="video" muted autoplay loop>
<source src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4">
</video>
<canvas width="1920" height="1080"></canvas>
</div>
<div class="row">
<div class="col-3 overflow-hidden">
<div>
<canvas class="canvas-left" width="1920" height="1080"></canvas>
</div>
<div class="div-filler"></div>
</div>
<div class="col-3">
</div>
<div class="col-4 overflow-hidden">
<div>
<canvas class="canvas-right" width="1920" height="1080"></canvas>
<div class="div-filler"></div>
</div>
</div>
</div>
</div>
</main>