FIX: Much less jankiness on the scroller

This commit is contained in:
Robin Ward 2016-05-30 15:48:06 -04:00
parent 717999b302
commit 5fc47e6942
No known key found for this signature in database
GPG Key ID: 0E091E2B4ED1B83D
4 changed files with 46 additions and 24 deletions

View File

@ -1,3 +1,5 @@
import { scrollTopFor } from 'discourse/lib/offset-calculator';
// 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 past, we are insisting on it being where we want it to be
@ -30,14 +32,10 @@ export default class LockOn {
}
elementTop() {
const offsetCalculator = this.options.offsetCalculator;
if (this.offsetTop === null) {
this.offsetTop = offsetCalculator ? offsetCalculator() : 0;
}
const selected = $(this.selector);
if (selected && selected.offset && selected.offset()) {
return selected.offset().top - this.offsetTop;
const result = selected.offset().top;
return result - Math.round(scrollTopFor(result));
}
}
@ -53,10 +51,11 @@ export default class LockOn {
$(window).scrollTop(previousTop);
let i = 0;
const interval = setInterval(() => {
i = i + 1;
const top = this.elementTop();
let top = this.elementTop();
const scrollTop = $(window).scrollTop();
if (typeof(top) === "undefined" || isNaN(top)) {

View File

@ -1,4 +1,14 @@
export default function offsetCalculator() {
// TODO: This is quite ugly but seems reasonably fast? Maybe refactor
// this out before we merge into stable.
export function scrollTopFor(y) {
let off = 0;
for (let i=0; i<3; i++) {
off = offsetCalculator(y - off);
}
return off;
}
export default function offsetCalculator(y) {
const $header = $('header');
const $title = $('#topic-title');
const rawWinHeight = $(window).height();
@ -9,7 +19,7 @@ export default function offsetCalculator() {
const $container = $('.posts-wrapper');
const topPos = $container.offset().top;
const scrollTop = $(window).scrollTop();
const scrollTop = y || $(window).scrollTop();
const docHeight = $(document).height();
const scrollPercent = (scrollTop / (docHeight-rawWinHeight));
@ -17,7 +27,11 @@ export default function offsetCalculator() {
if (inter > ideal) {
const bottom = $('#topic-bottom').offset().top;
if (bottom > (scrollTop + rawWinHeight)) {
const switchPos = bottom - rawWinHeight;
if (scrollTop > switchPos) {
const p = Math.max(Math.min((scrollTop + inter - switchPos) / rawWinHeight, 1.0), 0.0);
return ((1 - p) * ideal) + (p * inter);
} else {
return ideal;
}
}

View File

@ -23,7 +23,7 @@ const DiscourseURL = Ember.Object.extend({
return;
}
const lockon = new LockOn(holderId, { offsetCalculator });
const lockon = new LockOn(holderId);
const holder = $(holderId);
if (holder.length > 0 && opts && opts.skipIfOnScreen){

View File

@ -7,6 +7,10 @@ const SCROLLAREA_HEIGHT = 300;
const SCROLLER_HEIGHT = 50;
const SCROLLAREA_REMAINING = SCROLLAREA_HEIGHT - SCROLLER_HEIGHT;
function clamp(p, min=0.0, max=1.0) {
return Math.max(Math.min(p, max), min);
}
createWidget('timeline-last-read', {
tagName: 'div.timeline-last-read',
@ -96,10 +100,8 @@ createWidget('timeline-scrollarea', {
const topic = attrs.topic;
const postStream = topic.get('postStream');
const total = postStream.get('filteredPostsCount');
let current = Math.floor(total * percentage) + 1;
if (current < 1) { current = 1; }
if (current > total) { current = total; }
const current = clamp(Math.floor(total * percentage) + 1, 1, total);
const daysAgo = postStream.closestDaysAgoFor(current);
const date = new Date();
@ -155,15 +157,24 @@ createWidget('timeline-scrollarea', {
const $area = $('.timeline-scrollarea');
const areaTop = $area.offset().top;
let percentage = parseFloat(y - areaTop) / $area.height();
if (percentage > 1.0) { percentage = 1.0; };
if (percentage < 0.0) { percentage = 0.0; };
const topic = this.attrs.topic;
const postStream = topic.get('postStream');
const total = postStream.get('filteredPostsCount');
const percentage = clamp(parseFloat(y - areaTop) / $area.height());
const current = clamp(Math.floor(total * percentage) + 1, 1, total);
this.state.percentage = percentage;
// If viewing the last post consider it 100%
if (current === total) {
this.state.percentage = 1.0;
} else {
this.state.percentage = this._percentFor(topic, parseFloat(current));
}
},
commit() {
const position = this.position();
this.state.scrolledPost = position.current;
this.sendWidgetAction('jumpToIndex', position.current);
},
@ -171,16 +182,14 @@ createWidget('timeline-scrollarea', {
const { postIndex, percent } = event;
// If the post number didn't change keep our scroll position
this.state.percentage = this._percentFor(this.attrs.topic, parseFloat(postIndex) + percent);
if (this.state.scrolledPost !== postIndex) {
this.state.percentage = this._percentFor(this.attrs.topic, parseFloat(postIndex) + percent);
}
},
_percentFor(topic, postIndex) {
const total = topic.get('postStream.filteredPostsCount');
let result = parseFloat(postIndex - 1.0) / total;
if (result < 0) { return 0.0; }
if (result > 1.0) { return 1.0; }
return result;
return clamp(parseFloat(postIndex - 1.0) / total);
}
});