Improve syncscroll performance and accuracy with few UX tweaks

This commit is contained in:
Cheng-Han, Wu 2016-05-29 13:58:32 +08:00
parent 20fbc9957f
commit 2c60f0dd67
2 changed files with 64 additions and 60 deletions

View file

@ -713,6 +713,14 @@ $(window).error(function () {
//setNeedRefresh(); //setNeedRefresh();
}); });
function autoSyncscroll() {
if (editorHasFocus()) {
syncScrollToView();
} else {
syncScrollToEdit();
}
}
var windowResizeDebounce = 200; var windowResizeDebounce = 200;
var windowResize = _.debounce(windowResizeInner, windowResizeDebounce); var windowResize = _.debounce(windowResizeInner, windowResizeDebounce);
@ -727,11 +735,7 @@ function windowResizeInner(callback) {
if (editor.getOption('scrollbarStyle') === 'native') { if (editor.getOption('scrollbarStyle') === 'native') {
setTimeout(function () { setTimeout(function () {
clearMap(); clearMap();
if (editorHasFocus()) { autoSyncscroll();
syncScrollToView();
} else {
syncScrollToEdit();
}
updateScrollspy(); updateScrollspy();
if (callback && typeof callback === 'function') if (callback && typeof callback === 'function')
callback(); callback();
@ -741,11 +745,7 @@ function windowResizeInner(callback) {
editor.setOption('viewportMargin', Infinity); editor.setOption('viewportMargin', Infinity);
setTimeout(function () { setTimeout(function () {
clearMap(); clearMap();
if (editorHasFocus()) { autoSyncscroll();
syncScrollToView();
} else {
syncScrollToEdit();
}
editor.setOption('viewportMargin', viewportMargin); editor.setOption('viewportMargin', viewportMargin);
//add or update user cursors //add or update user cursors
for (var i = 0; i < onlineUsers.length; i++) { for (var i = 0; i < onlineUsers.length; i++) {
@ -1029,12 +1029,12 @@ function changeMode(type) {
if (lastMode == modeType.view && currentMode == modeType.both) { if (lastMode == modeType.view && currentMode == modeType.both) {
preventSyncScrollToView = 2; preventSyncScrollToView = 2;
syncScrollToEdit(); syncScrollToEdit(null, true);
} }
if (lastMode == modeType.edit && currentMode == modeType.both) { if (lastMode == modeType.edit && currentMode == modeType.both) {
preventSyncScrollToEdit = 2; preventSyncScrollToEdit = 2;
syncScrollToView(); syncScrollToView(null, true);
} }
if (lastMode == modeType.both && currentMode != modeType.both) { if (lastMode == modeType.both && currentMode != modeType.both) {
@ -2458,10 +2458,10 @@ editor.on('beforeChange', function (cm, change) {
cmClient.editorAdapter.ignoreNextChange = true; cmClient.editorAdapter.ignoreNextChange = true;
}); });
editor.on('cut', function () { editor.on('cut', function () {
windowResize(); //workaround for scrollMap //na
}); });
editor.on('paste', function () { editor.on('paste', function () {
windowResize(); //workaround for scrollMap //na
}); });
editor.on('changes', function (cm, changes) { editor.on('changes', function (cm, changes) {
updateHistory(); updateHistory();
@ -2630,11 +2630,7 @@ function updateViewInner() {
clearMap(); clearMap();
//buildMap(); //buildMap();
updateTitleReminder(); updateTitleReminder();
if (editorHasFocus()) { autoSyncscroll();
syncScrollToView();
} else {
syncScrollToEdit();
}
} }
var updateHistoryDebounce = 600; var updateHistoryDebounce = 600;

View file

@ -110,7 +110,7 @@ var syncscroll = true;
var preventSyncScrollToEdit = false; var preventSyncScrollToEdit = false;
var preventSyncScrollToView = false; var preventSyncScrollToView = false;
var editScrollThrottle = 2; var editScrollThrottle = 10;
var viewScrollThrottle = 10; var viewScrollThrottle = 10;
var buildMapThrottle = 100; var buildMapThrottle = 100;
@ -214,7 +214,7 @@ function buildMapInner(callback) {
// sync view scroll progress to edit // sync view scroll progress to edit
var viewScrollingTimer = null; var viewScrollingTimer = null;
function syncScrollToEdit(e) { function syncScrollToEdit(event, preventAnimate) {
if (currentMode != modeType.both || !syncscroll) return; if (currentMode != modeType.both || !syncscroll) return;
if (preventSyncScrollToEdit) { if (preventSyncScrollToEdit) {
if (typeof preventSyncScrollToEdit === 'number') { if (typeof preventSyncScrollToEdit === 'number') {
@ -225,7 +225,9 @@ function syncScrollToEdit(e) {
return; return;
} }
if (!scrollMap || !lineHeightMap) { if (!scrollMap || !lineHeightMap) {
buildMap(syncScrollToEdit); buildMap(function () {
syncScrollToEdit(event, preventAnimate);
});
return; return;
} }
if (editScrolling) return; if (editScrolling) return;
@ -263,24 +265,28 @@ function syncScrollToEdit(e) {
posTo = preLastLineHeight; posTo = preLastLineHeight;
topDiffPercent = (scrollTop - preLastLinePos) / (viewBottom - preLastLinePos); topDiffPercent = (scrollTop - preLastLinePos) / (viewBottom - preLastLinePos);
posToNextDiff = textHeight * topDiffPercent; posToNextDiff = textHeight * topDiffPercent;
posTo += Math.floor(posToNextDiff); posTo += Math.ceil(posToNextDiff);
} else { } else {
posTo = lineNo * textHeight; posTo = lineNo * textHeight;
topDiffPercent = (scrollTop - scrollMap[lineNo]) / (scrollMap[lineNo + lineDiff] - scrollMap[lineNo]); topDiffPercent = (scrollTop - scrollMap[lineNo]) / (scrollMap[lineNo + lineDiff] - scrollMap[lineNo]);
posToNextDiff = textHeight * lineDiff * topDiffPercent; posToNextDiff = textHeight * lineDiff * topDiffPercent;
posTo += Math.floor(posToNextDiff); posTo += Math.ceil(posToNextDiff);
} }
var posDiff = Math.abs(scrollInfo.top - posTo); if (preventAnimate) {
var duration = posDiff / 50; ui.area.codemirrorScroll.scrollTop(posTo);
duration = duration >= 100 ? duration : 100; } else {
ui.area.codemirrorScroll.stop(true, true).animate({ var posDiff = Math.abs(scrollInfo.top - posTo);
scrollTop: posTo var duration = posDiff / 50;
}, duration, "linear"); duration = duration >= 100 ? duration : 100;
ui.area.codemirrorScroll.stop(true, true).animate({
scrollTop: posTo
}, duration, "linear");
}
viewScrolling = true; viewScrolling = true;
clearTimeout(viewScrollingTimer); clearTimeout(viewScrollingTimer);
viewScrollingTimer = setTimeout(viewScrollingTimeoutInner, duration * 1.2); viewScrollingTimer = setTimeout(viewScrollingTimeoutInner, duration * 1.5);
} }
function viewScrollingTimeoutInner() { function viewScrollingTimeoutInner() {
@ -290,7 +296,7 @@ function viewScrollingTimeoutInner() {
// sync edit scroll progress to view // sync edit scroll progress to view
var editScrollingTimer = null; var editScrollingTimer = null;
function syncScrollToView(event, _lineNo) { function syncScrollToView(event, preventAnimate) {
if (currentMode != modeType.both || !syncscroll) return; if (currentMode != modeType.both || !syncscroll) return;
if (preventSyncScrollToView) { if (preventSyncScrollToView) {
if (typeof preventSyncScrollToView === 'number') { if (typeof preventSyncScrollToView === 'number') {
@ -301,44 +307,46 @@ function syncScrollToView(event, _lineNo) {
return; return;
} }
if (!scrollMap || !lineHeightMap) { if (!scrollMap || !lineHeightMap) {
buildMap(syncScrollToView); buildMap(function () {
syncScrollToView(event, preventAnimate);
});
return; return;
} }
if (viewScrolling) return; if (viewScrolling) return;
if (!_lineNo) { var lineNo, posTo;
var lineNo, posTo; var topDiffPercent, posToNextDiff;
var topDiffPercent, posToNextDiff; var scrollInfo = editor.getScrollInfo();
var scrollInfo = editor.getScrollInfo(); var textHeight = editor.defaultTextHeight();
var textHeight = editor.defaultTextHeight(); lineNo = Math.floor(scrollInfo.top / textHeight);
lineNo = Math.floor(scrollInfo.top / textHeight); // if reach the last line, will start lerp to the bottom
// if reach the last line, will start lerp to the bottom var diffToBottom = (scrollInfo.top + scrollInfo.clientHeight) - (scrollInfo.height - textHeight);
var diffToBottom = (scrollInfo.top + scrollInfo.clientHeight) - (scrollInfo.height - textHeight); if (scrollInfo.height > scrollInfo.clientHeight && diffToBottom > 0) {
if (scrollInfo.height > scrollInfo.clientHeight && diffToBottom > 0) { topDiffPercent = diffToBottom / textHeight;
topDiffPercent = diffToBottom / textHeight; posTo = scrollMap[lineNo + 1];
posTo = scrollMap[lineNo + 1]; posToNextDiff = (viewBottom - posTo) * topDiffPercent;
posToNextDiff = (viewBottom - posTo) * topDiffPercent; posTo += Math.floor(posToNextDiff);
posTo += Math.floor(posToNextDiff);
} else {
topDiffPercent = (scrollInfo.top % textHeight) / textHeight;
posTo = scrollMap[lineNo];
posToNextDiff = (scrollMap[lineNo + 1] - posTo) * topDiffPercent;
posTo += Math.floor(posToNextDiff);
}
} else { } else {
posTo = scrollMap[lineHeightMap[_lineNo]]; topDiffPercent = (scrollInfo.top % textHeight) / textHeight;
posTo = scrollMap[lineNo];
posToNextDiff = (scrollMap[lineNo + 1] - posTo) * topDiffPercent;
posTo += Math.floor(posToNextDiff);
} }
var posDiff = Math.abs(ui.area.view.scrollTop() - posTo); if (preventAnimate) {
var duration = posDiff / 50; ui.area.view.scrollTop(posTo);
duration = duration >= 100 ? duration : 100; } else {
ui.area.view.stop(true, true).animate({ var posDiff = Math.abs(ui.area.view.scrollTop() - posTo);
scrollTop: posTo var duration = posDiff / 50;
}, duration, "linear"); duration = duration >= 100 ? duration : 100;
ui.area.view.stop(true, true).animate({
scrollTop: posTo
}, duration, "linear");
}
editScrolling = true; editScrolling = true;
clearTimeout(editScrollingTimer); clearTimeout(editScrollingTimer);
editScrollingTimer = setTimeout(editScrollingTimeoutInner, duration * 1.2); editScrollingTimer = setTimeout(editScrollingTimeoutInner, duration * 1.5);
} }
function editScrollingTimeoutInner() { function editScrollingTimeoutInner() {