From 2c60f0dd671b63885ffcbc494025ef037fdf34fd Mon Sep 17 00:00:00 2001 From: "Cheng-Han, Wu" Date: Sun, 29 May 2016 13:58:32 +0800 Subject: [PATCH] Improve syncscroll performance and accuracy with few UX tweaks --- public/js/index.js | 34 +++++++--------- public/js/syncscroll.js | 90 ++++++++++++++++++++++------------------- 2 files changed, 64 insertions(+), 60 deletions(-) diff --git a/public/js/index.js b/public/js/index.js index 92a06a5..22652d0 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -713,6 +713,14 @@ $(window).error(function () { //setNeedRefresh(); }); +function autoSyncscroll() { + if (editorHasFocus()) { + syncScrollToView(); + } else { + syncScrollToEdit(); + } +} + var windowResizeDebounce = 200; var windowResize = _.debounce(windowResizeInner, windowResizeDebounce); @@ -727,11 +735,7 @@ function windowResizeInner(callback) { if (editor.getOption('scrollbarStyle') === 'native') { setTimeout(function () { clearMap(); - if (editorHasFocus()) { - syncScrollToView(); - } else { - syncScrollToEdit(); - } + autoSyncscroll(); updateScrollspy(); if (callback && typeof callback === 'function') callback(); @@ -741,11 +745,7 @@ function windowResizeInner(callback) { editor.setOption('viewportMargin', Infinity); setTimeout(function () { clearMap(); - if (editorHasFocus()) { - syncScrollToView(); - } else { - syncScrollToEdit(); - } + autoSyncscroll(); editor.setOption('viewportMargin', viewportMargin); //add or update user cursors for (var i = 0; i < onlineUsers.length; i++) { @@ -1029,12 +1029,12 @@ function changeMode(type) { if (lastMode == modeType.view && currentMode == modeType.both) { preventSyncScrollToView = 2; - syncScrollToEdit(); + syncScrollToEdit(null, true); } if (lastMode == modeType.edit && currentMode == modeType.both) { preventSyncScrollToEdit = 2; - syncScrollToView(); + syncScrollToView(null, true); } if (lastMode == modeType.both && currentMode != modeType.both) { @@ -2458,10 +2458,10 @@ editor.on('beforeChange', function (cm, change) { cmClient.editorAdapter.ignoreNextChange = true; }); editor.on('cut', function () { - windowResize(); //workaround for scrollMap + //na }); editor.on('paste', function () { - windowResize(); //workaround for scrollMap + //na }); editor.on('changes', function (cm, changes) { updateHistory(); @@ -2630,11 +2630,7 @@ function updateViewInner() { clearMap(); //buildMap(); updateTitleReminder(); - if (editorHasFocus()) { - syncScrollToView(); - } else { - syncScrollToEdit(); - } + autoSyncscroll(); } var updateHistoryDebounce = 600; diff --git a/public/js/syncscroll.js b/public/js/syncscroll.js index c8b45e7..83c9a69 100644 --- a/public/js/syncscroll.js +++ b/public/js/syncscroll.js @@ -110,7 +110,7 @@ var syncscroll = true; var preventSyncScrollToEdit = false; var preventSyncScrollToView = false; -var editScrollThrottle = 2; +var editScrollThrottle = 10; var viewScrollThrottle = 10; var buildMapThrottle = 100; @@ -214,7 +214,7 @@ function buildMapInner(callback) { // sync view scroll progress to edit var viewScrollingTimer = null; -function syncScrollToEdit(e) { +function syncScrollToEdit(event, preventAnimate) { if (currentMode != modeType.both || !syncscroll) return; if (preventSyncScrollToEdit) { if (typeof preventSyncScrollToEdit === 'number') { @@ -225,7 +225,9 @@ function syncScrollToEdit(e) { return; } if (!scrollMap || !lineHeightMap) { - buildMap(syncScrollToEdit); + buildMap(function () { + syncScrollToEdit(event, preventAnimate); + }); return; } if (editScrolling) return; @@ -263,24 +265,28 @@ function syncScrollToEdit(e) { posTo = preLastLineHeight; topDiffPercent = (scrollTop - preLastLinePos) / (viewBottom - preLastLinePos); posToNextDiff = textHeight * topDiffPercent; - posTo += Math.floor(posToNextDiff); + posTo += Math.ceil(posToNextDiff); } else { posTo = lineNo * textHeight; topDiffPercent = (scrollTop - scrollMap[lineNo]) / (scrollMap[lineNo + lineDiff] - scrollMap[lineNo]); posToNextDiff = textHeight * lineDiff * topDiffPercent; - posTo += Math.floor(posToNextDiff); + posTo += Math.ceil(posToNextDiff); } - var posDiff = Math.abs(scrollInfo.top - posTo); - var duration = posDiff / 50; - duration = duration >= 100 ? duration : 100; - ui.area.codemirrorScroll.stop(true, true).animate({ - scrollTop: posTo - }, duration, "linear"); + if (preventAnimate) { + ui.area.codemirrorScroll.scrollTop(posTo); + } else { + var posDiff = Math.abs(scrollInfo.top - posTo); + var duration = posDiff / 50; + duration = duration >= 100 ? duration : 100; + ui.area.codemirrorScroll.stop(true, true).animate({ + scrollTop: posTo + }, duration, "linear"); + } viewScrolling = true; clearTimeout(viewScrollingTimer); - viewScrollingTimer = setTimeout(viewScrollingTimeoutInner, duration * 1.2); + viewScrollingTimer = setTimeout(viewScrollingTimeoutInner, duration * 1.5); } function viewScrollingTimeoutInner() { @@ -290,7 +296,7 @@ function viewScrollingTimeoutInner() { // sync edit scroll progress to view var editScrollingTimer = null; -function syncScrollToView(event, _lineNo) { +function syncScrollToView(event, preventAnimate) { if (currentMode != modeType.both || !syncscroll) return; if (preventSyncScrollToView) { if (typeof preventSyncScrollToView === 'number') { @@ -301,44 +307,46 @@ function syncScrollToView(event, _lineNo) { return; } if (!scrollMap || !lineHeightMap) { - buildMap(syncScrollToView); + buildMap(function () { + syncScrollToView(event, preventAnimate); + }); return; } if (viewScrolling) return; - if (!_lineNo) { - var lineNo, posTo; - var topDiffPercent, posToNextDiff; - var scrollInfo = editor.getScrollInfo(); - var textHeight = editor.defaultTextHeight(); - lineNo = Math.floor(scrollInfo.top / textHeight); - // if reach the last line, will start lerp to the bottom - var diffToBottom = (scrollInfo.top + scrollInfo.clientHeight) - (scrollInfo.height - textHeight); - if (scrollInfo.height > scrollInfo.clientHeight && diffToBottom > 0) { - topDiffPercent = diffToBottom / textHeight; - posTo = scrollMap[lineNo + 1]; - posToNextDiff = (viewBottom - posTo) * topDiffPercent; - posTo += Math.floor(posToNextDiff); - } else { - topDiffPercent = (scrollInfo.top % textHeight) / textHeight; - posTo = scrollMap[lineNo]; - posToNextDiff = (scrollMap[lineNo + 1] - posTo) * topDiffPercent; - posTo += Math.floor(posToNextDiff); - } + var lineNo, posTo; + var topDiffPercent, posToNextDiff; + var scrollInfo = editor.getScrollInfo(); + var textHeight = editor.defaultTextHeight(); + lineNo = Math.floor(scrollInfo.top / textHeight); + // if reach the last line, will start lerp to the bottom + var diffToBottom = (scrollInfo.top + scrollInfo.clientHeight) - (scrollInfo.height - textHeight); + if (scrollInfo.height > scrollInfo.clientHeight && diffToBottom > 0) { + topDiffPercent = diffToBottom / textHeight; + posTo = scrollMap[lineNo + 1]; + posToNextDiff = (viewBottom - posTo) * topDiffPercent; + posTo += Math.floor(posToNextDiff); } 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); - var duration = posDiff / 50; - duration = duration >= 100 ? duration : 100; - ui.area.view.stop(true, true).animate({ - scrollTop: posTo - }, duration, "linear"); + if (preventAnimate) { + ui.area.view.scrollTop(posTo); + } else { + var posDiff = Math.abs(ui.area.view.scrollTop() - posTo); + var duration = posDiff / 50; + duration = duration >= 100 ? duration : 100; + ui.area.view.stop(true, true).animate({ + scrollTop: posTo + }, duration, "linear"); + } editScrolling = true; clearTimeout(editScrollingTimer); - editScrollingTimer = setTimeout(editScrollingTimeoutInner, duration * 1.2); + editScrollingTimer = setTimeout(editScrollingTimeoutInner, duration * 1.5); } function editScrollingTimeoutInner() {