Dynamically create / destroy videos.

This commit is contained in:
John Preston 2024-03-13 15:08:22 +04:00
parent 7913d2a82d
commit 3e373200b1
2 changed files with 101 additions and 42 deletions

View file

@ -41,6 +41,14 @@ var IV = {
} }
e.preventDefault(); e.preventDefault();
}, },
getElementTop: function (element) {
var top = 0;
while (element && !element.classList.contains('page-scroll')) {
top += element.offsetTop;
element = element.offsetParent;
}
return top;
},
jumpToHash: function (hash, instant) { jumpToHash: function (hash, instant) {
var current = IV.computeCurrentState(); var current = IV.computeCurrentState();
current.hash = hash; current.hash = hash;
@ -55,12 +63,7 @@ var IV = {
var element = document.getElementsByName(hash)[0]; var element = document.getElementsByName(hash)[0];
if (element) { if (element) {
var y = 0; IV.scrollTo(IV.getElementTop(element), instant);
while (element && !element.classList.contains('page-scroll')) {
y += element.offsetTop;
element = element.offsetParent;
}
IV.scrollTo(y, instant);
} }
}, },
frameKeyDown: function (e) { frameKeyDown: function (e) {
@ -103,6 +106,7 @@ var IV = {
const was = IV.lastScrollTop; const was = IV.lastScrollTop;
IV.lastScrollTop = IV.findPageScroll().scrollTop; IV.lastScrollTop = IV.findPageScroll().scrollTop;
IV.updateJumpToTop(was < IV.lastScrollTop); IV.updateJumpToTop(was < IV.lastScrollTop);
IV.checkVideos();
}, },
updateJumpToTop: function (scrolledDown) { updateJumpToTop: function (scrolledDown) {
if (IV.lastScrollTop < 100) { if (IV.lastScrollTop < 100) {
@ -246,9 +250,11 @@ var IV = {
IV.notify({ event: 'ready' }); IV.notify({ event: 'ready' });
IV.forceScrollFocus(); IV.forceScrollFocus();
IV.frameScrolled();
}, },
initMedia: function () { initMedia: function () {
const photos = document.getElementsByClassName('photo'); var scroll = IV.findPageScroll();
const photos = scroll.getElementsByClassName('photo');
for (let i = 0; i < photos.length; ++i) { for (let i = 0; i < photos.length; ++i) {
const photo = photos[i]; const photo = photos[i];
if (photo.classList.contains('loaded')) { if (photo.classList.contains('loaded')) {
@ -265,18 +271,70 @@ var IV = {
} }
img.src = url.substr(5, url.length - 7); img.src = url.substr(5, url.length - 7);
if (img.complete) { if (img.complete) {
img.onload(); photo.classList.add('loaded');
IV.stopAnimations(photo);
} }
} }
const videos = document.getElementsByTagName('video'); IV.videos = [];
const videos = scroll.getElementsByClassName('video');
for (let i = 0; i < videos.length; ++i) {
const element = videos[i];
IV.videos.push({
element: element,
src: String(element.getAttribute('data-src')),
autoplay: (element.getAttribute('data-autoplay') == '1'),
loop: (element.getAttribute('data-loop') == '1'),
small: (element.getAttribute('data-small') == '1'),
filled: (element.firstChild
&& element.firstChild.tagName == 'VIDEO'),
});
}
},
checkVideos: function () {
const visibleTop = IV.lastScrollTop;
const visibleBottom = visibleTop + IV.findPageScroll().offsetHeight;
const videos = IV.videos;
for (let i = 0; i < videos.length; ++i) { for (let i = 0; i < videos.length; ++i) {
const video = videos[i]; const video = videos[i];
if (video.classList.contains('loaded')) { const element = video.element;
continue; const wrap = element.offsetParent; // video-wrap
const top = IV.getElementTop(wrap);
const bottom = top + wrap.offsetHeight;
if (top < visibleBottom && bottom > visibleTop) {
if (!video.filled) {
video.filled = true;
element.innerHTML = '<video muted class="'
+ (video.small ? 'video-small' : '')
+ '"'
+ (video.autoplay
? ' preload="auto" autoplay'
: (video.small
? ''
: ' controls'))
+ (video.loop ? ' loop' : '')
+ '><source src="'
+ video.src
+ '" type="video/mp4" />'
+ '</video>';
var media = element.firstChild;
const HAVE_CURRENT_DATA = 2;
if (media && media.readyState >= HAVE_CURRENT_DATA) {
media.classList.add('loaded');
IV.stopAnimations(media);
} else if (media) {
const created = new Date();
media.addEventListener('canplay', function () {
media.classList.add('loaded');
if ((new Date() - created) < 100) {
IV.stopAnimations(media);
}
});
}
}
} else if (video.filled && video.autoplay) {
video.filled = false;
element.innerHTML = '';
} }
video.addEventListener('canplay', function () {
video.classList.add('loaded');
});
} }
}, },
showTooltip: function (text) { showTooltip: function (text) {
@ -346,7 +404,14 @@ var IV = {
var to = article(IV.makeScrolledContent(data.html)); var to = article(IV.makeScrolledContent(data.html));
morphdom(from, to, { morphdom(from, to, {
onBeforeElUpdated: function (fromEl, toEl) { onBeforeElUpdated: function (fromEl, toEl) {
if (fromEl.classList.contains('loaded')) { if (fromEl.classList.contains('video')
&& toEl.classList.contains('video')
&& fromEl.hasAttribute('data-src')
&& toEl.hasAttribute('data-src')
&& (fromEl.getAttribute('data-src')
== toEl.getAttribute('data-src'))) {
return false;
} else if (fromEl.classList.contains('loaded')) {
toEl.classList.add('loaded'); toEl.classList.add('loaded');
} }
return !fromEl.isEqualNode(toEl); return !fromEl.isEqualNode(toEl);
@ -466,8 +531,7 @@ var IV = {
now.classList.add(back ? 'hidden-left' : 'hidden-right'); now.classList.add(back ? 'hidden-left' : 'hidden-right');
now.classList.remove(back ? 'hidden-right' : 'hidden-left'); now.classList.remove(back ? 'hidden-right' : 'hidden-left');
now.firstChild.getAnimations().forEach( IV.stopAnimations(now.firstChild);
(animation) => animation.finish());
if (!was.listening) { if (!was.listening) {
was.listening = true; was.listening = true;
@ -476,6 +540,10 @@ var IV = {
|| was.classList.contains('hidden-right')) { || was.classList.contains('hidden-right')) {
if (was.parentNode) { if (was.parentNode) {
was.parentNode.removeChild(was); was.parentNode.removeChild(was);
var videos = was.getElementsByClassName('video');
for (var i = 0; i < videos.length; ++i) {
videos[i].innerHTML = '';
}
} }
} }
}); });
@ -512,6 +580,7 @@ var IV = {
} }
IV.forceScrollFocus(); IV.forceScrollFocus();
IV.frameScrolled();
}, },
forceScrollFocus: function () { forceScrollFocus: function () {
IV.findPageScroll().focus(); IV.findPageScroll().focus();
@ -520,10 +589,17 @@ var IV = {
IV.findPageScroll().focus(); IV.findPageScroll().focus();
}, 100); }, 100);
}, },
stopAnimations: function (element) {
element.getAnimations().forEach(
(animation) => animation.finish());
},
back: function () { back: function () {
window.history.back(); window.history.back();
}, },
videos: {},
videosPlaying: {},
cache: {}, cache: {},
index: 0, index: 0,
position: 0 position: 0
@ -533,6 +609,7 @@ document.onclick = IV.frameClickHandler;
document.onkeydown = IV.frameKeyDown; document.onkeydown = IV.frameKeyDown;
document.onmouseenter = IV.frameMouseEnter; document.onmouseenter = IV.frameMouseEnter;
document.onmouseup = IV.frameMouseUp; document.onmouseup = IV.frameMouseUp;
document.onresize = IV.checkVideos;
window.onmessage = IV.postMessageHandler; window.onmessage = IV.postMessageHandler;
window.addEventListener('popstate', function (e) { window.addEventListener('popstate', function (e) {
if (e.state) { if (e.state) {

View file

@ -534,31 +534,13 @@ QByteArray Parser::block(
if (!video.id) { if (!video.id) {
return "Video not found."; return "Video not found.";
} }
const auto src = documentFullUrl(video); auto inner = tag("div", {
auto vattributes = Attributes{}; { "class", "video" },
if (collageSmall) { { "data-src", documentFullUrl(video) },
vattributes.push_back({ "class", "video-small" }); { "data-autoplay", data.is_autoplay() ? "1" : "0" },
} { "data-loop", data.is_loop() ? "1" : "0" },
if (data.is_autoplay()) { { "data-small", collageSmall ? "1" : "0" },
vattributes.push_back({ "preload", "auto" }); });
vattributes.push_back({ "autoplay", std::nullopt });
} else if (!collageSmall) {
vattributes.push_back({ "controls", std::nullopt });
}
if (data.is_loop()) {
vattributes.push_back({ "loop", std::nullopt });
}
vattributes.push_back({ "muted", std::nullopt });
auto inner = tag(
"video",
vattributes,
tag("source", { { "src", src }, { "type", "video/mp4" } }));
if (collageSmall) {
inner += tag(
"div",
{ { "class", "video-play-outer" } },
tag("div", { { "class", "video-play" } }));
}
const auto minithumb = Images::ExpandInlineBytes(video.minithumbnail); const auto minithumb = Images::ExpandInlineBytes(video.minithumbnail);
if (!minithumb.isEmpty()) { if (!minithumb.isEmpty()) {
const auto image = Images::Read({ .content = minithumb }); const auto image = Images::Read({ .content = minithumb });