优化模式切换功能
This commit is contained in:
@@ -89,6 +89,61 @@ const isEditingEmptyDocument = computed(
|
|||||||
() => editingBlockId.value === EMPTY_BLOCK_ID,
|
() => editingBlockId.value === EMPTY_BLOCK_ID,
|
||||||
);
|
);
|
||||||
let textareaResizeFrame: number | null = null;
|
let textareaResizeFrame: number | null = null;
|
||||||
|
let modeScrollRestoreFrame: number | null = null;
|
||||||
|
|
||||||
|
interface ScrollSnapshot {
|
||||||
|
ratio: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScrollableDistance(el: HTMLElement) {
|
||||||
|
return Math.max(0, el.scrollHeight - el.clientHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
function captureScrollSnapshot(): ScrollSnapshot | null {
|
||||||
|
const el = scrollRef.value;
|
||||||
|
if (!el) return null;
|
||||||
|
|
||||||
|
const maxScrollTop = getScrollableDistance(el);
|
||||||
|
return {
|
||||||
|
ratio: maxScrollTop > 0 ? el.scrollTop / maxScrollTop : 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreScrollSnapshot(snapshot: ScrollSnapshot | null) {
|
||||||
|
const el = scrollRef.value;
|
||||||
|
if (!el || !snapshot) return;
|
||||||
|
|
||||||
|
const maxScrollTop = getScrollableDistance(el);
|
||||||
|
el.scrollTop = maxScrollTop * snapshot.ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreScrollAfterModeChange(
|
||||||
|
snapshot: ScrollSnapshot | null,
|
||||||
|
mode: DocumentMode,
|
||||||
|
) {
|
||||||
|
if (!snapshot) return;
|
||||||
|
|
||||||
|
if (modeScrollRestoreFrame !== null) {
|
||||||
|
cancelAnimationFrame(modeScrollRestoreFrame);
|
||||||
|
modeScrollRestoreFrame = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nextTick(() => {
|
||||||
|
if (mode === "edit" && editorTextareaRef.value) {
|
||||||
|
resizeTextarea(editorTextareaRef.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreScrollSnapshot(snapshot);
|
||||||
|
|
||||||
|
modeScrollRestoreFrame = requestAnimationFrame(() => {
|
||||||
|
modeScrollRestoreFrame = null;
|
||||||
|
if (mode === "edit" && editorTextareaRef.value) {
|
||||||
|
resizeTextarea(editorTextareaRef.value);
|
||||||
|
}
|
||||||
|
restoreScrollSnapshot(snapshot);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function syncRenderedState(md: string, mode = activeDocumentMode.value) {
|
function syncRenderedState(md: string, mode = activeDocumentMode.value) {
|
||||||
if (mode === "preview") {
|
if (mode === "preview") {
|
||||||
@@ -455,6 +510,7 @@ function flushPendingChanges(): string | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watch(activeDocumentMode, (mode, oldMode) => {
|
watch(activeDocumentMode, (mode, oldMode) => {
|
||||||
|
const scrollSnapshot = captureScrollSnapshot();
|
||||||
let committedValue: string | undefined;
|
let committedValue: string | undefined;
|
||||||
if (oldMode === "mixed" && mode !== "mixed" && editingBlockId.value) {
|
if (oldMode === "mixed" && mode !== "mixed" && editingBlockId.value) {
|
||||||
committedValue = finalizeBlockEdit();
|
committedValue = finalizeBlockEdit();
|
||||||
@@ -465,12 +521,17 @@ watch(activeDocumentMode, (mode, oldMode) => {
|
|||||||
if (mode === "edit") {
|
if (mode === "edit") {
|
||||||
void nextTick(scheduleEditorTextareaResize);
|
void nextTick(scheduleEditorTextareaResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restoreScrollAfterModeChange(scrollSnapshot, mode);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (textareaResizeFrame !== null) {
|
if (textareaResizeFrame !== null) {
|
||||||
cancelAnimationFrame(textareaResizeFrame);
|
cancelAnimationFrame(textareaResizeFrame);
|
||||||
}
|
}
|
||||||
|
if (modeScrollRestoreFrame !== null) {
|
||||||
|
cancelAnimationFrame(modeScrollRestoreFrame);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|||||||
Reference in New Issue
Block a user