Lazy Loading Videos und der Viewport - Vanilla JS
Lazy loading von Videos wird leider noch nicht nativ von den Browsern unterstützt und so realisiert man es halt genau so wie es früher auch für Bilder notwendig war, oder vielmehr wie es für ätere Browser noch als Fallback eingesetzt wird.
HTML:
<!DOCTYPE html >
<html lang="de">
<head>
<meta charset="utf-8" />
<title>Video Lazy Loading Example</title>
</head>
<body>
<video controls preload="none" data-loading="lazy" data-autoplay="true" data-poster="/media/video/sammy.jpg" title="Sammy geht baden" data-description="Ein Beispiel-Video zur Demonstration der Einbindung." data-uploaddate="30.12.2018" width="100%">
<source type="video/mp4" src="/media/video/sammy-426x240.mp4" />
<source type="video/mp4" data-src="/media/video/sammy-640x360.mp4" data-media="(min-width: 640px)" />
<source type="video/mp4" data-src="/media/video/sammy-854x480.mp4" data-media="(min-width: 854px)" />
<source type="video/mp4" data-src="/media/video/sammy-1280x720.mp4" data-media="(min-width: 1280px)" />
<source type="video/mp4" data-src="/media/video/sammy-1920x1080.mp4" data-media="(min-width: 1920px)" />
</video>
<script src="video_lazy.js"></script>
</body>
</html>
JavaScript:
/** Created by: Udo Schmal | https://www.gocher.me/ */
(function() {
'use strict';
// lazy loading video
function loadVideo(el) {
if (el.dataset.poster) {
el.setAttribute('poster', el.dataset.poster);
el.removeAttribute('data-poster');
}
var childs = el.children;
for(var i = 0; i < childs.length; i++) {
if ((childs[i].tagName.toLowerCase() == 'source') && childs[i].dataset.src) {
childs[i].src = childs[i].dataset.src;
childs[i].removeAttribute('data-src');
}
}
if (el.dataset.controls && el.dataset.controls == 'true') {
el.setAttribute('controls', '');
el.removeAttribute('data-controls');
}
el.setAttribute('preload', 'auto');
el.load();
if (el.dataset.autoplay && el.dataset.autoplay == 'true') {
el.removeAttribute('data-autoplay');
el.setAttribute('autoplay', 'autoplay');
el.autoplay = true;
el.play();
}
}
// Viewport prototype to trigger event if element moves into viewport the fist time
function Viewport(els, callback) {
this.els = els;
this.callback = callback;
this.active = false;
var self = this;
var hidden = [];
function alreadyObserved(el) {
for(var i=0; i<hidden.length; i++) {
if (el === hidden[i]) {
return true;
}
}
return false;
}
for (var i=0; i < els.length; i++) {
var el = els[i];
// parent must have position attribute for getBoundingClientRect
let properties = window.getComputedStyle(el.parentNode);
if (!properties.getPropertyValue('position')) {
el.parentNode.style.position = 'relative';
while(el && el.tagName.toLowerCase() !== 'body') {
if (window.getComputedStyle(el).display === "none") {
if (!alreadyObserved(el)) {
hidden.push(el);
var observer = new MutationObserver(function(mutations) {
if (mutations[0].target.style.display !== 'none') {
self.handleEvent();
}
});
observer.observe(el, { attributes: true });
break;
}
}
el = el.parentNode;
}
}
}
this.handleEvent.bind(this);
// is already in viewport after dom ready
function ready(f){
/complete|loaded/i.test(document.readyState) ? f() : setTimeout(function(){ready(f);},9);
}
ready(function(){self.handleEvent();});
// add event listener to scroll event to check visibility change
document.addEventListener('scroll', this, true);
window.addEventListener('resize', this, true);
window.addEventListener('orientationchange', this, true);
}
Viewport.prototype = {
// check if element is visible
isVisible: function (el) {
var style = window.getComputedStyle(el);
return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
},
// check if element is in viewport
isInViewport: function (el) {
var bounding = el.getBoundingClientRect();
return (
bounding.bottom >= 0 &&
bounding.right >= 0 &&
bounding.top <= (window.innerHeight || document.documentElement.clientHeight) &&
bounding.left <= (window.innerWidth || document.documentElement.clientWidth)
);
},
// handle the visibility check and the resulting action
handleEvent: function () {
if (this.active === false) {
this.active = true;
for (var i=0; i < this.els.length; i++) {
if (this.isInViewport(this.els[i]) && this.isVisible(this.els[i])) {
this.callback(this.els[i]);
}
}
this.active = false;
}
}
};
// activate viewport for video loading
var videos = document.querySelectorAll("video[data-loading='lazy']");
if (videos.length > 0) {
if ("IntersectionObserver" in window) {
// create intersection observer
let lazyobserver = new IntersectionObserver(function(entries, observer) {
for (var i=0; i < entries.length; i++) {
if (entries[i].isIntersecting) {
loadVideo(entries[i].target);
lazyobserver.unobserve(entries[i].target);
}
}
},
{
threshold: [0.1],
// Set a minimum delay between notifications
delay: 100
});
// start observing
for (var i=0; i < videos.length; i++) {
lazyobserver.observe(videos[i]);
}
} else {
// use eventListeners
new Viewport(videos, loadVideo);
}
}
})();
Video.js - HTML5 Video Player
Hier nun eine simple Implementierung des HTML5 Video-Tags mit Flash Fallback. Mehr Infos unter https://videojs.com/.
Damit das Video auch auf dem iPad läuft müss auf die richtigen Einstellungen beim Encoden geachtet werden:
- H.264 Baseline Profile Level 3.0
- Auflösung unter 640 x 480 und Frameraten bis 30 Frames/Sec
- keine B Frames
- Bitraten bis 900kb
<!DOCTYPE html >
<html>
<head>
<title>Video.js | HTML5 Video Player</title>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: " />
<!-- Chang URLs to wherever Video.js files will be hosted -->
<link href="/scripts/video-js/video-js.css" rel="stylesheet" type="text/css" />
<!-- video.js must be in the html document head for older IEs to work. -->
<script src="/scripts/video-js/video.js"></script>
<!-- Unless using the CDN hosted version, update the URL to the Flash SWF -->
<script>
videojs.options.flash.swf = "/scripts/video-js/video-js.swf";
</script>
</head>
<body>
<video id="example_video_1" class="video-js vjs-default-skin vjs-big-play-centered" controls preload="auto" width="640" height="360" poster="/media/video/sammy.png" data-setup="{}">
<source src="/media/video/sammy.mp4" type="video/mp4" />
<source src="/media/video/sammy.webm" type="video/webm" />
<source src="/media/video/sammy.ogv" type="video/ogg" />
<track kind="captions" src="/scripts/video-js/sammy.de.vtt" srclang="de" label="Deutsch" />
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video>
</body>
</html>