From b28750f256d6fbab1406c6c00155ea14eef5c600 Mon Sep 17 00:00:00 2001 From: "Cheng-Han, Wu" Date: Thu, 26 May 2016 13:17:00 +0800 Subject: [PATCH] Update to improve syncscroll performance and add toggle for sync scrolling --- public/css/index.css | 11 +++ public/js/index.js | 63 +++++++------ public/js/syncscroll.js | 194 ++++++++++++++++++---------------------- 3 files changed, 132 insertions(+), 136 deletions(-) diff --git a/public/css/index.css b/public/css/index.css index aaf84a0..1b9fb5d 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -112,6 +112,17 @@ body { background-color: white; box-shadow: 5px 0px 10px #e7e7e7; } +.ui-edit-area .ui-sync-toggle { + width: 42px; + height: 42px; + padding: 3px 1px 0 0; + border-radius: 50%; + border-color: #e7e7e7; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} .ui-view-area { /*overflow-y: scroll;*/ -webkit-overflow-scrolling: touch; diff --git a/public/js/index.js b/public/js/index.js index b4f912b..29e2297 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -552,7 +552,11 @@ var ui = { codemirrorScroll: $(".ui-edit-area .CodeMirror .CodeMirror-scroll"), codemirrorSizer: $(".ui-edit-area .CodeMirror .CodeMirror-sizer"), codemirrorSizerInner: $(".ui-edit-area .CodeMirror .CodeMirror-sizer > div"), - markdown: $(".ui-view-area .markdown-body") + markdown: $(".ui-view-area .markdown-body"), + resize: { + handle: $('.ui-resizable-handle'), + syncToggle: $('.ui-sync-toggle') + } }, modal: { snippetImportProjects: $("#snippetImportModalProjects"), @@ -705,30 +709,6 @@ $(window).error(function () { //setNeedRefresh(); }); -//when page hash change -window.onhashchange = locationHashChanged; - -function locationHashChanged(e) { - e.stopPropagation(); - e.preventDefault(); - if (currentMode != modeType.both) { - return; - } - var hashtarget = $("[id$='" + location.hash.substr(1) + "']"); - if (hashtarget.length > 0) { - var linenumber = hashtarget.attr('data-startline'); - if (linenumber) { - editor.setOption('viewportMargin', Infinity); - editor.setOption('viewportMargin', viewportMargin); - var t = editor.charCoords({ - line: linenumber, - ch: 0 - }, "local").top; - editor.scrollTo(null, t - defaultTextHeight * 1.2); - } - } -} - var windowResizeDebounce = 200; var windowResize = _.debounce(windowResizeInner, windowResizeDebounce); @@ -814,10 +794,38 @@ function checkEditorStyle() { handles: 'e', maxWidth: $(window).width() * 0.7, minWidth: $(window).width() * 0.2, + resize: function (e) { + ui.area.resize.syncToggle.stop(true, true).show(); + }, stop: function (e) { lastEditorWidth = ui.area.edit.width(); } }); + if (!ui.area.resize.handle.length) { + ui.area.resize.handle = $('.ui-resizable-handle'); + } + if (!ui.area.resize.syncToggle.length) { + ui.area.resize.syncToggle = $(''); + ui.area.resize.syncToggle.click(function () { + syncscroll = !syncscroll; + checkSyncToggle(); + }); + ui.area.resize.handle.append(ui.area.resize.syncToggle); + ui.area.resize.syncToggle.hide(); + ui.area.resize.handle.hover(function () { + ui.area.resize.syncToggle.stop(true, true).delay(200).fadeIn(100); + }, function () { + ui.area.resize.syncToggle.stop(true, true).delay(300).fadeOut(300); + }); + } +} + +function checkSyncToggle() { + if (syncscroll) { + ui.area.resize.syncToggle.find('i').removeClass('fa-unlink').addClass('fa-link'); + } else { + ui.area.resize.syncToggle.find('i').removeClass('fa-link').addClass('fa-unlink'); + } } function checkEditorScrollbar() { @@ -984,10 +992,10 @@ function changeMode(type) { ui.area.edit.css('width', lastEditorWidth + 'px'); else ui.area.edit.css('width', ''); - ui.area.edit.find('.ui-resizable-handle').show(); + ui.area.resize.handle.show(); } else { ui.area.edit.css('width', ''); - ui.area.edit.find('.ui-resizable-handle').hide(); + ui.area.resize.handle.hide(); } windowResizeInner(); @@ -995,6 +1003,7 @@ function changeMode(type) { restoreInfo(); if (lastMode == modeType.view && currentMode == modeType.both) { + preventSyncScrollToView = true; syncScrollToEdit(); } diff --git a/public/js/syncscroll.js b/public/js/syncscroll.js index b52e2bf..348af2d 100644 --- a/public/js/syncscroll.js +++ b/public/js/syncscroll.js @@ -105,10 +105,13 @@ md.use(window.markdownitContainer, 'info', { render: renderContainer }); md.use(window.markdownitContainer, 'warning', { render: renderContainer }); md.use(window.markdownitContainer, 'danger', { render: renderContainer }); -var preventSyncScroll = false; +var syncscroll = true; + +var preventSyncScrollToEdit = false; +var preventSyncScrollToView = false; var editScrollThrottle = 1; -var viewScrollThrottle = 20; +var viewScrollThrottle = 10; var buildMapThrottle = 100; var viewScrolling = false; @@ -126,71 +129,6 @@ if (editor.getOption('scrollbarStyle') === 'native') { } ui.area.view.on('scroll', _.throttle(syncScrollToEdit, viewScrollThrottle)); -var preventViewScroll = false; - -function syncScrollToEdit(e) { - if (currentMode != modeType.both) return; - if (preventViewScroll) { - if (typeof preventViewScroll === 'number') { - preventViewScroll--; - } else { - preventViewScroll = false; - } - return; - } - if (!scrollMap || !lineHeightMap) { - buildMap(true); - return; - } - if (editScrolling) return; - var scrollTop = ui.area.view[0].scrollTop; - var lineIndex = 0; - for (var i = 0, l = scrollMap.length; i < l; i++) { - if (scrollMap[i] > scrollTop) { - break; - } else { - lineIndex = i; - } - } - var lineNo = 0; - var lineDiff = 0; - for (var i = 0, l = lineHeightMap.length; i < l; i++) { - if (lineHeightMap[i] > lineIndex) { - break; - } else { - lineNo = lineHeightMap[i]; - lineDiff = lineHeightMap[i + 1] - lineNo; - } - } - - var scrollInfo = editor.getScrollInfo(); - var textHeight = editor.defaultTextHeight(); - var posTo = 0; - var topDiffPercent = 0; - var posToNextDiff = 0; - var preLastLineHeight = scrollInfo.height - scrollInfo.clientHeight - textHeight; - var preLastLineNo = Math.round(preLastLineHeight / textHeight); - - if (scrollInfo.height > scrollInfo.clientHeight && lineNo >= preLastLineNo) { - posTo = preLastLineHeight; - topDiffPercent = (scrollTop - scrollMap[preLastLineNo]) / (viewBottom - scrollMap[preLastLineNo]); - posToNextDiff = Math.ceil(textHeight * topDiffPercent); - } else { - posTo = lineNo * textHeight; - topDiffPercent = (scrollTop - scrollMap[lineNo]) / (scrollMap[lineNo + lineDiff] - scrollMap[lineNo]); - posToNextDiff = Math.ceil(textHeight * lineDiff * topDiffPercent); - } - - editor.scrollTo(0, posTo + posToNextDiff); - preventSyncScroll = true; - - viewScrolling = true; - clearTimeout(viewScrollingTimer); - viewScrollingTimer = setTimeout(function () { - viewScrolling = false; - }, viewScrollingDelay); -} - var scrollMap, lineHeightMap, viewTop, viewBottom; viewAjaxCallback = clearMap; @@ -279,60 +217,98 @@ function buildMapInner(syncBack) { scrollMap = _scrollMap; lineHeightMap = _lineHeightMap; - if (loaded && syncBack) + if (loaded && syncBack) { syncScrollToView(); + syncScrollToEdit(); + } } -function getPartByEditorLineNo(lineNo) { - var part = null; - ui.area.markdown.find('.part').each(function (n, el) { - if (part) return; - var $el = $(el), - t = $el.data('startline') - 1, - f = $el.data('endline') - 1; - if (t === '' || f === '') { - return; - } - if (lineNo >= t && lineNo <= f) { - part = $el; - } - }); - if (part) - return { - startline: part.data('startline') - 1, - endline: part.data('endline') - 1, - linediff: Math.abs(part.data('endline') - part.data('startline')) + 1, - element: part - }; - else - return null; -} -function getEditorLineNoByTop(top) { - for (var i = 0; i < lineHeightMap.length; i++) - if (lineHeightMap[i] * editor.defaultTextHeight() > top) - return i; - return null; -} - -function syncScrollToView(event, _lineNo) { - if (currentMode != modeType.both) return; - if (preventSyncScroll) { - if (typeof preventSyncScroll === 'number') { - preventSyncScroll--; +function syncScrollToEdit(e) { + if (currentMode != modeType.both || !syncscroll) return; + if (preventSyncScrollToEdit) { + if (typeof preventSyncScrollToEdit === 'number') { + preventSyncScrollToEdit--; } else { - preventSyncScroll = false; + preventSyncScrollToEdit = false; } return; } - var lineNo, posTo; - var scrollInfo = editor.getScrollInfo(); if (!scrollMap || !lineHeightMap) { buildMap(true); return; } + if (editScrolling) return; + + var scrollTop = ui.area.view[0].scrollTop; + var lineIndex = 0; + for (var i = 0, l = scrollMap.length; i < l; i++) { + if (scrollMap[i] > scrollTop) { + break; + } else { + lineIndex = i; + } + } + var lineNo = 0; + var lineDiff = 0; + for (var i = 0, l = lineHeightMap.length; i < l; i++) { + if (lineHeightMap[i] > lineIndex) { + break; + } else { + lineNo = lineHeightMap[i]; + lineDiff = lineHeightMap[i + 1] - lineNo; + } + } + + var posTo = 0; + var topDiffPercent = 0; + var posToNextDiff = 0; + var scrollInfo = editor.getScrollInfo(); + var textHeight = editor.defaultTextHeight(); + var preLastLineHeight = scrollInfo.height - scrollInfo.clientHeight - textHeight; + var preLastLineNo = Math.round(preLastLineHeight / textHeight); + var preLastLinePos = scrollMap[preLastLineNo]; + + if (scrollInfo.height > scrollInfo.clientHeight && scrollTop >= preLastLinePos) { + posTo = preLastLineHeight; + topDiffPercent = (scrollTop - preLastLinePos) / (viewBottom - preLastLinePos); + posToNextDiff = Math.ceil(textHeight * topDiffPercent); + } else { + posTo = lineNo * textHeight; + topDiffPercent = (scrollTop - scrollMap[lineNo]) / (scrollMap[lineNo + lineDiff] - scrollMap[lineNo]); + posToNextDiff = Math.ceil(textHeight * lineDiff * topDiffPercent); + } + + editor.scrollTo(0, posTo + posToNextDiff); + preventSyncScrollToView = true; + + viewScrolling = true; + clearTimeout(viewScrollingTimer); + viewScrollingTimer = setTimeout(function () { + viewScrolling = false; + }, viewScrollingDelay); +} + +function syncScrollToView(event, _lineNo) { + if (currentMode != modeType.both || !syncscroll) return; + if (preventSyncScrollToView) { + if (typeof preventSyncScrollToView === 'number') { + preventSyncScrollToView--; + } else { + preventSyncScrollToView = false; + } + return; + } + if (!scrollMap || !lineHeightMap) { + buildMap(true); + 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 @@ -349,11 +325,11 @@ function syncScrollToView(event, _lineNo) { posTo += Math.floor(posToNextDiff); } } else { - if (viewScrolling) return; posTo = scrollMap[lineHeightMap[_lineNo]]; } + ui.area.view.stop(true, true).scrollTop(posTo); - preventViewScroll = true; + preventSyncScrollToEdit = true; editScrolling = true; clearTimeout(editScrollingTimer);