使用 reveal.js 逐步浏览视频文件
Stepping through a video file with reveal.js
问题与疑问
在 reveal.js 演示文稿中,我想包含一个长视频文件。我想让 playblack 停在某些位置,这样我就有时间向观众解释他们看到的是什么。然后,我希望在单击时继续播放。我该怎么做?
到目前为止未成功的尝试
我的尝试如下。我将视频文件分成 1.webm
、2.webm
、3.webm
等部分,这样每个部分都在我想休息的地方结束。那么我的想法是
覆盖 Reveal.js 的 keydown 事件,这样它就不会转到下一张幻灯片,而是执行我的 Javascript。我怎样才能做这样的事情?
<div class="slides">
<section class="video-stepper">
<video>
<source data-src="1.webm" type="video/webm" />
</video>
</section>
</div>
<script>
$(function() {
// How can I do this?
Reveal.addEventListener('click', function(event) {
if ($(event.currentSlide).hasClass('video-stepper')) {
event.preventDefault();
// change 'src' of the video element and start the playback.
}
});
});
</script>
使用片段并在显示时自动播放视频:
<div class="slides">
<section class="video-stepper">
<video class="fragment current-visible video-step">
<source data-src="1.webm" type="video/webm" />
</video>
<video class="fragment current-visible video-step">
<source data-src="2.webm" type="video/webm" />
</video>
<video class="fragment current-visible video-step">
<source data-src="3.webm" type="video/webm" />
</video>
</section>
</div>
<script>
$(function() {
Reveal.addEventListener('fragmentshown', function(event) {
if ($(event.fragment).hasClass('video-step')) {
event.fragment.play();
}
});
});
</script>
还有一些 CSS 取自问题 Hide reveal.js fragments after their appearance,因此片段相互堆叠:
.fragment.current-visible.visible:not(.current-fragment) {
display: none;
height:0px;
line-height: 0px;
font-size: 0px;
}
但是,这带有一些淡入和淡出,看起来很糟糕。如何避免褪色?
进入视频幻灯片时,您基本上可以通过调用 Reveal.disableEventListeners()
来禁用 reveal.js,然后将您自己的逻辑绑定到 keydown 事件,直到您浏览完所有视频,然后再启用 reveal.js 再次 Reveal.addEventListeners()
.
需要一些额外的努力来避免切换到下一个视频时出现闪烁。您可以为新视频添加一个新的 <video>
元素,借助 CSS z-index
将其置于当前 <video>
之上,播放新视频,然后删除老.
HTML
<section class="video-stepper">
<!-- Unlike the other <video> element, this one is not absolutely
positioned. We hide it with CSS, but use it to reserve space
on the slide and compute the optimal width and height. -->
<video class="placeholder stretch">
<source src="1.webm">
</video>
<video class="video-step" data-sources='["1.webm","2.webm","3.webm"]'></video>
</section>
CSS
.video-stepper {
position: relative;
}
video.video-step {
position: absolute;
top: 0;
left: 0;
}
video.video-step.front {
z-index: 10;
}
video.placeholder {
visibility: hidden;
}
Javascript
这有点冗长,但效果很好。
Reveal.addEventListener('slidechanged', function(event) {
if ($(event.currentSlide).hasClass('video-stepper')) {
// When we enter a slide with a step-by-step video, we stop reveal.js
// from doing anything. Below, we define our own keystroke handler.
Reveal.removeEventListeners();
// Set the width and height of the video so that it fills the slide.
var stretcher = $(event.currentSlide).find('video.placeholder').get(0);
var video = $(event.currentSlide).find('video.video-step').get(0);
video.setAttribute('width', stretcher.getAttribute('width'));
video.setAttribute('height', stretcher.getAttribute('height'));
// Convert the data-sources attribute to an array of strings. We will
// iterate through the array with current_video_index.
var sources = JSON.parse(video.getAttribute('data-sources'));
var current_video_index = 0;
// Add a <source> element to the video and set the 'src' to
// the first video.
var source = document.createElement('source');
source.setAttribute('src', sources[0]);
video.appendChild(source);
document.addEventListener('keydown', function step_through_videos(event) {
if (event.which == 39) {
// right arrow key: show next video
// For the next video, create a new <video> element
// and place it on top of the old <video> element.
// Then load and play the new. This avoids flickering.
var new_video = $(video).clone().get(0);
var new_video_source = $(new_video).children('source').get(0);
new_video_source.src = sources[current_video_index];
new_video.load();
$(new_video).addClass('front video-step');
$(new_video).insertAfter(video);
new_video.play();
// Wait a little before removing the old video.
new Promise((resolve) => setTimeout(resolve, 500)).then(function() {
video.remove();
video = new_video;
$(video).removeClass('front');
});
current_video_index = current_video_index + 1;
event.preventDefault();
} else if (event.which == 37) {
// left arrow key: return the counter to previous video
current_video_index = current_video_index - 1;
event.preventDefault();
}
if (0 > current_video_index || current_video_index >= sources.length) {
// Reinstall reveal.js handlers.
document.removeEventListener('keydown', step_through_videos, true);
Reveal.addEventListeners();
console.log('Added reveal.js event listeners.');
}
}, true);
}
});
问题与疑问
在 reveal.js 演示文稿中,我想包含一个长视频文件。我想让 playblack 停在某些位置,这样我就有时间向观众解释他们看到的是什么。然后,我希望在单击时继续播放。我该怎么做?
到目前为止未成功的尝试
我的尝试如下。我将视频文件分成 1.webm
、2.webm
、3.webm
等部分,这样每个部分都在我想休息的地方结束。那么我的想法是
覆盖 Reveal.js 的 keydown 事件,这样它就不会转到下一张幻灯片,而是执行我的 Javascript。我怎样才能做这样的事情?
<div class="slides"> <section class="video-stepper"> <video> <source data-src="1.webm" type="video/webm" /> </video> </section> </div> <script> $(function() { // How can I do this? Reveal.addEventListener('click', function(event) { if ($(event.currentSlide).hasClass('video-stepper')) { event.preventDefault(); // change 'src' of the video element and start the playback. } }); }); </script>
使用片段并在显示时自动播放视频:
<div class="slides"> <section class="video-stepper"> <video class="fragment current-visible video-step"> <source data-src="1.webm" type="video/webm" /> </video> <video class="fragment current-visible video-step"> <source data-src="2.webm" type="video/webm" /> </video> <video class="fragment current-visible video-step"> <source data-src="3.webm" type="video/webm" /> </video> </section> </div> <script> $(function() { Reveal.addEventListener('fragmentshown', function(event) { if ($(event.fragment).hasClass('video-step')) { event.fragment.play(); } }); }); </script>
还有一些 CSS 取自问题 Hide reveal.js fragments after their appearance,因此片段相互堆叠:
.fragment.current-visible.visible:not(.current-fragment) { display: none; height:0px; line-height: 0px; font-size: 0px; }
但是,这带有一些淡入和淡出,看起来很糟糕。如何避免褪色?
进入视频幻灯片时,您基本上可以通过调用 Reveal.disableEventListeners()
来禁用 reveal.js,然后将您自己的逻辑绑定到 keydown 事件,直到您浏览完所有视频,然后再启用 reveal.js 再次 Reveal.addEventListeners()
.
需要一些额外的努力来避免切换到下一个视频时出现闪烁。您可以为新视频添加一个新的 <video>
元素,借助 CSS z-index
将其置于当前 <video>
之上,播放新视频,然后删除老.
HTML
<section class="video-stepper">
<!-- Unlike the other <video> element, this one is not absolutely
positioned. We hide it with CSS, but use it to reserve space
on the slide and compute the optimal width and height. -->
<video class="placeholder stretch">
<source src="1.webm">
</video>
<video class="video-step" data-sources='["1.webm","2.webm","3.webm"]'></video>
</section>
CSS
.video-stepper {
position: relative;
}
video.video-step {
position: absolute;
top: 0;
left: 0;
}
video.video-step.front {
z-index: 10;
}
video.placeholder {
visibility: hidden;
}
Javascript
这有点冗长,但效果很好。
Reveal.addEventListener('slidechanged', function(event) {
if ($(event.currentSlide).hasClass('video-stepper')) {
// When we enter a slide with a step-by-step video, we stop reveal.js
// from doing anything. Below, we define our own keystroke handler.
Reveal.removeEventListeners();
// Set the width and height of the video so that it fills the slide.
var stretcher = $(event.currentSlide).find('video.placeholder').get(0);
var video = $(event.currentSlide).find('video.video-step').get(0);
video.setAttribute('width', stretcher.getAttribute('width'));
video.setAttribute('height', stretcher.getAttribute('height'));
// Convert the data-sources attribute to an array of strings. We will
// iterate through the array with current_video_index.
var sources = JSON.parse(video.getAttribute('data-sources'));
var current_video_index = 0;
// Add a <source> element to the video and set the 'src' to
// the first video.
var source = document.createElement('source');
source.setAttribute('src', sources[0]);
video.appendChild(source);
document.addEventListener('keydown', function step_through_videos(event) {
if (event.which == 39) {
// right arrow key: show next video
// For the next video, create a new <video> element
// and place it on top of the old <video> element.
// Then load and play the new. This avoids flickering.
var new_video = $(video).clone().get(0);
var new_video_source = $(new_video).children('source').get(0);
new_video_source.src = sources[current_video_index];
new_video.load();
$(new_video).addClass('front video-step');
$(new_video).insertAfter(video);
new_video.play();
// Wait a little before removing the old video.
new Promise((resolve) => setTimeout(resolve, 500)).then(function() {
video.remove();
video = new_video;
$(video).removeClass('front');
});
current_video_index = current_video_index + 1;
event.preventDefault();
} else if (event.which == 37) {
// left arrow key: return the counter to previous video
current_video_index = current_video_index - 1;
event.preventDefault();
}
if (0 > current_video_index || current_video_index >= sources.length) {
// Reinstall reveal.js handlers.
document.removeEventListener('keydown', step_through_videos, true);
Reveal.addEventListeners();
console.log('Added reveal.js event listeners.');
}
}, true);
}
});