Update to improve sync scroll to edit performance and fix sync scroll behavior on many situations

This commit is contained in:
Cheng-Han, Wu 2016-05-27 00:12:07 +08:00
parent 954137b760
commit e9b0ce8e04
2 changed files with 70 additions and 33 deletions

View file

@ -722,8 +722,11 @@ function windowResizeInner(callback) {
if (loaded) { if (loaded) {
if (editor.getOption('scrollbarStyle') === 'native') { if (editor.getOption('scrollbarStyle') === 'native') {
clearMap(); clearMap();
syncScrollToView(); if (editorHasFocus()) {
syncScrollToEdit(); syncScrollToView();
} else {
syncScrollToEdit();
}
updateScrollspy(); updateScrollspy();
if (callback && typeof callback === 'function') if (callback && typeof callback === 'function')
callback(); callback();
@ -732,8 +735,11 @@ function windowResizeInner(callback) {
editor.setOption('viewportMargin', Infinity); editor.setOption('viewportMargin', Infinity);
setTimeout(function () { setTimeout(function () {
clearMap(); clearMap();
syncScrollToView(); if (editorHasFocus()) {
syncScrollToEdit(); 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++) {
@ -774,6 +780,7 @@ function checkResponsive() {
} }
var lastEditorWidth = 0; var lastEditorWidth = 0;
var previousFocusOnEditor = null;
function checkEditorStyle() { function checkEditorStyle() {
var desireHeight = statusBar ? (ui.area.edit.height() - statusBar.outerHeight()) : ui.area.edit.height(); var desireHeight = statusBar ? (ui.area.edit.height() - statusBar.outerHeight()) : ui.area.edit.height();
@ -806,6 +813,11 @@ function checkEditorStyle() {
} }
if (!ui.area.resize.syncToggle.length) { if (!ui.area.resize.syncToggle.length) {
ui.area.resize.syncToggle = $('<button class="btn btn-lg btn-default ui-sync-toggle" title="Toggle sync scrolling"><i class="fa fa-link fa-fw"></i></button>'); ui.area.resize.syncToggle = $('<button class="btn btn-lg btn-default ui-sync-toggle" title="Toggle sync scrolling"><i class="fa fa-link fa-fw"></i></button>');
ui.area.resize.syncToggle.hover(function () {
previousFocusOnEditor = editorHasFocus();
}, function () {
previousFocusOnEditor = null;
});
ui.area.resize.syncToggle.click(function () { ui.area.resize.syncToggle.click(function () {
syncscroll = !syncscroll; syncscroll = !syncscroll;
checkSyncToggle(); checkSyncToggle();
@ -822,6 +834,13 @@ function checkEditorStyle() {
function checkSyncToggle() { function checkSyncToggle() {
if (syncscroll) { if (syncscroll) {
if (previousFocusOnEditor) {
preventSyncScrollToView = false;
syncScrollToView();
} else {
preventSyncScrollToEdit = false;
syncScrollToEdit();
}
ui.area.resize.syncToggle.find('i').removeClass('fa-unlink').addClass('fa-link'); ui.area.resize.syncToggle.find('i').removeClass('fa-unlink').addClass('fa-link');
} else { } else {
ui.area.resize.syncToggle.find('i').removeClass('fa-link').addClass('fa-unlink'); ui.area.resize.syncToggle.find('i').removeClass('fa-link').addClass('fa-unlink');
@ -1003,9 +1022,14 @@ function changeMode(type) {
restoreInfo(); restoreInfo();
if (lastMode == modeType.view && currentMode == modeType.both) { if (lastMode == modeType.view && currentMode == modeType.both) {
preventSyncScrollToView = true; preventSyncScrollToView = 2;
syncScrollToEdit(); syncScrollToEdit();
} }
if (lastMode == modeType.edit && currentMode == modeType.both) {
preventSyncScrollToEdit = 2;
syncScrollToView();
}
if (lastMode != modeType.edit && currentMode == modeType.edit) { if (lastMode != modeType.edit && currentMode == modeType.edit) {
editor.refresh(); editor.refresh();
@ -2595,8 +2619,11 @@ function updateViewInner() {
clearMap(); clearMap();
//buildMap(); //buildMap();
updateTitleReminder(); updateTitleReminder();
syncScrollToView(); if (editorHasFocus()) {
syncScrollToEdit(); syncScrollToView();
} else {
syncScrollToEdit();
}
} }
var updateHistoryDebounce = 600; var updateHistoryDebounce = 600;

View file

@ -110,17 +110,15 @@ var syncscroll = true;
var preventSyncScrollToEdit = false; var preventSyncScrollToEdit = false;
var preventSyncScrollToView = false; var preventSyncScrollToView = false;
var editScrollThrottle = 1; var editScrollThrottle = 2;
var viewScrollThrottle = 10; var viewScrollThrottle = 10;
var buildMapThrottle = 100; var buildMapThrottle = 100;
var viewScrolling = false; var viewScrolling = false;
var viewScrollingDelay = 200; var viewScrollingDebounce = 200;
var viewScrollingTimer = null;
var editScrolling = false; var editScrolling = false;
var editScrollingDelay = 100; var editScrollingDebounce = 200;
var editScrollingTimer = null;
if (editor.getOption('scrollbarStyle') === 'native') { if (editor.getOption('scrollbarStyle') === 'native') {
ui.area.codemirrorScroll.on('scroll', _.throttle(syncScrollToView, editScrollThrottle)); ui.area.codemirrorScroll.on('scroll', _.throttle(syncScrollToView, editScrollThrottle));
@ -145,7 +143,7 @@ var buildMap = _.throttle(buildMapInner, buildMapThrottle);
// Build offsets for each line (lines can be wrapped) // Build offsets for each line (lines can be wrapped)
// That's a bit dirty to process each line everytime, but ok for demo. // That's a bit dirty to process each line everytime, but ok for demo.
// Optimizations are required only for big texts. // Optimizations are required only for big texts.
function buildMapInner(syncBack) { function buildMapInner(callback) {
var i, offset, nonEmptyList, pos, a, b, _lineHeightMap, linesCount, var i, offset, nonEmptyList, pos, a, b, _lineHeightMap, linesCount,
acc, _scrollMap; acc, _scrollMap;
@ -217,12 +215,15 @@ function buildMapInner(syncBack) {
scrollMap = _scrollMap; scrollMap = _scrollMap;
lineHeightMap = _lineHeightMap; lineHeightMap = _lineHeightMap;
if (loaded && syncBack) { if (loaded && callback) callback();
syncScrollToView();
syncScrollToEdit();
}
} }
// sync view scroll progress to edit
var viewScrollingTimeout = _.debounce(viewScrollingTimeoutInner, viewScrollingDebounce);
function viewScrollingTimeoutInner() {
viewScrolling = false;
}
function syncScrollToEdit(e) { function syncScrollToEdit(e) {
if (currentMode != modeType.both || !syncscroll) return; if (currentMode != modeType.both || !syncscroll) return;
@ -235,7 +236,7 @@ function syncScrollToEdit(e) {
return; return;
} }
if (!scrollMap || !lineHeightMap) { if (!scrollMap || !lineHeightMap) {
buildMap(true); buildMap(syncScrollToEdit);
return; return;
} }
if (editScrolling) return; if (editScrolling) return;
@ -272,21 +273,30 @@ function syncScrollToEdit(e) {
if (scrollInfo.height > scrollInfo.clientHeight && scrollTop >= preLastLinePos) { if (scrollInfo.height > scrollInfo.clientHeight && scrollTop >= preLastLinePos) {
posTo = preLastLineHeight; posTo = preLastLineHeight;
topDiffPercent = (scrollTop - preLastLinePos) / (viewBottom - preLastLinePos); topDiffPercent = (scrollTop - preLastLinePos) / (viewBottom - preLastLinePos);
posToNextDiff = Math.ceil(textHeight * topDiffPercent); posToNextDiff = textHeight * topDiffPercent;
posTo += Math.floor(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 = Math.ceil(textHeight * lineDiff * topDiffPercent); posToNextDiff = textHeight * lineDiff * topDiffPercent;
posTo += Math.floor(posToNextDiff);
} }
editor.scrollTo(0, posTo + posToNextDiff); var posDiff = Math.abs(scrollInfo.top - posTo);
preventSyncScrollToView = true; var duration = posDiff / 50;
ui.area.codemirrorScroll.stop(true, true).animate({
scrollTop: posTo
}, duration >= 100 ? duration : 100, "linear");
viewScrolling = true; viewScrolling = true;
clearTimeout(viewScrollingTimer); viewScrollingTimeout();
viewScrollingTimer = setTimeout(function () { }
viewScrolling = false;
}, viewScrollingDelay); // sync edit scroll progress to view
var editScrollingTimeout = _.debounce(editScrollingTimeoutInner, editScrollingDebounce);
function editScrollingTimeoutInner() {
editScrolling = false;
} }
function syncScrollToView(event, _lineNo) { function syncScrollToView(event, _lineNo) {
@ -300,7 +310,7 @@ function syncScrollToView(event, _lineNo) {
return; return;
} }
if (!scrollMap || !lineHeightMap) { if (!scrollMap || !lineHeightMap) {
buildMap(true); buildMap(syncScrollToView);
return; return;
} }
if (viewScrolling) return; if (viewScrolling) return;
@ -328,12 +338,12 @@ function syncScrollToView(event, _lineNo) {
posTo = scrollMap[lineHeightMap[_lineNo]]; posTo = scrollMap[lineHeightMap[_lineNo]];
} }
ui.area.view.stop(true, true).scrollTop(posTo); var posDiff = Math.abs(ui.area.view.scrollTop() - posTo);
preventSyncScrollToEdit = true; var duration = posDiff / 50;
ui.area.view.stop(true, true).animate({
scrollTop: posTo
}, duration >= 100 ? duration : 100, "linear");
editScrolling = true; editScrolling = true;
clearTimeout(editScrollingTimer); editScrollingTimeout();
editScrollingTimer = setTimeout(function () {
editScrolling = false;
}, editScrollingDelay);
} }