diff --git a/app/src/editor/util.ts b/app/src/editor/util.ts index 1d21ec1d7..14c4900cd 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, "top"); + highlightById(editor.editor.protyle, options.id, "start"); } else if (options.action?.includes(Constants.CB_GET_FOCUS)) { if (nodeElement) { 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, {position: "top"}); + scrollCenter(editor.editor.protyle); editor.editor.protyle.observerLoad = new ResizeObserver(() => { if (document.contains(nodeElement)) { - scrollCenter(editor.editor.protyle, nodeElement, {position: "top"}); + scrollCenter(editor.editor.protyle); } }); setTimeout(() => { @@ -407,9 +407,7 @@ const switchEditor = (editor: Editor, options: IOpenFileOptions, allModels: IMod } else if (editor.editor.protyle.toolbar.range) { nodeElement = hasClosestBlock(editor.editor.protyle.toolbar.range.startContainer) as Element; focusByRange(editor.editor.protyle.toolbar.range); - if (nodeElement) { - scrollCenter(editor.editor.protyle, nodeElement); - } + scrollCenter(editor.editor.protyle); } } pushBack(editor.editor.protyle, editor.editor.protyle.toolbar.range); diff --git a/app/src/layout/Wnd.ts b/app/src/layout/Wnd.ts index 7622a1231..5e8e636c6 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, {position: "top"}); + scrollCenter(currentTab.model.editor.protyle, nodeElement, "start"); } else { openFileById({ app: this.app, diff --git a/app/src/menus/protyle.ts b/app/src/menus/protyle.ts index 87d4ce0c4..a980d9a77 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, {position: "top"}); + scrollCenter(options.protyle, focusElement, "start"); }); resizeObserver.observe(options.protyle.wysiwyg.element); setTimeout(() => { diff --git a/app/src/mobile/editor.ts b/app/src/mobile/editor.ts index b730a6d04..d1cf235d8 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, {position: "top"}); + scrollCenter(window.siyuan.mobile.editor.protyle, blockElement, "start"); if (action.includes(Constants.CB_GET_HL)) { - highlightById(window.siyuan.mobile.editor.protyle, id, "top"); + highlightById(window.siyuan.mobile.editor.protyle, id, "start"); } closePanel(); // 更新文档浏览时间 diff --git a/app/src/protyle/upload/index.ts b/app/src/protyle/upload/index.ts index 7d14b14c2..0925c382e 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, {behavior: "smooth"}); + scrollCenter(protyle, undefined, "nearest", "smooth"); }, hasImage ? 0 : Constants.TIMEOUT_LOAD); }; diff --git a/app/src/protyle/util/insertHTML.ts b/app/src/protyle/util/insertHTML.ts index bb2a3d854..e54cfc374 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, {behavior: "smooth"}); + scrollCenter(protyle, undefined, "nearest", "smooth"); }, Constants.TIMEOUT_LOAD); return; } diff --git a/app/src/protyle/util/onGet.ts b/app/src/protyle/util/onGet.ts index 4655a3670..ac7af0f78 100644 --- a/app/src/protyle/util/onGet.ts +++ b/app/src/protyle/util/onGet.ts @@ -490,10 +490,8 @@ const focusElementById = (protyle: IProtyle, action: string[], scrollAttr?: IScr // 下一个请求过来前需断开,否则 observerLoad 重新赋值后无法 disconnect https://ld246.com/article/1704612002446 protyle.observerLoad?.disconnect(); if (action.includes(Constants.CB_GET_FOCUS) || action.includes(Constants.CB_GET_SCROLL) || action.includes(Constants.CB_GET_HL) || action.includes(Constants.CB_GET_FOCUSFIRST)) { - const contentRect = protyle.contentElement.getBoundingClientRect(); - const focusRect = focusElement.getBoundingClientRect(); - if (!hasScrollTop && (contentRect.top > focusRect.top || contentRect.bottom < focusRect.bottom)) { - scrollCenter(protyle, focusElement, {position: "top"}); + if (!hasScrollTop) { + scrollCenter(protyle, focusElement); } } else { return; @@ -504,10 +502,8 @@ const focusElementById = (protyle: IProtyle, action: string[], scrollAttr?: IScr protyle.contentElement.scrollTop = scrollAttr.scrollTop; } if (action.includes(Constants.CB_GET_FOCUS) || action.includes(Constants.CB_GET_HL) || action.includes(Constants.CB_GET_FOCUSFIRST)) { - const contentRect = protyle.contentElement.getBoundingClientRect(); - const focusRect = focusElement.getBoundingClientRect(); - if (!hasScrollTop && (contentRect.top > focusRect.top || contentRect.bottom < focusRect.bottom)) { - scrollCenter(protyle, focusElement, {position: "top"}); + if (!hasScrollTop) { + scrollCenter(protyle, focusElement); } } }); diff --git a/app/src/protyle/util/paste.ts b/app/src/protyle/util/paste.ts index f79ad4f3d..6c880f888 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, {behavior: "smooth"}); + scrollCenter(protyle, undefined, "nearest", "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, {behavior: "smooth"}); + scrollCenter(protyle, undefined, "nearest", "smooth"); } }; diff --git a/app/src/protyle/wysiwyg/move.ts b/app/src/protyle/wysiwyg/move.ts index f6007393e..118fee5d7 100644 --- a/app/src/protyle/wysiwyg/move.ts +++ b/app/src/protyle/wysiwyg/move.ts @@ -78,8 +78,8 @@ export const moveToUp = (protyle: IProtyle, nodeElement: HTMLElement, range: Ran } updateTransaction(protyle, previousElement.parentElement.parentElement.parentElement.getAttribute("data-node-id"), previousElement.parentElement.parentElement.parentElement.outerHTML, oldListHTML); preventScroll(protyle); - scrollCenter(protyle); focusByWbr(previousElement.parentElement, range); + scrollCenter(protyle); return; } if (!sourceElements[0].previousElementSibling || sourceElements[0].previousElementSibling?.classList.contains("protyle-action")) { @@ -176,8 +176,8 @@ export const moveToDown = (protyle: IProtyle, nodeElement: HTMLElement, range: R } updateTransaction(protyle, nextElement.parentElement.parentElement.parentElement.getAttribute("data-node-id"), nextElement.parentElement.parentElement.parentElement.outerHTML, oldListHTML); preventScroll(protyle); - scrollCenter(protyle); focusByWbr(nextElement.parentElement, range); + scrollCenter(protyle); return; } if (!sourceElements[sourceElements.length - 1].nextElementSibling || sourceElements[sourceElements.length - 1].nextElementSibling?.classList.contains("protyle-attr")) { diff --git a/app/src/util/backForward.ts b/app/src/util/backForward.ts index a46c31948..f9fc76328 100644 --- a/app/src/util/backForward.ts +++ b/app/src/util/backForward.ts @@ -104,7 +104,7 @@ const focusStack = async (app: App, stack: IBackStack) => { } }); focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end); - scrollCenter(protyle, blockElement); + scrollCenter(protyle); } return true; } else { @@ -136,7 +136,7 @@ const focusStack = async (app: App, stack: IBackStack) => { stack.protyle.model.parent.parent.switchTab(stack.protyle.model.parent.headElement); } focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end); - scrollCenter(stack.protyle, blockElement); + scrollCenter(stack.protyle); getAllModels().outline.forEach(item => { if (item.blockId === stack.protyle.block.rootID) { item.setCurrent(blockElement); @@ -179,7 +179,7 @@ const focusStack = async (app: App, stack: IBackStack) => { } }); focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end); - scrollCenter(stack.protyle, blockElement, {position: "top"}); + scrollCenter(stack.protyle); } }); }); @@ -207,7 +207,7 @@ const focusStack = async (app: App, stack: IBackStack) => { } }); focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end); - scrollCenter(stack.protyle, blockElement, {position: "top"}); + scrollCenter(stack.protyle); } }); return true; diff --git a/app/src/util/highlightById.ts b/app/src/util/highlightById.ts index 0305e7f8a..7f4cd9259 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, position: "auto" | "center" | "top" = "auto") => { +export const highlightById = (protyle: IProtyle, id: string, position: ScrollLogicalPosition = "nearest") => { 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, position: "auto" | } }); if (nodeElement) { - scrollCenter(protyle, nodeElement, {position}); + scrollCenter(protyle, nodeElement, position); bgFade(nodeElement); return nodeElement;// 仅配合前进后退使用 } @@ -41,13 +41,10 @@ export const highlightById = (protyle: IProtyle, id: string, position: "auto" | export const scrollCenter = ( protyle: IProtyle, nodeElement?: Element, - options?: { - position?: "top" | "center" | "auto"; - behavior?: ScrollBehavior; - } + position: ScrollLogicalPosition = "nearest", + behavior: ScrollBehavior = "auto" ) => { - const {position = "auto", behavior = "auto"} = options || {}; - if (!protyle.disabled && position === "auto" && getSelection().rangeCount > 0) { + if (!protyle.disabled && !nodeElement && getSelection().rangeCount > 0) { const range = getSelection().getRangeAt(0); const blockElement = hasClosestBlock(range.startContainer); if (blockElement) { @@ -55,7 +52,7 @@ export const scrollCenter = ( if (blockElement.classList.contains("code-block")) { const brElement = document.createElement("br"); range.insertNode(brElement); - brElement.scrollIntoView({block: "nearest", behavior}); + brElement.scrollIntoView({block: position, behavior}); brElement.remove(); return; } @@ -68,9 +65,9 @@ export const scrollCenter = ( } const activeElement = blockElement.querySelector(".av__cell--select, .av__row--select, .av__gallery-item--select"); if (activeElement) { - activeElement.scrollIntoView({block: "nearest", behavior}); + activeElement.scrollIntoView({block: position, behavior}); } else { - blockElement.scrollIntoView({block: "nearest", behavior}); + blockElement.scrollIntoView({block: position, behavior}); } return; } @@ -103,46 +100,34 @@ export const scrollCenter = ( if (!nodeElement) { return; } - - let offsetTop = 0; - let parentNodeElement = nodeElement; - while (parentNodeElement && !parentNodeElement.classList.contains("protyle-wysiwyg")) { - offsetTop += (parentNodeElement as HTMLElement).offsetTop; - parentNodeElement = parentNodeElement.parentElement; - } - let contentTop = 0; - let topElement = protyle.element.firstElementChild; - while (topElement && !topElement.classList.contains("protyle-content")) { - contentTop += topElement.clientHeight; - topElement = topElement.nextElementSibling; - } - 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) { + const elementRect = nodeElement.getBoundingClientRect(); + const contentRect = protyle.contentElement.getBoundingClientRect(); + if (position === "start") { protyle.contentElement.scroll({ - top: offsetTop + nodeElement.clientHeight - contentTop - protyle.contentElement.clientHeight, + top: protyle.contentElement.scrollTop + elementRect.top - contentRect.top, 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}); + if (position === "nearest") { + // 在可视区域内不进行滚动 + if (elementRect.top < contentRect.top) { + protyle.contentElement.scroll({ + top: protyle.contentElement.scrollTop + elementRect.top - contentRect.top, + behavior + }); + } else if (elementRect.bottom > contentRect.bottom) { + protyle.contentElement.scroll({ + top: protyle.contentElement.scrollTop + elementRect.bottom - contentRect.bottom, + behavior + }); + } + return; + } + if (position === "center") { + protyle.contentElement.scroll({ + top: protyle.contentElement.scrollTop + elementRect.top - (contentRect.top + contentRect.height / 2), + behavior + }); + } };