diff --git a/app/src/editor/util.ts b/app/src/editor/util.ts index de1898220..d29a7fccd 100644 --- a/app/src/editor/util.ts +++ b/app/src/editor/util.ts @@ -27,6 +27,7 @@ import {resize} from "../protyle/util/resize"; import {Search} from "../search"; import {App} from "../index"; import {newCardModel} from "../card/newCardTab"; +import {preventScroll} from "../protyle/scroll/preventScroll"; export const checkFold = (id: string, cb: (zoomIn: boolean, action: string[]) => void) => { if (!id) { @@ -369,6 +370,8 @@ const switchEditor = (editor: Editor, options: IOpenFileOptions, allModels: IMod updateBacklinkGraph(allModels, editor.editor.protyle); }); } else { + // 点击大纲产生滚动时会动态加载内容,最终导致定位不准确 + preventScroll(editor.editor.protyle); if (options.action?.includes(Constants.CB_GET_HL)) { highlightById(editor.editor.protyle, options.id, true); } else if (options.action?.includes(Constants.CB_GET_FOCUS)) { diff --git a/app/src/protyle/scroll/event.ts b/app/src/protyle/scroll/event.ts index 61e56e4ab..7780ec815 100644 --- a/app/src/protyle/scroll/event.ts +++ b/app/src/protyle/scroll/event.ts @@ -39,7 +39,11 @@ export const scrollEvent = (protyle: IProtyle, element: HTMLElement) => { if (protyle.scroll && !protyle.scroll.element.classList.contains("fn__none")) { clearTimeout(getIndexTimeout); getIndexTimeout = window.setTimeout(() => { - const targetElement = document.elementFromPoint(elementRect.left + elementRect.width / 2, elementRect.top + 10); + let targetElement = document.elementFromPoint(elementRect.left + elementRect.width / 2, elementRect.top + 10); + if (targetElement.classList.contains("protyle-wysiwyg")) { + // 恰好定位到块的中间时 + targetElement = document.elementFromPoint(elementRect.left + elementRect.width / 2, elementRect.top + 20); + } const blockElement = hasClosestBlock(targetElement); if (!blockElement) { if ((protyle.wysiwyg.element.firstElementChild.getAttribute("data-eof") === "1" || diff --git a/app/src/protyle/util/destroy.ts b/app/src/protyle/util/destroy.ts index 23b3358cb..a592d4e94 100644 --- a/app/src/protyle/util/destroy.ts +++ b/app/src/protyle/util/destroy.ts @@ -5,6 +5,8 @@ export const destroy = (protyle: IProtyle) => { return; } hideElements(["util"], protyle); + protyle.observer?.disconnect(); + protyle.observerLoad?.disconnect(); protyle.element.classList.remove("protyle"); protyle.element.removeAttribute("style"); if (protyle.wysiwyg) { diff --git a/app/src/protyle/util/onGet.ts b/app/src/protyle/util/onGet.ts index 937dbfcf4..054a74f51 100644 --- a/app/src/protyle/util/onGet.ts +++ b/app/src/protyle/util/onGet.ts @@ -4,7 +4,7 @@ import {fetchPost} from "../../util/fetch"; import {processRender} from "./processCode"; import {highlightRender} from "../render/highlightRender"; import {blockRender} from "../render/blockRender"; -import {highlightById} from "../../util/highlightById"; +import {bgFade} from "../../util/highlightById"; /// #if !MOBILE import {pushBack} from "../../util/backForward"; /// #endif @@ -185,54 +185,21 @@ const setHTML = (options: { if (protyle.options.render.scroll) { protyle.scroll.update(protyle); } - if (options.scrollAttr) { - protyle.contentElement.scrollTop = options.scrollAttr.scrollTop; - if (options.action.includes(Constants.CB_GET_HL)) { - highlightById(protyle, options.scrollAttr.focusId, true); - } else if (options.action.includes(Constants.CB_GET_FOCUS)) { - if (options.scrollAttr.focusId) { - const range = focusByOffset(protyle.wysiwyg.element.querySelector(`[data-node-id="${options.scrollAttr.focusId}"]`), options.scrollAttr.focusStart, options.scrollAttr.focusEnd); - /// #if !MOBILE - if (!options.action.includes(Constants.CB_GET_UNUNDO)) { - pushBack(protyle, range || undefined); - } - /// #endif - } else { - focusElementById(protyle, options.action); - } + if (options.scrollAttr && !protyle.scroll.element.classList.contains("fn__none")) { + // 使用动态滚动条定位到最后一个块,重启后无法触发滚动事件,需要再次更新 index + protyle.scroll.updateIndex(protyle, options.scrollAttr.startId); + // https://github.com/siyuan-note/siyuan/issues/8224 + const contentRect = protyle.contentElement.getBoundingClientRect(); + if (protyle.wysiwyg.element.clientHeight - parseInt(protyle.wysiwyg.element.style.paddingBottom) < protyle.contentElement.clientHeight && + protyle.wysiwyg.element.lastElementChild.getBoundingClientRect().bottom < contentRect.bottom && + protyle.wysiwyg.element.firstElementChild.getBoundingClientRect().top > contentRect.top) { + showMessage(window.siyuan.languages.scrollGetMore); } - if (!protyle.scroll.element.classList.contains("fn__none")) { - // 使用动态滚动条定位到最后一个块,重启后无法触发滚动事件,需要再次更新 index - protyle.scroll.updateIndex(protyle, options.scrollAttr.startId); - // https://github.com/siyuan-note/siyuan/issues/8224 - const contentRect = protyle.contentElement.getBoundingClientRect(); - if (protyle.wysiwyg.element.clientHeight - parseInt(protyle.wysiwyg.element.style.paddingBottom) < protyle.contentElement.clientHeight && - protyle.wysiwyg.element.lastElementChild.getBoundingClientRect().bottom < contentRect.bottom && - protyle.wysiwyg.element.firstElementChild.getBoundingClientRect().top > contentRect.top) { - showMessage(window.siyuan.languages.scrollGetMore); - } - } - } else if (options.action.includes(Constants.CB_GET_HL)) { - preventScroll(protyle); // 搜索页签滚动会导致再次请求 - const hlElement = highlightById(protyle, protyle.block.id, true); - /// #if !MOBILE - if (hlElement && !options.action.includes(Constants.CB_GET_UNUNDO)) { - pushBack(protyle, undefined, hlElement); - } - /// #endif - } else if (options.action.includes(Constants.CB_GET_FOCUS)) { - focusElementById(protyle, options.action); } else if (options.action.includes(Constants.CB_GET_FOCUSFIRST)) { // settimeout 时间需短一点,否则定位后快速滚动无效 const headerHeight = protyle.wysiwyg.element.offsetTop - 16; preventScroll(protyle, headerHeight, 256); protyle.contentElement.scrollTop = headerHeight; - focusBlock(protyle.wysiwyg.element.firstElementChild); - /// #if !MOBILE - if (!options.action.includes(Constants.CB_GET_UNUNDO)) { - pushBack(protyle, undefined, protyle.wysiwyg.element.firstElementChild); - } - /// #endif } if (options.isSyncing) { disabledForeverProtyle(protyle); @@ -243,6 +210,9 @@ const setHTML = (options: { protyle.element.removeAttribute("disabled-forever"); setReadonlyByConfig(protyle); } + + focusElementById(protyle, options.action, options.scrollAttr); + if (options.action.includes(Constants.CB_GET_SETID)) { // 点击大纲后,如果需要动态加载,在定位后,需要重置 block.id https://github.com/siyuan-note/siyuan/issues/4487 protyle.block.id = protyle.block.rootID; @@ -269,7 +239,6 @@ const setHTML = (options: { onGet({data: getResponse, protyle, action: [Constants.CB_GET_APPEND, Constants.CB_GET_UNCHANGEID]}); }); } - if (options.action.includes(Constants.CB_GET_APPEND) || options.action.includes(Constants.CB_GET_BEFORE)) { protyle.app.plugins.forEach(item => { item.eventBus.emit("loaded-protyle-dynamic", { @@ -279,6 +248,7 @@ const setHTML = (options: { }); return; } + if (protyle.options.render.breadcrumb) { protyle.breadcrumb.toggleExit(!options.action.includes(Constants.CB_GET_ALL)); protyle.breadcrumb.render(protyle); @@ -387,37 +357,62 @@ export const enableProtyle = (protyle: IProtyle) => { hideTooltip(); }; -const focusElementById = (protyle: IProtyle, action: string[]) => { +const focusElementById = (protyle: IProtyle, action: string[], scrollAttr?: IScrollAttr) => { let focusElement: Element; - Array.from(protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${protyle.block.id}"]`)).find((item: HTMLElement) => { - if (!hasClosestByAttribute(item, "data-type", "block-render", true)) { - focusElement = item; - return true; + let range: Range; + if (scrollAttr && scrollAttr.focusId) { + focusElement = protyle.wysiwyg.element.querySelector(`[data-node-id="${scrollAttr.focusId}"]`) + if (action.includes(Constants.CB_GET_FOCUS)) { + range = focusByOffset(focusElement, scrollAttr.focusStart, scrollAttr.focusEnd) as Range; } - }); + } else { + Array.from(protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${protyle.block.id}"]`)).find((item: HTMLElement) => { + if (!hasClosestByAttribute(item, "data-type", "block-render", true)) { + focusElement = item; + return true; + } + }); + } if (protyle.block.mode === 4) { preventScroll(protyle); focusElement = protyle.wysiwyg.element.lastElementChild; + } else if (!focusElement || action.includes(Constants.CB_GET_FOCUSFIRST)) { + focusElement = protyle.wysiwyg.element.firstElementChild } - if (focusElement && !protyle.wysiwyg.element.firstElementChild.isSameNode(focusElement)) { + if (action.includes(Constants.CB_GET_HL)) { + preventScroll(protyle); // 搜索页签滚动会导致再次请求 + bgFade(focusElement); + } + if (action.includes(Constants.CB_GET_FOCUS) || action.includes(Constants.CB_GET_FOCUSFIRST)) { focusBlock(focusElement); /// #if !MOBILE if (!action.includes(Constants.CB_GET_UNUNDO)) { - pushBack(protyle, undefined, focusElement); + pushBack(protyle, range, focusElement); } /// #endif + } + if (action.includes(Constants.CB_GET_FOCUS) || action.includes(Constants.CB_GET_HL) || action.includes(Constants.CB_GET_FOCUSFIRST)) { focusElement.scrollIntoView(); - // 减少抖动 https://ld246.com/article/1654263598088 - setTimeout(() => { + } else if (scrollAttr && scrollAttr.scrollTop) { + protyle.contentElement.scrollTop = scrollAttr.scrollTop; + } + // 加强定位 + protyle.observerLoad = new ResizeObserver((entries) => { + if (action.includes(Constants.CB_GET_FOCUS) || action.includes(Constants.CB_GET_HL) || action.includes(Constants.CB_GET_FOCUSFIRST)) { focusElement.scrollIntoView(); - }, Constants.TIMEOUT_LOAD); - } else { - focusBlock(protyle.wysiwyg.element.firstElementChild); - /// #if !MOBILE - if (!action.includes(Constants.CB_GET_UNUNDO)) { - pushBack(protyle, undefined, protyle.wysiwyg.element.firstElementChild); + } else if (scrollAttr && scrollAttr.scrollTop) { + protyle.contentElement.scrollTop = scrollAttr.scrollTop; } - /// #endif + }); + protyle.observerLoad.observe(protyle.wysiwyg.element); + protyle.observer.unobserve(protyle.wysiwyg.element); + setTimeout(() => { + protyle.observerLoad.disconnect(); + protyle.observer.observe(protyle.wysiwyg.element); + }, 1000 * 16); + + if (focusElement.isSameNode(protyle.wysiwyg.element.firstElementChild)) { + protyle.observerLoad.disconnect(); } }; diff --git a/app/src/protyle/wysiwyg/index.ts b/app/src/protyle/wysiwyg/index.ts index 2b3ab1b00..6cba55206 100644 --- a/app/src/protyle/wysiwyg/index.ts +++ b/app/src/protyle/wysiwyg/index.ts @@ -1050,6 +1050,16 @@ export class WYSIWYG { } private bindEvent(protyle: IProtyle) { + // 删除块时,av 头尾需重新计算位置 + protyle.observer = new ResizeObserver((entries) => { + const contentRect = protyle.contentElement.getBoundingClientRect(); + protyle.wysiwyg.element.querySelectorAll(".av").forEach((item: HTMLElement) => { + if (item.querySelector(".av__title")) { + stickyRow(item, contentRect, "all"); + } + }); + }); + this.element.addEventListener("focusout", () => { if (getSelection().rangeCount === 0) { return; diff --git a/app/src/types/protyle.d.ts b/app/src/types/protyle.d.ts index 4ce2fd7fc..32cfd703b 100644 --- a/app/src/types/protyle.d.ts +++ b/app/src/types/protyle.d.ts @@ -431,6 +431,8 @@ interface IOptions { interface IProtyle { getInstance: () => import("../protyle").Protyle, + observerLoad?: ResizeObserver, + observer?: ResizeObserver, app: import("../index").App, transactionTime: number, id: string, diff --git a/app/src/util/highlightById.ts b/app/src/util/highlightById.ts index 2347a797e..4f9a121d8 100644 --- a/app/src/util/highlightById.ts +++ b/app/src/util/highlightById.ts @@ -1,7 +1,7 @@ import {hasClosestBlock, hasClosestByAttribute} from "../protyle/util/hasClosest"; import {getEditorRange, getSelectionPosition} from "../protyle/util/selection"; -const bgFade = (element: HTMLElement) => { +export const bgFade = (element: Element) => { element.classList.add("protyle-wysiwyg--hl"); setTimeout(function () { element.classList.remove("protyle-wysiwyg--hl");