diff --git a/app/src/editor/util.ts b/app/src/editor/util.ts index 554296ecd..1d21ec1d7 100644 --- a/app/src/editor/util.ts +++ b/app/src/editor/util.ts @@ -385,17 +385,17 @@ const switchEditor = (editor: Editor, options: IOpenFileOptions, allModels: IMod preventScroll(editor.editor.protyle); editor.editor.protyle.observerLoad?.disconnect(); if (options.action?.includes(Constants.CB_GET_HL)) { - highlightById(editor.editor.protyle, options.id, true); + highlightById(editor.editor.protyle, options.id, "top"); } else if (options.action?.includes(Constants.CB_GET_FOCUS)) { if (nodeElement) { - const newRange = focusBlock(nodeElement, undefined, options.action?.includes(Constants.CB_GET_OUTLINE) ? false : true); + const newRange = focusBlock(nodeElement, undefined, !options.action?.includes(Constants.CB_GET_OUTLINE)); if (newRange) { editor.editor.protyle.toolbar.range = newRange; } - scrollCenter(editor.editor.protyle, nodeElement, true); + scrollCenter(editor.editor.protyle, nodeElement, {position: "top"}); editor.editor.protyle.observerLoad = new ResizeObserver(() => { if (document.contains(nodeElement)) { - scrollCenter(editor.editor.protyle, nodeElement, true); + scrollCenter(editor.editor.protyle, nodeElement, {position: "top"}); } }); setTimeout(() => { diff --git a/app/src/layout/Wnd.ts b/app/src/layout/Wnd.ts index f4ae223ab..7622a1231 100644 --- a/app/src/layout/Wnd.ts +++ b/app/src/layout/Wnd.ts @@ -548,7 +548,7 @@ export class Wnd { range.collapse(); currentTab.model.editor.protyle.toolbar.range = range; } - scrollCenter(currentTab.model.editor.protyle, nodeElement, true); + scrollCenter(currentTab.model.editor.protyle, nodeElement, {position: "top"}); } else { openFileById({ app: this.app, diff --git a/app/src/menus/protyle.ts b/app/src/menus/protyle.ts index 0845ca487..87d4ce0c4 100644 --- a/app/src/menus/protyle.ts +++ b/app/src/menus/protyle.ts @@ -1022,7 +1022,7 @@ export const zoomOut = (options: { } focusBlock(showElement); const resizeObserver = new ResizeObserver(() => { - scrollCenter(options.protyle, focusElement, true); + scrollCenter(options.protyle, focusElement, {position: "top"}); }); resizeObserver.observe(options.protyle.wysiwyg.element); setTimeout(() => { diff --git a/app/src/mobile/editor.ts b/app/src/mobile/editor.ts index 08c47a63f..b730a6d04 100644 --- a/app/src/mobile/editor.ts +++ b/app/src/mobile/editor.ts @@ -41,9 +41,9 @@ export const openMobileFileById = (app: App, id: string, action: TProtyleAction[ }); if (blockElement) { pushBack(); - scrollCenter(window.siyuan.mobile.editor.protyle, blockElement, true); + scrollCenter(window.siyuan.mobile.editor.protyle, blockElement, {position: "top"}); if (action.includes(Constants.CB_GET_HL)) { - highlightById(window.siyuan.mobile.editor.protyle, id, true); + highlightById(window.siyuan.mobile.editor.protyle, id, "top"); } closePanel(); // 更新文档浏览时间 diff --git a/app/src/protyle/upload/index.ts b/app/src/protyle/upload/index.ts index 9eaee7425..7d14b14c2 100644 --- a/app/src/protyle/upload/index.ts +++ b/app/src/protyle/upload/index.ts @@ -209,7 +209,7 @@ const genUploadedLabel = (responseText: string, protyle: IProtyle) => { insertHTML(successFileText, protyle, insertBlock); // 粘贴图片后定位不准确 https://github.com/siyuan-note/siyuan/issues/13336 setTimeout(() => { - scrollCenter(protyle, undefined, false, "smooth"); + scrollCenter(protyle, undefined, {behavior: "smooth"}); }, hasImage ? 0 : Constants.TIMEOUT_LOAD); }; diff --git a/app/src/protyle/util/insertHTML.ts b/app/src/protyle/util/insertHTML.ts index a3eb9d5ad..bb2a3d854 100644 --- a/app/src/protyle/util/insertHTML.ts +++ b/app/src/protyle/util/insertHTML.ts @@ -333,7 +333,7 @@ export const insertHTML = (html: string, protyle: IProtyle, isBlock = false, blockElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss")); updateTransaction(protyle, id, blockElement.outerHTML, oldHTML); setTimeout(() => { - scrollCenter(protyle, blockElement, false, "smooth"); + scrollCenter(protyle, blockElement, {behavior: "smooth"}); }, Constants.TIMEOUT_LOAD); return; } diff --git a/app/src/protyle/util/onGet.ts b/app/src/protyle/util/onGet.ts index ad681c3d2..4655a3670 100644 --- a/app/src/protyle/util/onGet.ts +++ b/app/src/protyle/util/onGet.ts @@ -493,7 +493,7 @@ const focusElementById = (protyle: IProtyle, action: string[], scrollAttr?: IScr const contentRect = protyle.contentElement.getBoundingClientRect(); const focusRect = focusElement.getBoundingClientRect(); if (!hasScrollTop && (contentRect.top > focusRect.top || contentRect.bottom < focusRect.bottom)) { - scrollCenter(protyle, focusElement, true); + scrollCenter(protyle, focusElement, {position: "top"}); } } else { return; @@ -507,7 +507,7 @@ const focusElementById = (protyle: IProtyle, action: string[], scrollAttr?: IScr const contentRect = protyle.contentElement.getBoundingClientRect(); const focusRect = focusElement.getBoundingClientRect(); if (!hasScrollTop && (contentRect.top > focusRect.top || contentRect.bottom < focusRect.bottom)) { - scrollCenter(protyle, focusElement, true); + scrollCenter(protyle, focusElement, {position: "top"}); } } }); diff --git a/app/src/protyle/util/paste.ts b/app/src/protyle/util/paste.ts index 3c5a65c08..f79ad4f3d 100644 --- a/app/src/protyle/util/paste.ts +++ b/app/src/protyle/util/paste.ts @@ -539,7 +539,7 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven processRender(protyle.wysiwyg.element); highlightRender(protyle.wysiwyg.element); avRender(protyle.wysiwyg.element, protyle); - scrollCenter(protyle, undefined, false, "smooth"); + scrollCenter(protyle, undefined, {behavior: "smooth"}); }); return; } else if (files && files.length > 0) { @@ -592,7 +592,7 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven if (nodeElement.classList.contains("av") && selectCellElement) { cellScrollIntoView(nodeElement, selectCellElement); } else { - scrollCenter(protyle, undefined, false, "smooth"); + scrollCenter(protyle, undefined, {behavior: "smooth"}); } }; diff --git a/app/src/protyle/wysiwyg/transaction.ts b/app/src/protyle/wysiwyg/transaction.ts index 346d55fc7..6cdf4e35e 100644 --- a/app/src/protyle/wysiwyg/transaction.ts +++ b/app/src/protyle/wysiwyg/transaction.ts @@ -1417,7 +1417,7 @@ const processFold = (operation: IOperation, protyle: IProtyle) => { if (operation.context?.focusId) { const focusElement = protyle.wysiwyg.element.querySelector(`[data-node-id="${operation.context.focusId}"]`); focusBlock(focusElement); - scrollCenter(protyle, focusElement, false); + scrollCenter(protyle, focusElement); } else { protyle.contentElement.scrollTop = scrollTop; protyle.scroll.lastScrollTop = scrollTop; diff --git a/app/src/search/util.ts b/app/src/search/util.ts index df2520ebf..c40d37b2a 100644 --- a/app/src/search/util.ts +++ b/app/src/search/util.ts @@ -1049,7 +1049,7 @@ const renderNextSearchMark = (options: { }); if (currentRange) { if (!currentRange.toString()) { - highlightById(options.edit.protyle, options.id); + highlightById(options.edit.protyle, options.id, "center"); } else { scrollToCurrent(options.edit.protyle.contentElement, currentRange, contentRect); } @@ -1131,12 +1131,12 @@ export const getArticle = (options: { const currentRange = options.edit.protyle.highlight.ranges[options.edit.protyle.highlight.rangeIndex]; if (options.edit.protyle.highlight.ranges.length > 0 && currentRange) { if (!currentRange.toString()) { - highlightById(options.edit.protyle, options.id); + highlightById(options.edit.protyle, options.id, "center"); } else { scrollToCurrent(options.edit.protyle.contentElement, currentRange, contentRect); } } else { - highlightById(options.edit.protyle, options.id); + highlightById(options.edit.protyle, options.id, "center"); } }; if (observer) { diff --git a/app/src/util/backForward.ts b/app/src/util/backForward.ts index 5940f0f37..a46c31948 100644 --- a/app/src/util/backForward.ts +++ b/app/src/util/backForward.ts @@ -179,7 +179,7 @@ const focusStack = async (app: App, stack: IBackStack) => { } }); focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end); - scrollCenter(stack.protyle, blockElement, true); + scrollCenter(stack.protyle, blockElement, {position: "top"}); } }); }); @@ -207,7 +207,7 @@ const focusStack = async (app: App, stack: IBackStack) => { } }); focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end); - scrollCenter(stack.protyle, blockElement, true); + scrollCenter(stack.protyle, blockElement, {position: "top"}); } }); return true; diff --git a/app/src/util/highlightById.ts b/app/src/util/highlightById.ts index 1426f921c..0305e7f8a 100644 --- a/app/src/util/highlightById.ts +++ b/app/src/util/highlightById.ts @@ -8,7 +8,7 @@ export const bgFade = (element: Element) => { }, 1024); }; -export const highlightById = (protyle: IProtyle, id: string, top = false) => { +export const highlightById = (protyle: IProtyle, id: string, position: "auto" | "center" | "top" = "auto") => { let nodeElement: HTMLElement; const protyleElement = protyle.wysiwyg.element; if (!protyle.preview.element.classList.contains("fn__none")) { @@ -28,7 +28,7 @@ export const highlightById = (protyle: IProtyle, id: string, top = false) => { } }); if (nodeElement) { - scrollCenter(protyle, nodeElement, top); + scrollCenter(protyle, nodeElement, {position}); bgFade(nodeElement); return nodeElement;// 仅配合前进后退使用 } @@ -38,8 +38,16 @@ export const highlightById = (protyle: IProtyle, id: string, top = false) => { } }; -export const scrollCenter = (protyle: IProtyle, nodeElement?: Element, top = false, behavior: ScrollBehavior = "auto") => { - if (!protyle.disabled && !top && getSelection().rangeCount > 0) { +export const scrollCenter = ( + protyle: IProtyle, + nodeElement?: Element, + options?: { + position?: "top" | "center" | "auto"; + behavior?: ScrollBehavior; + } +) => { + const {position = "auto", behavior = "auto"} = options || {}; + if (!protyle.disabled && position === "auto" && getSelection().rangeCount > 0) { const range = getSelection().getRangeAt(0); const blockElement = hasClosestBlock(range.startContainer); if (blockElement) { @@ -108,16 +116,33 @@ export const scrollCenter = (protyle: IProtyle, nodeElement?: Element, top = fal contentTop += topElement.clientHeight; topElement = topElement.nextElementSibling; } - if (top) { + if (position === "center") { + const elementRect = nodeElement.getBoundingClientRect(); + const contentRect = protyle.contentElement.getBoundingClientRect(); + // 如果块的高度超过容器视口,让块的上边界贴着容器上边界;否则让整个块居中 + if (elementRect.height > contentRect.height) { + protyle.contentElement.scroll({top: protyle.contentElement.scrollTop + elementRect.top - contentRect.top, behavior}); + } else { + const elementCenter = elementRect.top + elementRect.height / 2; + const contentCenter = contentRect.top + contentRect.height / 2; + protyle.contentElement.scroll({top: protyle.contentElement.scrollTop + elementCenter - contentCenter, behavior}); + } + return; + } else if (position === "top") { protyle.contentElement.scroll({top: offsetTop - contentTop, behavior}); return; } if (protyle.contentElement.scrollTop > offsetTop - 32) { protyle.contentElement.scroll({top: offsetTop - contentTop, behavior}); + return; } else if (protyle.contentElement.scrollTop + protyle.contentElement.clientHeight < offsetTop + nodeElement.clientHeight - contentTop) { protyle.contentElement.scroll({ top: offsetTop + nodeElement.clientHeight - contentTop - protyle.contentElement.clientHeight, behavior }); + return; } + const elementRect = nodeElement.getBoundingClientRect(); + const contentRect = protyle.contentElement.getBoundingClientRect(); + protyle.contentElement.scroll({top: protyle.contentElement.scrollTop + elementRect.top - contentRect.top - contentRect.height / 2, behavior}); };