fix(title): 拖拽时用ref直接改DOM避免React重渲染导致换行
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 6m59s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 14m21s
PR Automation / Auto Approve on CI Green (pull_request) Failing after 14m22s
AI Code Review / AI Code Review (pull_request) Failing after 14m23s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 2m30s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 3m24s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 3m13s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 3m39s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 3m44s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 7m33s
CI/CD Pipeline / CI Gate (pull_request) Failing after 6s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 360h20m30s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 360h20m34s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 360h20m30s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 360h20m32s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 360h27m17s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 360h27m21s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 360h27m23s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 360h27m27s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 360h27m29s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 360h27m59s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 360h28m1s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 360h28m9s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 360h28m9s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 360h28m11s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 360h54m51s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 361h1m37s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 361h1m43s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 361h1m56s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 361h2m26s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 360h54m50s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 361h1m49s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 6m59s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 14m21s
PR Automation / Auto Approve on CI Green (pull_request) Failing after 14m22s
AI Code Review / AI Code Review (pull_request) Failing after 14m23s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 2m30s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 3m24s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 3m13s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 3m39s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 3m44s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 7m33s
CI/CD Pipeline / CI Gate (pull_request) Failing after 6s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 360h20m30s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 360h20m34s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 360h20m30s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 360h20m32s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 360h27m17s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 360h27m21s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 360h27m23s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 360h27m27s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 360h27m29s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 360h27m59s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 360h28m1s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 360h28m9s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 360h28m9s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 360h28m11s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 360h54m51s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 361h1m37s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 361h1m43s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 361h1m56s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 361h2m26s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 360h54m50s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 361h1m49s
handleTitlePointerMove 每次 mousemove 都调用 onTitlePositionChange 触发 React 频繁重渲染,wordBreak:break-word 导致标题文字换行。 修复:拖拽过程中用 titleDragRef 直接修改 DOM style.left/top, 不触发 React 渲染;只在 handleTitlePointerUp(拖拽结束)时调用 onTitlePositionChange 保存最终位置。
This commit is contained in:
@@ -141,6 +141,7 @@ const FrontendPreviewPlayer: React.FC<FrontendPreviewPlayerProps> = ({
|
||||
|
||||
// ── 拖拽状态(用 ref 避免在每帧渲染中触发重渲染)──
|
||||
const draggingTitleRef = useRef(false)
|
||||
const titleDragRef = useRef<HTMLDivElement>(null)
|
||||
const handleTitlePointerDown = useCallback(
|
||||
(e: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (!onTitlePositionChange || !playerContainerRef.current) return
|
||||
@@ -152,32 +153,45 @@ const FrontendPreviewPlayer: React.FC<FrontendPreviewPlayerProps> = ({
|
||||
},
|
||||
[onTitlePositionChange],
|
||||
)
|
||||
const handleTitlePointerMove = useCallback(
|
||||
(e: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (!draggingTitleRef.current || !onTitlePositionChange || !playerContainerRef.current) return
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
const handleTitlePointerMove = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (!draggingTitleRef.current || !playerContainerRef.current) return
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
// 拖拽过程中直接修改 DOM,不触发 React 渲染(避免频繁重渲染导致换行)
|
||||
if (titleDragRef.current) {
|
||||
const rect = playerContainerRef.current.getBoundingClientRect()
|
||||
const relX = Math.max(0, Math.min(rect.width, e.clientX - rect.left))
|
||||
const relY = Math.max(0, Math.min(rect.height, e.clientY - rect.top))
|
||||
const posX = Math.round((relX / rect.width) * playRes.width)
|
||||
const posY = Math.round((relY / rect.height) * playRes.height)
|
||||
onTitlePositionChange(posX, posY)
|
||||
const xpct = (relX / rect.width) * 100
|
||||
const ypct = (relY / rect.height) * 100
|
||||
titleDragRef.current.style.left = `${xpct}%`
|
||||
titleDragRef.current.style.top = `${ypct}%`
|
||||
}
|
||||
}, [])
|
||||
const handleTitlePointerUp = useCallback(
|
||||
(e: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (!draggingTitleRef.current) return
|
||||
draggingTitleRef.current = false
|
||||
// 拖拽结束时才调用 onTitlePositionChange 保存最终位置
|
||||
if (onTitlePositionChange && playerContainerRef.current) {
|
||||
const rect = playerContainerRef.current.getBoundingClientRect()
|
||||
const relX = Math.max(0, Math.min(rect.width, e.clientX - rect.left))
|
||||
const relY = Math.max(0, Math.min(rect.height, e.clientY - rect.top))
|
||||
const posX = Math.round((relX / rect.width) * playRes.width)
|
||||
const posY = Math.round((relY / rect.height) * playRes.height)
|
||||
onTitlePositionChange(posX, posY)
|
||||
}
|
||||
;(e.currentTarget as HTMLDivElement).style.cursor = "grab"
|
||||
try {
|
||||
if ((e.currentTarget as Element).hasPointerCapture(e.pointerId)) {
|
||||
;(e.currentTarget as Element).releasePointerCapture(e.pointerId)
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
},
|
||||
[onTitlePositionChange, playRes.width, playRes.height],
|
||||
)
|
||||
const handleTitlePointerUp = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (!draggingTitleRef.current) return
|
||||
draggingTitleRef.current = false
|
||||
;(e.currentTarget as HTMLDivElement).style.cursor = "grab"
|
||||
try {
|
||||
if ((e.currentTarget as Element).hasPointerCapture(e.pointerId)) {
|
||||
;(e.currentTarget as Element).releasePointerCapture(e.pointerId)
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}, [])
|
||||
|
||||
const playerContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [containerHeight, setContainerHeight] = useState(0)
|
||||
@@ -602,6 +616,7 @@ const FrontendPreviewPlayer: React.FC<FrontendPreviewPlayerProps> = ({
|
||||
padding: "8px 12px",
|
||||
boxShadow: "inset 0 0 0 16px transparent",
|
||||
}}
|
||||
ref={titleDragRef}
|
||||
onPointerDown={handleTitlePointerDown}
|
||||
onPointerMove={handleTitlePointerMove}
|
||||
onPointerUp={handleTitlePointerUp}
|
||||
|
||||
Reference in New Issue
Block a user