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();
},
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) {
var current = IV.computeCurrentState();
current.hash = hash;
@ -55,12 +63,7 @@ var IV = {
var element = document.getElementsByName(hash)[0];
if (element) {
var y = 0;
while (element && !element.classList.contains('page-scroll')) {
y += element.offsetTop;
element = element.offsetParent;
}
IV.scrollTo(y, instant);
IV.scrollTo(IV.getElementTop(element), instant);
}
},
frameKeyDown: function (e) {
@ -103,6 +106,7 @@ var IV = {
const was = IV.lastScrollTop;
IV.lastScrollTop = IV.findPageScroll().scrollTop;
IV.updateJumpToTop(was < IV.lastScrollTop);
IV.checkVideos();
},
updateJumpToTop: function (scrolledDown) {
if (IV.lastScrollTop < 100) {
@ -246,9 +250,11 @@ var IV = {
IV.notify({ event: 'ready' });
IV.forceScrollFocus();
IV.frameScrolled();
},
initMedia: function () {
const photos = document.getElementsByClassName('photo');
var scroll = IV.findPageScroll();
const photos = scroll.getElementsByClassName('photo');
for (let i = 0; i < photos.length; ++i) {
const photo = photos[i];
if (photo.classList.contains('loaded')) {
@ -265,18 +271,70 @@ var IV = {
}
img.src = url.substr(5, url.length - 7);
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) {
const video = videos[i];
if (video.classList.contains('loaded')) {
continue;
const element = video.element;
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) {
@ -346,7 +404,14 @@ var IV = {
var to = article(IV.makeScrolledContent(data.html));
morphdom(from, to, {
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');
}
return !fromEl.isEqualNode(toEl);
@ -466,8 +531,7 @@ var IV = {
now.classList.add(back ? 'hidden-left' : 'hidden-right');
now.classList.remove(back ? 'hidden-right' : 'hidden-left');
now.firstChild.getAnimations().forEach(
(animation) => animation.finish());
IV.stopAnimations(now.firstChild);
if (!was.listening) {
was.listening = true;
@ -476,6 +540,10 @@ var IV = {
|| was.classList.contains('hidden-right')) {
if (was.parentNode) {
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.frameScrolled();
},
forceScrollFocus: function () {
IV.findPageScroll().focus();
@ -520,10 +589,17 @@ var IV = {
IV.findPageScroll().focus();
}, 100);
},
stopAnimations: function (element) {
element.getAnimations().forEach(
(animation) => animation.finish());
},
back: function () {
window.history.back();
},
videos: {},
videosPlaying: {},
cache: {},
index: 0,
position: 0
@ -533,6 +609,7 @@ document.onclick = IV.frameClickHandler;
document.onkeydown = IV.frameKeyDown;
document.onmouseenter = IV.frameMouseEnter;
document.onmouseup = IV.frameMouseUp;
document.onresize = IV.checkVideos;
window.onmessage = IV.postMessageHandler;
window.addEventListener('popstate', function (e) {
if (e.state) {

View File

@ -534,31 +534,13 @@ QByteArray Parser::block(
if (!video.id) {
return "Video not found.";
}
const auto src = documentFullUrl(video);
auto vattributes = Attributes{};
if (collageSmall) {
vattributes.push_back({ "class", "video-small" });
}
if (data.is_autoplay()) {
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" } }));
}
auto inner = tag("div", {
{ "class", "video" },
{ "data-src", documentFullUrl(video) },
{ "data-autoplay", data.is_autoplay() ? "1" : "0" },
{ "data-loop", data.is_loop() ? "1" : "0" },
{ "data-small", collageSmall ? "1" : "0" },
});
const auto minithumb = Images::ExpandInlineBytes(video.minithumbnail);
if (!minithumb.isEmpty()) {
const auto image = Images::Read({ .content = minithumb });