fix: 实现编辑器-预览滚动同步
问题: 分屏模式下编辑器滚动时,预览面板不跟随滚动。 实现: - Editor handleScroll 计算滚动比例,派发 editor-scroll 自定义事件 - Preview 监听 editor-scroll 事件,按比例设置 preview-panel scrollTop - isSyncingRef 防止双向循环触发 原理: 编辑器 scrollTop / (scrollHeight - clientHeight) = 预览 scrollTop / (scrollHeight - clientHeight)
This commit is contained in:
@@ -118,13 +118,18 @@ export function Editor() {
|
||||
}, 150)
|
||||
}, [getActiveTab, updateTabContent, setModified, updateLineNumbers, updateFileSizeDisplay])
|
||||
|
||||
// 滚动同步
|
||||
// 滚动同步:通知 Preview 跟随滚动
|
||||
const handleScroll = useCallback(() => {
|
||||
const textarea = textareaRef.current
|
||||
if (!textarea) return
|
||||
// 行号跟随
|
||||
if (lineNumbersRef.current) {
|
||||
lineNumbersRef.current.scrollTop = textarea.scrollTop
|
||||
}
|
||||
// 计算滚动比例,派发给 Preview
|
||||
const maxScroll = textarea.scrollHeight - textarea.clientHeight
|
||||
const ratio = maxScroll > 0 ? textarea.scrollTop / maxScroll : 0
|
||||
window.dispatchEvent(new CustomEvent('editor-scroll', { detail: { ratio } }))
|
||||
}, [])
|
||||
|
||||
// Tab 键支持
|
||||
|
||||
@@ -6,12 +6,13 @@ export function Preview() {
|
||||
const [html, setHtml] = useState('')
|
||||
const previewRef = useRef<HTMLDivElement>(null)
|
||||
const requestIdRef = useRef(0)
|
||||
const isSyncingRef = useRef(false)
|
||||
|
||||
const activeTabId = useTabStore(s => s.activeTabId)
|
||||
const tabs = useTabStore(s => s.tabs)
|
||||
const activeTab = tabs.find(t => t.id === activeTabId) ?? null
|
||||
|
||||
// B-03 + M-07: 响应式渲染(无轮询,无竞态)
|
||||
// 响应式渲染(无轮询,无竞态)
|
||||
useEffect(() => {
|
||||
if (!activeTab) {
|
||||
setHtml('')
|
||||
@@ -20,13 +21,30 @@ export function Preview() {
|
||||
|
||||
const requestId = ++requestIdRef.current
|
||||
renderMarkdown(activeTab.content, activeTab.filePath).then(result => {
|
||||
// M-07: 只有当 requestId 仍为最新时才更新
|
||||
if (requestId === requestIdRef.current) {
|
||||
setHtml(result)
|
||||
}
|
||||
})
|
||||
}, [activeTabId, activeTab?.content])
|
||||
|
||||
// 滚动同步:监听编辑器滚动事件
|
||||
useEffect(() => {
|
||||
const handleEditorScroll = (e: Event) => {
|
||||
const ratio = (e as CustomEvent).detail?.ratio ?? 0
|
||||
const panel = previewRef.current?.parentElement
|
||||
if (!panel || isSyncingRef.current) return
|
||||
|
||||
isSyncingRef.current = true
|
||||
const maxScroll = panel.scrollHeight - panel.clientHeight
|
||||
panel.scrollTop = ratio * maxScroll
|
||||
// 下一帧重置标志
|
||||
requestAnimationFrame(() => { isSyncingRef.current = false })
|
||||
}
|
||||
|
||||
window.addEventListener('editor-scroll', handleEditorScroll)
|
||||
return () => window.removeEventListener('editor-scroll', handleEditorScroll)
|
||||
}, [])
|
||||
|
||||
// 拦截链接点击
|
||||
const handleClick = useCallback((e: React.MouseEvent) => {
|
||||
const link = (e.target as HTMLElement).closest('a')
|
||||
|
||||
Reference in New Issue
Block a user