Sync HTML Videos

Issue: I am currently developing a 3 column layout where certain columns need to remain sticky at the top of the viewport. In one section, I want to use the same video as a background for all 3 columns, with the specific effect of having the middle column stay 'sticky-top' while allowing the left and right columns to scroll up.

I attempted a solution using <Canvas>, but encountered a few issues. Canvases are unable to render frames without a blurry effect, especially in webkit browsers (though it seems to work better in Firefox). If jsFiddle doesn't play the video correctly, you may need to insert Bootstrap's CDN links to view the sample solution I'm aiming for here. I also tried inserting videos in the left and right columns and adjusting margins to create a seamless effect, but encountered synchronization issues where the videos would eventually go out of sync, losing the cohesive visual effect. You can check out the solution with 3 background videos here.

 
<!-- HTML structure for the columns and videos will go here -->
 
/* CSS styles for the layout */
.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;
}
 
// JavaScript code for handling the video structures and synchronization
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();
});


//Continuation of the HTML structure for handling multiple videos
<!-- HTML structure for multiple videos goes here -->

// CSS styles for handling multiple videos
.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;
}

// JavaScript code for handling multiple videos structure and synchronization
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);

Answer №1

Using canvases can be a great solution. If the canvas is initialized with a smaller size than the video, a blurry effect may be noticeable. To enhance the appearance, simply increase the size of the canvas (e.g. set width="1920" height="1080" for HD). Keep in mind that the canvas may not look exactly like the <video> element, but you can hide the video and add an additional <canvas> element to work around this. Drawing to multiple canvases simultaneously can also ensure synchronization.

Note: my css may not be perfect, but it effectively displays the left part.

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>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Troubles with CSS in Wordpress

Hey there! I'm currently working on some wordpress tasks and running into an issue with the code. Everything looks good in the first two sections, but the third section is displaying at the top. Please refer to the attached image for a visual of the p ...

Why is the script from URL "http://.../js/myscript.js" not executing as expected after being fetched?

Is it possible to have the server run a JavaScript file specified in a URL, such as "http://.../js/script_name.js", and return the result instead of just displaying the source code? I am assuming that the server is using node.js. ...

Once the grunt build process is complete, execute "heroku run fileName" to launch the

In order to gain a deeper understanding of Heroku scheduling, I decided to delve into an informative post on the topic and followed the instructions provided to build the app. The highlight of the post was when I successfully executed heroku run numCheck a ...

Oops! An error occurred: Uncaught promise rejection - invalid link found for ProductListComponent

I am currently in the process of learning Angular and Ionic, but I am feeling a bit confused as to where my mistake is. I have looked at other questions, but I still can't seem to figure it out. Can anyone provide some assistance? Perhaps someone has ...

Distinguishing between resolving a promise in a service/factory as opposed to in a controller within AngularJS

After experimenting with resolving a promise in both a service and a controller, I have found that I prefer to resolve it in the service so that I can reuse the variable without having to resolve it multiple times. However, I am encountering an issue where ...

Tips for executing an npm command within a C# class library

I am currently developing a project in a class library. The main objective of this project is to execute a JavaScript project using an npm command through a method call in C#. The npm command to run the JavaScript project is: npm start The JavaScript ...

The submit button fails to produce any result

I have a submit button in a contact form that triggers PHP code to send an email. However, when I press the button nothing happens - not even the PHP code is displayed as it should be if PHP were not installed. Interestingly, I have tested other simple mai ...

Implement a Loop that Generates Buttons with Popups Using jQuery Mobile

Within the context of XML parsing, I have utilized this code to generate buttons dynamically using a loop: $('#button' + counter + paramcounter).click(function(){ sendData(escape(parameterarray[cnt2] + $('#textinput' + cnt + cnt2).v ...

How to keep a window/tab active without physically staying on that specific tab

Whenever I'm watching a video on a website and switch to another tab, the video stops playing. But when I switch back to the original tab, the video resumes. Is there a trick to prevent the video from stopping? I've already tried using scrollInto ...

Achieving True WYSIWYG Performance in TinyMCE 4.x: Tips and Tricks

Currently, I am in the process of developing a custom CMS where I am integrating tinymce to empower users to create page content, similar to what is seen on Wordpress. The content inputted by users in the admin area will be showcased within a designated el ...

Can the load API in jQuery be utilized to load fragments using a class selector instead of an id?

I've been working on enhancing a website's dashboard by incorporating more widgets onto the page. Interestingly, these widgets are already present on another page within the same domain. After some research, I discovered that utilizing the load A ...

Top tips for writing JavaScript that incorporates an AJAX call to determine whether the default action should be blocked

Utilizing JavaScript and jQuery, I am developing a handler for form submission that will either allow or prevent the form from being submitted based on certain conditions. Although this task is fairly straightforward, it becomes more complex due to the ne ...

React automatic scrolling

I am currently working on implementing lazy loading for the product list. I have created a simulated asynchronous request to the server. Users should be able to update the page by scrolling even when all items have been displayed. The issue arises when ...

What is the most effective way to adjust an adaptation: adjusting the max-width or the font-size?

Being new to coding, I'm still in the practice stage. However, I've noticed that whenever I shrink the screen size, the text starts misbehaving. How can I address this issue? Here is a specific example of the problem: https://i.sstatic.net/QZlN ...

When utilizing a node.js TCP socket to receive and send modified data, the functionality only operates successfully on the initial attempt

I am attempting to create a node.js TCP socket that will receive data, modify it, and then send it back. I found a helpful example on Stack Overflow here: . The example works perfectly as is, but when I add my own code to send data back, it only works for ...

What potential factors could lead to an MUI Snackbar failing to produce the accurate class names?

I am facing an issue with displaying notifications on my Gatsby blog whenever the service worker updates. I am using a MUI Snackbar toast for this purpose. However, sometimes the styling of the toast is not applied correctly and it ends up looking like thi ...

Best method for connecting user input with a class

I'm currently working with a WYSIWYG editor (Tinymce) that allows users to post YouTube videos. In order to make the video content responsive, I need to add the class "video-container" around any "iframe" tags inserted when users paste a YouTube link ...

Angular (2/4) application utilizing custom-named routing within a single page architecture

I'm currently working on an Angular application that consists of a login component and a home component which serves as the main handler for the entire single page application. Additionally, I have three more components named users, each with function ...

Learn how to switch between search and results views in Angular while also transferring data

Currently, I'm attempting to tackle a common task that I've yet to encounter an example of. Display 1 and Control 1 : In this view, there is a basic textbox with ng-model="searchTerm" and ngSubmit event. After the user enters their search term, ...

Issue with adding a separate audio track to a video in HTML

Currently, I am facing a challenge while working with React.JS. I am attempting to display a default video with sound, but I have separate files for the video and the sound. Although the video is working perfectly fine, I am struggling to synchronize the a ...