FIX: remove scrolling jankiness

This commit is contained in:
Régis Hanol 2018-03-24 02:39:36 +01:00
parent 25bf5278e0
commit b1a799b526
4 changed files with 25 additions and 46 deletions

View File

@ -18,13 +18,15 @@ RawHandlebars.helpers['get'] = function(context, options) {
var firstContext = options.contexts[0]; var firstContext = options.contexts[0];
var val = firstContext[context]; var val = firstContext[context];
if (context.indexOf('controller') === 0) { if (context.indexOf('controller.') === 0) {
context = context.replace(/^controller\./, ''); context = context.slice(context.indexOf('.') + 1);
} }
if (val && val.isDescriptor) { return Em.get(firstContext, context); } if (val && val.isDescriptor) {
val = val === undefined ? Em.get(firstContext, context): val; return Em.get(firstContext, context);
return val; }
return val === undefined ? Em.get(firstContext, context) : val;
}; };
// adds compatability so this works with stringParams // adds compatability so this works with stringParams

View File

@ -93,7 +93,7 @@ export default MountWidget.extend({
let percent = null; let percent = null;
const offset = offsetCalculator(); const offset = offsetCalculator();
const topCheck = Math.ceil(windowTop + offset); const topCheck = Math.ceil(windowTop + offset + 5);
// uncomment to debug the eyeline // uncomment to debug the eyeline
/* /*

View File

@ -1,13 +1,13 @@
import { scrollTopFor } from 'discourse/lib/offset-calculator'; import { scrollTopFor } from 'discourse/lib/offset-calculator';
// Dear traveller, you are entering a zone where we are at war with the browser // Dear traveller, you are entering a zone where we are at war with the browser.
// the browser is insisting on positioning scrollTop per the location it was in // The browser is insisting on positioning scrollTop per the location it was in
// the past, we are insisting on it being where we want it to be // the past, we are insisting on it being where we want it to be.
// The hack is just to keep trying over and over to position the scrollbar (up to 1 minute) // The hack is just to keep trying over and over to position the scrollbar (up to 1 second).
// //
// The root cause is that a "refresh" on a topic page will almost never be at the // The root cause is that a "refresh" on a topic page will almost never be at the
// same position it was in the past, the URL points to the post at the top of the // same position it was in the past, the URL points to the post at the top of the
// page, so a refresh will try to bring that post into view causing drift // page, so a refresh will try to bring that post into view causing drift.
// //
// Additionally if you loaded multiple batches of posts, on refresh they will not // Additionally if you loaded multiple batches of posts, on refresh they will not
// be loaded. // be loaded.
@ -19,6 +19,7 @@ import { scrollTopFor } from 'discourse/lib/offset-calculator';
// 2. give up on the scrollbar and implement it ourselves (something that will happen) // 2. give up on the scrollbar and implement it ourselves (something that will happen)
const SCROLL_EVENTS = "scroll.lock-on touchmove.lock-on mousedown.lock-on wheel.lock-on DOMMouseScroll.lock-on mousewheel.lock-on keyup.lock-on"; const SCROLL_EVENTS = "scroll.lock-on touchmove.lock-on mousedown.lock-on wheel.lock-on DOMMouseScroll.lock-on mousewheel.lock-on keyup.lock-on";
const SCROLL_TYPES = ["mousedown", "mousewheel", "touchmove", "wheel"];
function within(threshold, x, y) { function within(threshold, x, y) {
return Math.abs(x-y) < threshold; return Math.abs(x-y) < threshold;
@ -34,8 +35,7 @@ export default class LockOn {
elementTop() { elementTop() {
const selected = $(this.selector); const selected = $(this.selector);
if (selected && selected.offset && selected.offset()) { if (selected && selected.offset && selected.offset()) {
const result = selected.offset().top; return scrollTopFor(selected.offset().top);
return scrollTopFor(result);
} }
} }
@ -54,9 +54,7 @@ export default class LockOn {
$(window).scrollTop(previousTop); $(window).scrollTop(previousTop);
const interval = setInterval(() => { const interval = setInterval(() => {
let top = this.elementTop(); const top = Math.max(0, this.elementTop());
if (top < 0) { top = 0; }
const scrollTop = $(window).scrollTop(); const scrollTop = $(window).scrollTop();
if (typeof(top) === "undefined" || isNaN(top)) { if (typeof(top) === "undefined" || isNaN(top)) {
@ -68,20 +66,14 @@ export default class LockOn {
previousTop = top; previousTop = top;
} }
// We commit suicide after 3s just to clean up // Commit suicide after a little while
const nowTime = new Date().getTime(); if (new Date().getTime() - startedAt > 1000) {
if (nowTime - startedAt > 1000) {
return this.clearLock(interval); return this.clearLock(interval);
} }
}, 50); }, 50);
$('body,html').off(SCROLL_EVENTS).on(SCROLL_EVENTS, e => { $('body,html').off(SCROLL_EVENTS).on(SCROLL_EVENTS, e => {
if ( e.which > 0 || if (e.which > 0 || SCROLL_TYPES.includes(e.type)) {
e.type === "mousedown" ||
e.type === "mousewheel" ||
e.type === "touchmove" ||
e.type === "wheel"
) {
this.clearLock(interval); this.clearLock(interval);
} }
}); });

View File

@ -5,34 +5,19 @@ export function scrollTopFor(y) {
export default function offsetCalculator(y) { export default function offsetCalculator(y) {
const $header = $('header'); const $header = $('header');
const $container = $('.posts-wrapper'); const $container = $('.posts-wrapper');
const containerOffset = $container.offset();
let titleHeight = 0;
const scrollTop = y || $(window).scrollTop(); const scrollTop = y || $(window).scrollTop();
const titleHeight = $('#topic-title').height() || 0;
if (!containerOffset || scrollTop < containerOffset.top) {
titleHeight = $('#topic-title').height() || 0;
}
const rawWinHeight = $(window).height(); const rawWinHeight = $(window).height();
const windowHeight = rawWinHeight - titleHeight; const expectedOffset = titleHeight - ($header.find('.contents').height() || 0);
const eyeTarget = (windowHeight / 10);
const headerHeight = $header.outerHeight(true);
const expectedOffset = titleHeight - ($header.find('.contents').height() || 0) + (eyeTarget * 2);
const ideal = headerHeight + ((expectedOffset < 0) ? 0 : expectedOffset);
if ($container.length === 0) { return expectedOffset; } if ($container.length === 0) { return expectedOffset; }
const headerHeight = $header.outerHeight(true);
const ideal = headerHeight + Math.max(0, expectedOffset);
const topPos = $container.offset().top; const topPos = $container.offset().top;
const docHeight = $(document).height(); const docHeight = $(document).height();
let scrollPercent = Math.min((scrollTop / (docHeight-rawWinHeight)), 1.0); const scrollPercent = Math.min(scrollTop / (docHeight - rawWinHeight), 1.0);
const inter = Math.min(headerHeight, topPos - scrollTop + ($container.height() * scrollPercent));
let inter = topPos - scrollTop + ($container.height() * scrollPercent);
if (inter < headerHeight + eyeTarget) {
inter = headerHeight + eyeTarget;
}
if (inter > ideal) { if (inter > ideal) {
const bottom = $('#topic-bottom').offset().top; const bottom = $('#topic-bottom').offset().top;