mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-03 04:25:55 +01:00
Fixing viewport shuffling due to image load
Introduced new method ".loadImages()" in posts client side lib to handle viewport height changes when loading images. Requires nodebb-plugin-markdown@5.0.0 @BenLubar @boomzillawtf
This commit is contained in:
@@ -47,7 +47,7 @@
|
|||||||
"nodebb-plugin-composer-default": "3.0.18",
|
"nodebb-plugin-composer-default": "3.0.18",
|
||||||
"nodebb-plugin-dbsearch": "1.0.1",
|
"nodebb-plugin-dbsearch": "1.0.1",
|
||||||
"nodebb-plugin-emoji-extended": "1.0.3",
|
"nodebb-plugin-emoji-extended": "1.0.3",
|
||||||
"nodebb-plugin-markdown": "4.0.17",
|
"nodebb-plugin-markdown": "5.0.0",
|
||||||
"nodebb-plugin-mentions": "1.0.21",
|
"nodebb-plugin-mentions": "1.0.21",
|
||||||
"nodebb-plugin-soundpack-default": "0.1.6",
|
"nodebb-plugin-soundpack-default": "0.1.6",
|
||||||
"nodebb-plugin-spam-be-gone": "0.4.6",
|
"nodebb-plugin-spam-be-gone": "0.4.6",
|
||||||
|
|||||||
21
public/less/global.less
Normal file
21
public/less/global.less
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
This stylesheet is applied to all themes and all pages.
|
||||||
|
They can be overridden by themes, though their presence (or initial settings) may be depended upon by
|
||||||
|
client-side logic in core.
|
||||||
|
|
||||||
|
==========
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Prevent viewport shuffling on image load by restricting image dimensions until viewed by the browser */
|
||||||
|
[component="post/content"] img:not(.not-responsive) {
|
||||||
|
height: 1rem;
|
||||||
|
opacity: 0;
|
||||||
|
transition: width 500ms ease;
|
||||||
|
transition: height 500ms ease;
|
||||||
|
transition: opacity 500ms ease;
|
||||||
|
|
||||||
|
&[data-state="loaded"] {
|
||||||
|
height: auto;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,12 +61,12 @@ define('forum/topic', [
|
|||||||
|
|
||||||
addParentHandler();
|
addParentHandler();
|
||||||
|
|
||||||
handleBookmark(tid);
|
|
||||||
|
|
||||||
handleKeys();
|
handleKeys();
|
||||||
|
|
||||||
navigator.init('[component="post/anchor"]', ajaxify.data.postcount, Topic.toTop, Topic.toBottom, Topic.navigatorCallback, Topic.calculateIndex);
|
navigator.init('[component="post/anchor"]', ajaxify.data.postcount, Topic.toTop, Topic.toBottom, Topic.navigatorCallback, Topic.calculateIndex);
|
||||||
|
|
||||||
|
handleBookmark(tid);
|
||||||
|
|
||||||
$(window).on('scroll', updateTopicTitle);
|
$(window).on('scroll', updateTopicTitle);
|
||||||
|
|
||||||
handleTopicSearch();
|
handleTopicSearch();
|
||||||
@@ -141,6 +141,7 @@ define('forum/topic', [
|
|||||||
return navigator.scrollToPostIndex(postIndex, true);
|
return navigator.scrollToPostIndex(postIndex, true);
|
||||||
}
|
}
|
||||||
} else if (bookmark && (!config.usePagination || (config.usePagination && ajaxify.data.pagination.currentPage === 1)) && ajaxify.data.postcount > 5) {
|
} else if (bookmark && (!config.usePagination || (config.usePagination && ajaxify.data.pagination.currentPage === 1)) && ajaxify.data.postcount > 5) {
|
||||||
|
navigator.update();
|
||||||
app.alert({
|
app.alert({
|
||||||
alert_id: 'bookmark',
|
alert_id: 'bookmark',
|
||||||
message: '[[topic:bookmark_instructions]]',
|
message: '[[topic:bookmark_instructions]]',
|
||||||
@@ -156,6 +157,8 @@ define('forum/topic', [
|
|||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
app.removeAlert('bookmark');
|
app.removeAlert('bookmark');
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
} else {
|
||||||
|
navigator.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +236,7 @@ define('forum/topic', [
|
|||||||
return index;
|
return index;
|
||||||
};
|
};
|
||||||
|
|
||||||
Topic.navigatorCallback = function(index, elementCount) {
|
Topic.navigatorCallback = function(index, elementCount, threshold) {
|
||||||
var path = ajaxify.removeRelativePath(window.location.pathname.slice(1));
|
var path = ajaxify.removeRelativePath(window.location.pathname.slice(1));
|
||||||
if (!path.startsWith('topic')) {
|
if (!path.startsWith('topic')) {
|
||||||
return 1;
|
return 1;
|
||||||
@@ -248,13 +251,13 @@ define('forum/topic', [
|
|||||||
newUrl += '/' + index;
|
newUrl += '/' + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
posts.loadImages(threshold);
|
||||||
|
|
||||||
if (newUrl !== currentUrl) {
|
if (newUrl !== currentUrl) {
|
||||||
if (Topic.replaceURLTimeout) {
|
if (Topic.replaceURLTimeout) {
|
||||||
clearTimeout(Topic.replaceURLTimeout);
|
clearTimeout(Topic.replaceURLTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
Topic.replaceURLTimeout = setTimeout(function() {
|
Topic.replaceURLTimeout = setTimeout(function() {
|
||||||
|
|
||||||
updateUserBookmark(index);
|
updateUserBookmark(index);
|
||||||
|
|
||||||
Topic.replaceURLTimeout = 0;
|
Topic.replaceURLTimeout = 0;
|
||||||
|
|||||||
@@ -231,6 +231,66 @@ define('forum/topic/posts', [
|
|||||||
hidePostToolsForDeletedPosts(posts);
|
hidePostToolsForDeletedPosts(posts);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Posts.loadImages = function(threshold) {
|
||||||
|
/*
|
||||||
|
If threshold is defined, images loaded above this threshold will modify
|
||||||
|
the user's scroll position so they are not scrolled away from content
|
||||||
|
they were reading. Images loaded below this threshold will push down content.
|
||||||
|
|
||||||
|
If no threshold is defined, loaded images will push down content, as per
|
||||||
|
default
|
||||||
|
*/
|
||||||
|
|
||||||
|
var images = components.get('post/content').find('img[data-state="unloaded"]'),
|
||||||
|
visible = images.filter(function() {
|
||||||
|
return utils.isElementInViewport(this);
|
||||||
|
}),
|
||||||
|
scrollTop = $(window).scrollTop(),
|
||||||
|
adjusting = false,
|
||||||
|
adjustQueue = [],
|
||||||
|
adjustPosition = function() {
|
||||||
|
adjusting = true;
|
||||||
|
oldHeight = document.body.clientHeight;
|
||||||
|
|
||||||
|
// Display the image
|
||||||
|
$(this).attr('data-state', 'loaded');
|
||||||
|
newHeight = document.body.clientHeight;
|
||||||
|
|
||||||
|
var imageRect = this.getBoundingClientRect();
|
||||||
|
if (imageRect.top < threshold) {
|
||||||
|
scrollTop = scrollTop + (newHeight - oldHeight);
|
||||||
|
$(window).scrollTop(scrollTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adjustQueue.length) {
|
||||||
|
adjustQueue.pop()();
|
||||||
|
} else {
|
||||||
|
adjusting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
oldHeight, newHeight;
|
||||||
|
|
||||||
|
// For each image, reset the source and adjust scrollTop when loaded
|
||||||
|
visible.attr('data-state', 'loading');
|
||||||
|
visible.each(function(index, image) {
|
||||||
|
image = $(image);
|
||||||
|
|
||||||
|
image.on('load', function() {
|
||||||
|
if (!adjusting) {
|
||||||
|
adjustPosition.call(this);
|
||||||
|
} else {
|
||||||
|
adjustQueue.push(adjustPosition.bind(this));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
image.attr('src', image.attr('data-src'));
|
||||||
|
if (image.parent().attr('href')) {
|
||||||
|
image.parent().attr('href', image.attr('data-src'));
|
||||||
|
}
|
||||||
|
image.removeAttr('data-src');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Posts.wrapImagesInLinks = function(posts) {
|
Posts.wrapImagesInLinks = function(posts) {
|
||||||
posts.find('[component="post/content"] img:not(.emoji)').each(function() {
|
posts.find('[component="post/content"] img:not(.emoji)').each(function() {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/* globals app, define, ajaxify, utils, config */
|
/* globals define, ajaxify, utils, config */
|
||||||
|
|
||||||
|
|
||||||
define('navigator', ['forum/pagination', 'components'], function(pagination, components) {
|
define('navigator', ['forum/pagination', 'components'], function(pagination, components) {
|
||||||
@@ -56,7 +56,6 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com
|
|||||||
});
|
});
|
||||||
|
|
||||||
navigator.setCount(count);
|
navigator.setCount(count);
|
||||||
navigator.update();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function generateUrl(index) {
|
function generateUrl(index) {
|
||||||
@@ -92,7 +91,13 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com
|
|||||||
$('.pagination-block').toggleClass('ready', flag);
|
$('.pagination-block').toggleClass('ready', flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.update = function() {
|
navigator.update = function(threshold) {
|
||||||
|
threshold = typeof threshold === 'number' ? threshold : undefined;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The "threshold" is defined as the distance from the top of the page to
|
||||||
|
a spot where a user is expecting to begin reading.
|
||||||
|
*/
|
||||||
var els = $(navigator.selector);
|
var els = $(navigator.selector);
|
||||||
if (els.length) {
|
if (els.length) {
|
||||||
index = parseInt(els.first().attr('data-index'), 10) + 1;
|
index = parseInt(els.first().attr('data-index'), 10) + 1;
|
||||||
@@ -114,7 +119,7 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (typeof navigator.callback === 'function') {
|
if (typeof navigator.callback === 'function') {
|
||||||
navigator.callback(index, count);
|
navigator.callback(index, count, threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.updateTextAndProgressBar();
|
navigator.updateTextAndProgressBar();
|
||||||
@@ -202,6 +207,9 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporarily disable navigator update on scroll
|
||||||
|
$(window).off('scroll', navigator.update);
|
||||||
|
|
||||||
duration = duration !== undefined ? duration : 400;
|
duration = duration !== undefined ? duration : 400;
|
||||||
navigator.scrollActive = true;
|
navigator.scrollActive = true;
|
||||||
var done = false;
|
var done = false;
|
||||||
@@ -209,21 +217,24 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com
|
|||||||
function animateScroll() {
|
function animateScroll() {
|
||||||
var scrollTop = 0;
|
var scrollTop = 0;
|
||||||
if (postHeight < viewportHeight) {
|
if (postHeight < viewportHeight) {
|
||||||
scrollTop = (scrollTo.offset().top - (viewportHeight / 2) + (postHeight / 2)) + 'px';
|
scrollTop = (scrollTo.offset().top - (viewportHeight / 2) + (postHeight / 2));
|
||||||
} else {
|
} else {
|
||||||
scrollTop = scrollTo.offset().top - navbarHeight;
|
scrollTop = scrollTo.offset().top - navbarHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('html, body').animate({
|
$('html, body').animate({
|
||||||
scrollTop: scrollTop
|
scrollTop: scrollTop + 'px'
|
||||||
}, duration, function() {
|
}, duration, function() {
|
||||||
if (done) {
|
if (done) {
|
||||||
|
// Re-enable onScroll behaviour
|
||||||
|
$(window).on('scroll', navigator.update);
|
||||||
|
var scrollToRect = scrollTo.get(0).getBoundingClientRect();
|
||||||
|
navigator.update(scrollToRect.top);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
done = true;
|
done = true;
|
||||||
|
|
||||||
navigator.scrollActive = false;
|
navigator.scrollActive = false;
|
||||||
navigator.update();
|
|
||||||
highlightPost();
|
highlightPost();
|
||||||
$('body').scrollTop($('body').scrollTop() - 1);
|
$('body').scrollTop($('body').scrollTop() - 1);
|
||||||
$('html').scrollTop($('html').scrollTop() - 1);
|
$('html').scrollTop($('html').scrollTop() - 1);
|
||||||
|
|||||||
@@ -309,6 +309,23 @@
|
|||||||
return labels;
|
return labels;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/* Retrieved from http://stackoverflow.com/a/7557433 @ 27 Mar 2016 */
|
||||||
|
isElementInViewport: function(el) {
|
||||||
|
//special bonus for those using jQuery
|
||||||
|
if (typeof jQuery === "function" && el instanceof jQuery) {
|
||||||
|
el = el[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var rect = el.getBoundingClientRect();
|
||||||
|
|
||||||
|
return (
|
||||||
|
rect.top >= 0 &&
|
||||||
|
rect.left >= 0 &&
|
||||||
|
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
|
||||||
|
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
// get all the url params in a single key/value hash
|
// get all the url params in a single key/value hash
|
||||||
params: function(options) {
|
params: function(options) {
|
||||||
var a, hash = {}, params;
|
var a, hash = {}, params;
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ module.exports = function(Meta) {
|
|||||||
source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/blacklist.less";';
|
source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/blacklist.less";';
|
||||||
source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/generics.less";';
|
source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/generics.less";';
|
||||||
source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/mixins.less";';
|
source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/mixins.less";';
|
||||||
|
source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/global.less";';
|
||||||
|
|
||||||
var acpSource = '\n@import "..' + path.sep + 'public/less/admin/admin";\n' + source;
|
var acpSource = '\n@import "..' + path.sep + 'public/less/admin/admin";\n' + source;
|
||||||
acpSource += '\n@import "..' + path.sep + 'public/less/generics.less";';
|
acpSource += '\n@import "..' + path.sep + 'public/less/generics.less";';
|
||||||
|
|||||||
Reference in New Issue
Block a user