186 lines
5.1 KiB
JavaScript
186 lines
5.1 KiB
JavaScript
import store from '../../store';
|
|
import animationSvc from '../animationSvc';
|
|
import editorSvc from '../editorSvc';
|
|
|
|
let editorScrollerElt;
|
|
let previewScrollerElt;
|
|
let previewElt;
|
|
let editorFinishTimeoutId;
|
|
let previewFinishTimeoutId;
|
|
let skipAnimation;
|
|
let isScrollEditor;
|
|
let isScrollPreview;
|
|
let isEditorMoving;
|
|
let isPreviewMoving;
|
|
let sectionDescList;
|
|
|
|
let throttleTimeoutId;
|
|
let throttleLastTime = 0;
|
|
|
|
function throttle(func, wait) {
|
|
clearTimeout(throttleTimeoutId);
|
|
const currentTime = Date.now();
|
|
const localWait = (wait + throttleLastTime) - currentTime;
|
|
if (localWait < 1) {
|
|
throttleLastTime = currentTime;
|
|
func();
|
|
} else {
|
|
throttleTimeoutId = setTimeout(() => {
|
|
throttleLastTime = Date.now();
|
|
func();
|
|
}, localWait);
|
|
}
|
|
}
|
|
|
|
const doScrollSync = () => {
|
|
const localSkipAnimation = skipAnimation || !store.getters['layout/styles'].showSidePreview;
|
|
skipAnimation = false;
|
|
if (!store.state.editor.scrollSync || !sectionDescList || sectionDescList.length === 0) {
|
|
return;
|
|
}
|
|
let editorScrollTop = editorScrollerElt.scrollTop;
|
|
if (editorScrollTop < 0) {
|
|
editorScrollTop = 0;
|
|
}
|
|
const previewScrollTop = previewScrollerElt.scrollTop;
|
|
let scrollTo;
|
|
if (isScrollEditor) {
|
|
// Scroll the preview
|
|
isScrollEditor = false;
|
|
sectionDescList.some((sectionDesc) => {
|
|
if (editorScrollTop > sectionDesc.editorDimension.endOffset) {
|
|
return false;
|
|
}
|
|
const posInSection = (editorScrollTop - sectionDesc.editorDimension.startOffset)
|
|
/ (sectionDesc.editorDimension.height || 1);
|
|
scrollTo = (sectionDesc.previewDimension.startOffset
|
|
+ (sectionDesc.previewDimension.height * posInSection));
|
|
return true;
|
|
});
|
|
scrollTo = Math.min(
|
|
scrollTo,
|
|
previewScrollerElt.scrollHeight - previewScrollerElt.offsetHeight,
|
|
);
|
|
|
|
throttle(() => {
|
|
clearTimeout(previewFinishTimeoutId);
|
|
animationSvc.animate(previewScrollerElt)
|
|
.scrollTop(scrollTo)
|
|
.duration(!localSkipAnimation && 100)
|
|
.start(() => {
|
|
previewFinishTimeoutId = setTimeout(() => {
|
|
isPreviewMoving = false;
|
|
}, 100);
|
|
}, () => {
|
|
isPreviewMoving = true;
|
|
});
|
|
}, localSkipAnimation ? 500 : 50);
|
|
} else if (!store.getters['layout/styles'].showEditor || isScrollPreview) {
|
|
// Scroll the editor
|
|
isScrollPreview = false;
|
|
sectionDescList.some((sectionDesc) => {
|
|
if (previewScrollTop > sectionDesc.previewDimension.endOffset) {
|
|
return false;
|
|
}
|
|
const posInSection = (previewScrollTop - sectionDesc.previewDimension.startOffset)
|
|
/ (sectionDesc.previewDimension.height || 1);
|
|
scrollTo = (sectionDesc.editorDimension.startOffset
|
|
+ (sectionDesc.editorDimension.height * posInSection));
|
|
return true;
|
|
});
|
|
scrollTo = Math.min(
|
|
scrollTo,
|
|
editorScrollerElt.scrollHeight - editorScrollerElt.offsetHeight,
|
|
);
|
|
|
|
throttle(() => {
|
|
clearTimeout(editorFinishTimeoutId);
|
|
animationSvc.animate(editorScrollerElt)
|
|
.scrollTop(scrollTo)
|
|
.duration(!localSkipAnimation && 100)
|
|
.start(() => {
|
|
editorFinishTimeoutId = setTimeout(() => {
|
|
isEditorMoving = false;
|
|
}, 100);
|
|
}, () => {
|
|
isEditorMoving = true;
|
|
});
|
|
}, localSkipAnimation ? 500 : 50);
|
|
}
|
|
};
|
|
|
|
let isPreviewRefreshing;
|
|
let timeoutId;
|
|
|
|
const forceScrollSync = () => {
|
|
if (!isPreviewRefreshing) {
|
|
doScrollSync();
|
|
}
|
|
};
|
|
store.watch(state => state.editor.scrollSync, forceScrollSync);
|
|
|
|
editorSvc.$on('inited', () => {
|
|
editorScrollerElt = editorSvc.editorElt.parentNode;
|
|
previewScrollerElt = editorSvc.previewElt.parentNode;
|
|
previewElt = editorSvc.previewElt;
|
|
|
|
editorScrollerElt.addEventListener('scroll', () => {
|
|
if (isEditorMoving) {
|
|
return;
|
|
}
|
|
isScrollEditor = true;
|
|
isScrollPreview = false;
|
|
doScrollSync();
|
|
});
|
|
|
|
previewScrollerElt.addEventListener('scroll', () => {
|
|
if (isPreviewMoving || isPreviewRefreshing) {
|
|
return;
|
|
}
|
|
isScrollPreview = true;
|
|
isScrollEditor = false;
|
|
doScrollSync();
|
|
});
|
|
});
|
|
|
|
editorSvc.$on('sectionList', () => {
|
|
clearTimeout(timeoutId);
|
|
isPreviewRefreshing = true;
|
|
sectionDescList = undefined;
|
|
});
|
|
|
|
editorSvc.$on('conversionCtx', () => {
|
|
// Set the preview height to prevent scrollbar from jumping
|
|
previewElt.style.height = `${previewElt.offsetHeight}px`;
|
|
});
|
|
|
|
editorSvc.$on('previewText', () => {
|
|
// Remove height property once the preview as been refreshed
|
|
previewElt.style.removeProperty('height');
|
|
// Assume the user is writing in the editor
|
|
isScrollEditor = store.getters['layout/styles'].showEditor;
|
|
// A preview scrolling event can occur if height is smaller
|
|
timeoutId = setTimeout(() => {
|
|
isPreviewRefreshing = false;
|
|
}, 100);
|
|
});
|
|
|
|
store.watch(
|
|
() => store.getters['layout/styles'],
|
|
() => {
|
|
isScrollEditor = true;
|
|
isScrollPreview = false;
|
|
skipAnimation = true;
|
|
});
|
|
|
|
store.watch(
|
|
() => store.getters['files/current'].id,
|
|
() => {
|
|
skipAnimation = true;
|
|
});
|
|
|
|
editorSvc.$on('sectionDescMeasuredList', (sectionDescMeasuredList) => {
|
|
sectionDescList = sectionDescMeasuredList;
|
|
forceScrollSync();
|
|
});
|