HTML5 Canvas中的交互式视频前后对比

Some time ago I wrote an article about building a video before-and-after comparator for a client. My most recent project with the video graphics company sequence used a variation of the technique, but with something a little special…

前段时间,我写了一篇关于为客户构建视频前后比较器的文章。 我最近在视频图形公司系列中的项目使用了一种技术的变化,但是有些特殊之处……

One of the issues with playing back two video streams in a browser at the same time is that eventually they will get out of sync with each other: one might start a little earlier than the other, or play a frame or two faster. The earlier example was only a few seconds in length, making any synchronization issue minor, but in this case the full videos are over two minutes long, meaning that any syncing issue would quickly become very apparent.

同时在浏览器中播放两个视频流的问题之一是,它们最终最终会彼此不同步:一个视频播放可能比另一个视频播放得早一些,或者播放的速度快了一两个帧。 较早的示例只有几秒钟的长度,这使得所有同步问题都很少,但是在这种情况下,完整视频的时长超过了两分钟 ,这意味着任何同步问题都会很快变得很明显。

There’s no clear performant way in JavaScript to sync two videos. It’s possible to read the time signature of one and try to force it on the other, but that means the second video isn’t playing per se, more like jumping from frame to frame, and usually skipping a few in the process.

JavaScript中没有明确的性能同步方式来同步两个视频。 可以读取一个的时标并尝试将其强加给另一个,但这意味着第二个视频本身并不是在播放 ,更像是逐帧跳转,并且通常在此过程中跳过一些。

My solution was to use . By starting both videos at the same time only when they were ready to play, and reading frames from them simultaneously using requestAnimationFrame to paint on the <canvas> element, I could pretty much guarantee the visual presentation of both videos at the same time.

我的解决方案是使用 。 通过仅在两个视频准备好播放时才同时启动两个视频,并使用requestAnimationFrame同时在其上读取帧以在<canvas>元素上绘制,我几乎可以保证两个视频的视觉呈现。

标记 (The Markup)

The HTML was very similar to my earlier example, only with an addition above and below the video code:

HTML与我之前的示例非常相似,只是在视频代码的上方和下方有一个附加内容:

<canvas id="videoMerge" width="1024" height="576">
</canvas>

<div id="video-compare-container">
    <video poster="sequence-logo-solid.png" id="rightVideo">
        <source src="after.webm">
        <source src="after.mp4">    
    </video>
    <video poster="sequence-logo-wireframe.png" id="leftVideo">
         <source src="before.webm">
         <source src="before.mp4">
    </video>
</div>

<div id="videoUI"></div>

The <canvas> is the exact width and height of the final videos, the #videoUI element is prepared to catch the buttons that will be progressively added to the page.

<canvas>是最终视频的确切宽度和高度, #videoUI元素准备捕获将逐渐添加到页面的按钮。

CSS (The CSS)

The styling is also fairly straightforward; the#videoUI container is set to display: flex in order to evenly distribute the buttons that will be added to it:

样式也非常简单。 #videoUI容器设置为display: flex ,以便均匀分布将添加到其中的按钮:

#video-compare-container {
    display: block;
    line-height: 0;
}
#videoMerge, #videoUI { 
    width: 100%;
    display: block;
    margin: 0 auto;
    background: url("sequence-background.png");
    background-size: cover;
}
#videoUI {
    font-family: Helvetica Neue Regular, Helvetica, Arial, sans-serif;
    display: flex;
    justify-content: space-between;
    background: #f5f5f5;
    
}
#videoUI button {
    padding: 1rem;
    font-weight: 400;
    font-size: 1.3rem;
    border: none;
    background: none;
    cursor: pointer;
    outline: none;
}
#videoUI button:hover {
    background: #8e8b8b;
    color: white;
}

The video container is provided with a background image representing the first (divided) frame of the video, since the videos themselves don’t appear on the <canvas> element until the ‘Play` button is used.

为视频容器提供了表示视频第一(分割)帧的背景图像,因为直到使用“播放”按钮,视频本身才会出现在<canvas>元素上。

剧本 (The Script)

The JavaScript is placed at the bottom of the page. The first part identifies the various elements, the initial position of the divider, the width and height of the videos, adds the Play button, and hides the videos. (Note that videos can still be played and read with JavaScript, even if they are hidden).

JavaScript位于页面底部。 第一部分标识各种元素,分隔线的初始位置,视频的宽度和高度,添加“ Play按钮并隐藏视频。 (请注意,即使隐藏了视频,仍可以使用JavaScript播放和阅读视频)。

var videoContainer = document.getElementById("video-compare-container"),
videoUI = document.getElementById("videoUI"),
videoMerge = document.getElementById("videoMerge"),
leftVideo = document.getElementById("leftVideo"),
rightVideo = document.getElementById("rightVideo"),
videoControl = document.createElement("button"),
position = 0.5,
vidHeight = 576,
vidWidth = 1024;
mergeContext = videoMerge.getContext("2d");
videoContainer.style.display = "none";
videoControl.innerHTML = "Play";
videoUI.appendChild(videoControl);

A function controls the pause and play of the videos:

一个功能控制视频的暂停和播放:

videoControl.addEventListener("click", playPause, false);

function playPause() {
    if (leftVideo.paused) {
        videoControl.innerHTML = "Pause";
        playVids();
    } else {
        leftVideo.pause();
        rightVideo.pause();
        videoControl.innerHTML = "Play";
    }
}

The rest of the script is initiated when both videos are ready to play:

当两个视频都可以播放时,脚本的其余部分将启动:

function playVids() {
    if (leftVideo.readyState > 3 && rightVideo.readyState > 3) {
    leftVideo.play();
    rightVideo.play();

	function trackLocation(e) {
            position = ((e.pageX - videoMerge.offsetLeft) / videoMerge.offsetWidth);
 		if (position <= 1 && position >= 0) {
            		leftVideo.volume = position;
            		rightVideo.volume = (1 - position);
        }
    }
    
videoMerge.addEventListener("mousemove", trackLocation, false); 
videoMerge.addEventListener("touchstart",trackLocation,false);
videoMerge.addEventListener("touchmove",trackLocation,false);

        function drawLoop() {
            mergeContext.drawImage(leftVideo, 0, 0, vidWidth, vidHeight,
		0, 0, vidWidth, vidHeight);
            mergeContext.drawImage(rightVideo, 
		(vidWidth * position).clamp(0.01,vidWidth), 0,
		(vidWidth - (vidWidth * position)).clamp(0.01,vidWidth),
		vidHeight,(vidWidth * position).clamp(0.01,vidWidth), 0,
		(vidWidth - (vidWidth * position)).clamp(0.01,vidWidth), vidHeight);
            requestAnimationFrame(drawLoop);
        }
    requestAnimationFrame(drawLoop);
}
}

The script starts by playing both videos, setting any interaction on them to the trackLocation() function. Similar to the previous example, trackLocation() determines the relative position of the interaction inside the video area from 0 (extreme left) and 1 (extreme right) and sets the volume of the leftVideo to that amount, setting the music volume appropriately to the proportion of the visible finished video shown.

该脚本首先播放两个视频,然后将它们之间的任何交互设置为trackLocation()函数。 与前面的示例类似, trackLocation()从0(最左端)和1(最右端trackLocation()确定视频区域内交互的相对位置,并将leftVideo的音量设置为该数量,并将音乐音量适当地设置为显示的可见完整视频的比例。

If the right video also had a music track, you could set the volume of that to be appropriately mixed with a little subtraction:

如果正确的视频也有音乐曲目,则可以将其音量设置为适当地混合一点点减法:

rightVideo.volume = (1 - position);

drawLoop (drawLoop)

The drawLoop function is the most complex aspect of the script: it must draw a portion of each video. You’ll notice that each mergeContext has eight values:

drawLoop函数是脚本中最复杂的方面:它必须绘制每个视频的一部分。 您会注意到,每个mergeContext具有八个值:

  • the first two values are the top left coordinates of the source (i.e. the video): an x value, followed by a y.

    前两个值是 (即视频)的左上坐标: x值,后跟y

  • the second pair of values are the bottom right coordinates of the source (again, x and y).

    第二对值是源的右下坐标(再次是xy )。

  • the third pair of numbers represent the top left corner of the target (the <canvas> element), in x and y order. This is where the portion of the video frame (as determined by the first four coordinates) will start to be “painted”.

    第三对数字表示目标 (的左上角 <canvas>元素),在xy顺序。 这是视频帧的一部分(由前四个坐标确定)将开始“绘制”的位置。

  • not surprisingly, the last pair of numbers (again, x and y) is the bottom right corner of the target, where the painting of the copied video frame ends.

    这并不奇怪,最后一对数字(再次,的xy )是目标,则复制的视频帧端部,其中画的右下角

You can think of mergeContext as “lifting” a portion of a frame from each (hidden) video and “pasting” it down onto the canvas. To make things easier, the first mergeContext draws the complete frame of the left video onto the <canvas>: starting from the top left corner of the element, all the way to the bottom right.

您可以将mergeContext看作是从每个(隐藏的)视频中“举起”一帧的一部分并将其“粘贴”到画布上。 为了使事情变得简单,第一个mergeContext将左视频的完整帧绘制到<canvas> :从元素的左上角一直到右下角。

The second mergeContext is tricker: it must copy the video from the position of the cursor or touch, relative to the video itself, all the way down to the bottom right corner of the video, and paste it into an appropriate area of the <canvas>. The clamp prototype is used to ensure that these computed coordinates do not exceed the bounds of the <canvas> itself.

第二个mergeContext棘手:必须将光标或触摸相对于视频本身的位置一直复制到视频的右下角,然后将其粘贴到<canvas>的适当区域中<canvas>clamp原型用于确保这些计算的坐标不会超出<canvas>本身的范围。

Number.prototype.clamp = function(min, max) {
  return Math.min(Math.max(this, min), max);
};

This copied portion is pasting on top of the pasted left video, completing the effect.

此复制的部分粘贴在粘贴的左侧视频的顶部,从而完成效果。

结论 (Conclusion)

It’s still possible for low-performing computers to skip frames using this solution - we’re still loading two 720p videos and trying to have them play back simultaneously - but the solution appears to work well for most.

效果不佳的计算机仍然可以使用此解决方案跳过帧-我们仍在加载两个720p视频,并尝试同时播放它们-但该解决方案似乎对大多数情况都适用。

Unfortunately Safari has a (so far) untreatable playback problem: one video lags about a second behind the first, despite every effort. Even Apple’s own suggested solution - using the HTML5 mediaController attribute to iniate playback - doesn’t appear to work. I’ve also found some issues with playback in the latest Firefox beta, but performance appears to be fine in the standard Firefox release, so I will assume that it is a bug in the beta.

不幸的是,Safari存在一个(迄今为止)无法解决的播放问题:尽管付出了很大的努力,但一个视频仍比第一个视频落后大约一秒钟。 甚至苹果自己建议的解决方案-使用HTML5 mediaController属性启动播放-似乎也不起作用。 我还发现最新的Firefox Beta中的播放存在一些问题,但是在标准Firefox版本中,性能似乎还不错,因此我认为这是Beta中的错误。

Sadly, iOS is still clever enough to work out that the resulting canvas is the product of autoplayed videos; it won’t play more than one (fullscreen) video at a time.

遗憾的是,iOS仍然足够聪明,无法得出结果,画布是自动播放视频的产物。 一次最多播放一个(全屏)视频。

翻译自: https://thenewcode.com/364/Interactive-Before-and-After-Video-Comparison-in-HTML5-Canvas

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值