From 7b7f7ad970aa7bbe66fb2bfcc1e4312f3e7b5a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?sunsonliu=28=E5=88=98=E9=98=B3=29?= Date: Sun, 22 Dec 2024 12:52:41 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20#1005=20=E5=A2=9E=E5=8A=A0=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E5=88=B0=E5=85=B7=E4=BD=93=E4=BD=8D=E7=BD=AE=E7=9A=84?= =?UTF-8?q?api=EF=BC=8C=E5=A2=9E=E5=8A=A0=E6=BB=9A=E5=8A=A8=E8=A1=8C?= =?UTF-8?q?=E4=B8=BA=E6=8E=A7=E5=88=B6=E7=9A=84=E5=8F=82=E6=95=B0=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=A2=84=E8=A7=88=E5=8C=BA=E6=BB=9A=E5=8A=A8?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E8=A7=A6=E5=8F=91=E7=BC=96=E8=BE=91=E5=8C=BA?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/api.html | 14 ++++++++-- src/Previewer.js | 71 +++++++++++++++++++++++------------------------ 2 files changed, 47 insertions(+), 38 deletions(-) diff --git a/examples/api.html b/examples/api.html index d83604a3..1316cec2 100644 --- a/examples/api.html +++ b/examples/api.html @@ -209,8 +209,8 @@

getPreviewer()

-

Previewer.scrollToId(id:string)

-

滚动到对应id的元素位置
id 可以为带#号hash,也可以是id值

+

Previewer.scrollToId(id:string, behavior:{'smooth'|'instant'|'auto'})

+

滚动到对应id的元素位置
id 可以为带#号hash,也可以是id值
behavior: smooth(默认) 平滑滚动;instant 立即滚动;auto 跟随浏览器默认行为

+ 试一试 +
+
+

setLocale(locale:string)

修改语言
系统默认支持:zh_CN | en_US | ru_RU

diff --git a/src/Previewer.js b/src/Previewer.js index ee94ca42..550594ac 100644 --- a/src/Previewer.js +++ b/src/Previewer.js @@ -17,7 +17,7 @@ import vDH from 'virtual-dom/h'; import vDDiff from 'virtual-dom/diff'; import vDPatch from 'virtual-dom/patch'; import MyersDiff from './utils/myersDiff'; -import { getBlockTopAndHeightWithMargin, elementsFromPoint } from './utils/dom'; +import { getBlockTopAndHeightWithMargin } from './utils/dom'; import Logger from './Logger'; // import locale from './utils/locale'; import { addEvent, removeEvent } from './utils/event'; @@ -416,51 +416,34 @@ export default class Previewer { this.editor.scrollToLineNum(null); return; } - // 获取预览容器基准坐标 - const basePoint = domContainer.getBoundingClientRect(); - // 观察点坐标,取容器中轴线 - const watchPoint = { - x: basePoint.left + basePoint.width / 2, - y: basePoint.top + 1, - }; - // 获取观察点处的DOM - const targetElements = elementsFromPoint(watchPoint.x, watchPoint.y); + const domPosition = domContainer.getBoundingClientRect(); let targetElement; - for (let i = 0; i < targetElements.length; i++) { - if (domContainer.contains(targetElements[i])) { - targetElement = targetElements[i]; + let lines = 0; + const elements = domContainer.children; + for (let i = 0; i < elements.length; i++) { + const element = elements[i]; + if (element.getBoundingClientRect().top < domPosition.top) { + targetElement = element; + const currentLines = element.getAttribute('data-lines') ?? 0; + lines += +currentLines; + } else { break; } } - if (!targetElement || targetElement === domContainer) { - return; - } - // 获取观察点处最近的markdown元素 - let mdElement = targetElement.closest('[data-sign]'); - // 由于新增脚注,内部容器也有可能存在data-sign,所以需要循环往父级找 - while (mdElement && mdElement.parentElement && mdElement.parentElement !== domContainer) { - mdElement = mdElement.parentElement.closest('[data-sign]'); - } - if (!mdElement) { + if (!targetElement) { + this.editor.scrollToLineNum(0, 0, 1); return; } - // 计算当前焦点容器的所在行数 - let lines = 0; - let element = mdElement; - while (element) { - lines += +element.getAttribute('data-lines'); - element = element.previousElementSibling; // 取上一个兄弟节点,直到为null - } // markdown元素存在margin,getBoundingRect不能获取到margin - const mdElementStyle = getComputedStyle(mdElement); + const mdElementStyle = getComputedStyle(targetElement); const marginTop = parseFloat(mdElementStyle.marginTop); const marginBottom = parseFloat(mdElementStyle.marginBottom); // markdown元素基于当前页面的矩形模型 - const mdRect = mdElement.getBoundingClientRect(); + const mdRect = targetElement.getBoundingClientRect(); const mdActualHeight = mdRect.height + marginTop + marginBottom; // (mdRect.y - marginTop)为顶部触达区域,basePoint.y为预览区域的顶部,故可视范围应减去预览区域的偏移 - const mdOffsetTop = mdRect.y - marginTop - basePoint.y; - const lineNum = +mdElement.getAttribute('data-lines'); // 当前markdown元素所占行数 + const mdOffsetTop = mdRect.y - marginTop - domPosition.y; + const lineNum = +targetElement.getAttribute('data-lines'); // 当前markdown元素所占行数 const percent = (100 * Math.abs(mdOffsetTop)) / mdActualHeight / 100; // console.log('destLine:', lines, percent, // mdRect.height + marginTop + marginBottom, mdOffsetTop, mdElement); @@ -940,12 +923,28 @@ export default class Previewer { this.highlightLine(lineNum); } + /** + * 滚动到对应位置 + * @param {number} scrollTop 元素的id属性值 + * @param {'auto'|'smooth'|'instant'} behavior 滚动方式 + */ + scrollToTop(scrollTop, behavior = 'auto') { + const previewDom = this.getDomContainer(); + const scrollDom = this.getDomCanScroll(previewDom); + scrollDom.scrollTo({ + top: scrollTop, + left: 0, + behavior, + }); + } + /** * 滚动到对应id的位置,实现锚点滚动能力 * @param {string} id 元素的id属性值 + * @param {'smooth'|'instant'|'auto'} behavior 滚动方式 * @return {boolean} 是否有对应id的元素并执行滚动 */ - scrollToId(id) { + scrollToId(id, behavior = 'smooth') { const previewDom = this.getDomContainer(); const scrollDom = this.getDomCanScroll(previewDom); let $id = id.replace(/^\s*#/, '').trim(); @@ -963,7 +962,7 @@ export default class Previewer { scrollDom.scrollTo({ top: scrollTop, left: 0, - behavior: 'smooth', + behavior, }); }