优化模式切换功能

This commit is contained in:
2026-07-08 14:59:54 +08:00
parent 0c8d5d1624
commit aabe685c48

View File

@@ -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({