去除/整理性能调试相关代码
This commit is contained in:
@@ -17,12 +17,6 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
||||
|
||||
// 性能监控
|
||||
const ENABLE_PERF_MONITOR = false // 生产环境关闭
|
||||
let drawCount = 0
|
||||
let drawTotalTime = 0
|
||||
let lastReportTime = 0
|
||||
|
||||
// 缓存 canvas 上下文和尺寸信息,避免重复初始化
|
||||
let cachedCtx: CanvasRenderingContext2D | null = null
|
||||
let cachedWidth = 0
|
||||
@@ -75,8 +69,6 @@ const initCanvas = () => {
|
||||
* @param isValidTarget 是否是合法的连接目标
|
||||
* @param errorMessage 错误提示消息
|
||||
*/
|
||||
// 🔧 调试开关:跳过实际绘制
|
||||
const DEBUG_SKIP_ACTUAL_DRAW = false
|
||||
const USE_SIMPLE_LINE = true // true = 使用简单直线替代贝塞尔曲线(性能更好)
|
||||
|
||||
const draw = (
|
||||
@@ -87,14 +79,10 @@ const draw = (
|
||||
isValidTarget = true,
|
||||
errorMessage = '',
|
||||
) => {
|
||||
// 🔧 调试:跳过实际绘制
|
||||
if (DEBUG_SKIP_ACTUAL_DRAW) return
|
||||
|
||||
const startTime = ENABLE_PERF_MONITOR ? performance.now() : 0
|
||||
|
||||
const ctx = initCanvas()
|
||||
if (!ctx) {
|
||||
console.warn('[LinkDragGuide] initCanvas returned null')
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('[LinkDragGuide] initCanvas returned null')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -132,8 +120,10 @@ const draw = (
|
||||
const color = isValidTarget ? '#67c23a' : '#f56c6c'
|
||||
ctx.strokeStyle = color
|
||||
ctx.lineWidth = 3
|
||||
// 🔧 测试:移除虚线,看是否是虚线+贝塞尔曲线导致的性能问题
|
||||
// ctx.setLineDash([8, 4])
|
||||
// 虚线绘制也会导致巨大的性能开销,慎用
|
||||
if (!USE_SIMPLE_LINE) {
|
||||
ctx.setLineDash([8, 4])
|
||||
}
|
||||
ctx.globalAlpha = 0.8
|
||||
|
||||
// 绘制直线
|
||||
@@ -190,22 +180,6 @@ const draw = (
|
||||
}
|
||||
|
||||
ctx.restore()
|
||||
|
||||
if (ENABLE_PERF_MONITOR) {
|
||||
drawCount++
|
||||
drawTotalTime += performance.now() - startTime
|
||||
|
||||
const now = Date.now()
|
||||
if (now - lastReportTime > 1000) {
|
||||
const avgTime = drawCount > 0 ? (drawTotalTime / drawCount).toFixed(3) : 0
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`[LinkDragGuide Perf] 绘制次数: ${drawCount}/秒, 平均耗时: ${avgTime}ms`)
|
||||
|
||||
drawCount = 0
|
||||
drawTotalTime = 0
|
||||
lastReportTime = now
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+60
-157
@@ -8,6 +8,7 @@ import LinkDragGuide from './LinkDragGuide.vue'
|
||||
import { useI18n } from '../composables/useI18n'
|
||||
import type { TaskBarConfig } from '../models/configs/TaskBarConfig'
|
||||
import { getPredecessorIds } from '../utils/predecessorUtils'
|
||||
import { perfMonitor } from '../utils/perfMonitor'
|
||||
import type { Task } from '../models/classes/Task'
|
||||
import type { Milestone } from '../models/classes/Milestone'
|
||||
import type { TimelineConfig } from '../models/configs/TimelineConfig'
|
||||
@@ -648,9 +649,6 @@ const setHighlightTask = (taskId: number) => {
|
||||
}
|
||||
|
||||
// ==================== 连接线拖拽状态管理 ====================
|
||||
// 🔧 调试开关:禁用传递给 TaskBar 的响应式 props 以测试 Vue 渲染性能影响
|
||||
const DISABLE_REACTIVE_PROPS_TO_TASKBAR = false // 设为 false 以启用正常的响应式 props(link anchor 需要此功能)
|
||||
|
||||
const dragLinkMode = ref<'predecessor' | 'successor' | null>(null) // 当前拖拽模式
|
||||
const linkDragSourceTask = shallowRef<Task | null>(null) // 拖拽起始任务(使用 shallowRef 优化性能)
|
||||
const linkDragTargetTask = shallowRef<Task | null>(null) // 当前悬停的目标任务(使用 shallowRef 优化性能)
|
||||
@@ -662,71 +660,11 @@ const linkAutoScrollInterval = ref<number | null>(null) // 自动滚动定时器
|
||||
let nonReactiveIsValidTarget = false
|
||||
let nonReactiveErrorMessage = ''
|
||||
|
||||
// 🔧 调试用:用于传递给 TaskBar 的静态替代值(避免触发 Vue 更新)
|
||||
const staticDragLinkMode = computed(() =>
|
||||
DISABLE_REACTIVE_PROPS_TO_TASKBAR ? null : dragLinkMode.value,
|
||||
)
|
||||
const staticLinkDragSourceTaskId = computed(() =>
|
||||
DISABLE_REACTIVE_PROPS_TO_TASKBAR ? null : linkDragSourceTask.value?.id,
|
||||
)
|
||||
const staticLinkDragTargetTaskId = computed(() =>
|
||||
DISABLE_REACTIVE_PROPS_TO_TASKBAR ? null : linkDragTargetTask.value?.id,
|
||||
)
|
||||
const staticIsValidLinkTarget = computed(() =>
|
||||
DISABLE_REACTIVE_PROPS_TO_TASKBAR ? false : isValidLinkTarget.value,
|
||||
)
|
||||
|
||||
// 任务查找缓存 Map(优化性能,避免每次都遍历数组)
|
||||
const taskIdMap = new Map<number, Task>()
|
||||
|
||||
// 性能监控开关(开发调试用)
|
||||
const ENABLE_PERF_MONITOR = true
|
||||
const perfStats = {
|
||||
coordUpdateCount: 0,
|
||||
coordUpdateTotalTime: 0,
|
||||
targetDetectCount: 0,
|
||||
targetDetectTotalTime: 0,
|
||||
lastReportTime: 0,
|
||||
}
|
||||
|
||||
// 帧时间监控(诊断主线程阻塞)
|
||||
let frameMonitorId: number | null = null
|
||||
let lastFrameTime = 0
|
||||
let longFrameCount = 0
|
||||
let frameCount = 0
|
||||
|
||||
const startFrameMonitor = () => {
|
||||
lastFrameTime = performance.now()
|
||||
longFrameCount = 0
|
||||
frameCount = 0
|
||||
|
||||
const checkFrame = () => {
|
||||
const now = performance.now()
|
||||
const frameTime = now - lastFrameTime
|
||||
frameCount++
|
||||
|
||||
// 超过 30ms 的帧被认为是"长帧"(正常应该是 ~16ms)
|
||||
if (frameTime > 30) {
|
||||
longFrameCount++
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[Frame Monitor] 长帧检测: ${frameTime.toFixed(1)}ms`)
|
||||
}
|
||||
|
||||
lastFrameTime = now
|
||||
frameMonitorId = requestAnimationFrame(checkFrame)
|
||||
}
|
||||
|
||||
frameMonitorId = requestAnimationFrame(checkFrame)
|
||||
}
|
||||
|
||||
const stopFrameMonitor = () => {
|
||||
if (frameMonitorId !== null) {
|
||||
cancelAnimationFrame(frameMonitorId)
|
||||
frameMonitorId = null
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`[Frame Monitor] 统计: ${frameCount}帧, 长帧数: ${longFrameCount} (${(longFrameCount / frameCount * 100).toFixed(1)}%)`)
|
||||
}
|
||||
}
|
||||
|
||||
// 开始连接线拖拽
|
||||
const handleLinkDragStart = (event: { task: Task; type: 'predecessor' | 'successor'; mouseEvent: MouseEvent }) => {
|
||||
@@ -734,7 +672,7 @@ const handleLinkDragStart = (event: { task: Task; type: 'predecessor' | 'success
|
||||
linkDragSourceTask.value = event.task
|
||||
|
||||
// 启动帧监控
|
||||
startFrameMonitor()
|
||||
perfMonitor.startFrameMonitor()
|
||||
|
||||
// 初始化鼠标坐标(使用非响应式版本)
|
||||
updateLinkDragCoordinatesNonReactive(event.mouseEvent.clientX, event.mouseEvent.clientY)
|
||||
@@ -781,11 +719,23 @@ const processLinkDragFrame = () => {
|
||||
|
||||
// 🔧 调试:逐步启用各操作
|
||||
if (DEBUG_ENABLE_COORD_UPDATE) {
|
||||
updateLinkDragCoordinatesNonReactive(pendingMouseX, pendingMouseY)
|
||||
if (ENABLE_PERF_MONITOR) {
|
||||
const startTime = performance.now()
|
||||
updateLinkDragCoordinatesNonReactive(pendingMouseX, pendingMouseY)
|
||||
perfMonitor.recordLinkDragCoordUpdate(performance.now() - startTime)
|
||||
} else {
|
||||
updateLinkDragCoordinatesNonReactive(pendingMouseX, pendingMouseY)
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG_ENABLE_TARGET_DETECT) {
|
||||
detectLinkTargetNonReactive(pendingMouseX, pendingMouseY)
|
||||
if (ENABLE_PERF_MONITOR) {
|
||||
const startTime = performance.now()
|
||||
detectLinkTargetNonReactive(pendingMouseX, pendingMouseY)
|
||||
perfMonitor.recordLinkDragTargetDetect(performance.now() - startTime)
|
||||
} else {
|
||||
detectLinkTargetNonReactive(pendingMouseX, pendingMouseY)
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG_ENABLE_CANVAS_DRAW) {
|
||||
@@ -824,7 +774,7 @@ const handleGlobalMouseUp = () => {
|
||||
if (!dragLinkMode.value) return
|
||||
|
||||
// 停止帧监控
|
||||
stopFrameMonitor()
|
||||
perfMonitor.stopFrameMonitor()
|
||||
|
||||
// 🚀 取消待处理的 RAF
|
||||
if (linkDragRafId !== null) {
|
||||
@@ -859,22 +809,14 @@ let bodyRectCacheTime = 0
|
||||
const BODY_RECT_CACHE_DURATION = 200 // 200ms 缓存(优化:增加缓存时间)
|
||||
let bodyRectInvalidated = false // 缓存失效标记(滚动时失效)
|
||||
|
||||
// 🚀 非响应式坐标更新(完全绕过 Vue 响应式系统)
|
||||
const updateLinkDragCoordinatesNonReactive = (mouseX: number, mouseY: number) => {
|
||||
const startTime = ENABLE_PERF_MONITOR ? performance.now() : 0
|
||||
|
||||
// 🚀 坐标更新辅助函数(提取重复逻辑)
|
||||
const updateCoordinates = (mouseX: number, mouseY: number): void => {
|
||||
if (!bodyContentRef.value) {
|
||||
currentDragX = mouseX
|
||||
currentDragY = mouseY
|
||||
|
||||
if (ENABLE_PERF_MONITOR) {
|
||||
perfStats.coordUpdateCount++
|
||||
perfStats.coordUpdateTotalTime += performance.now() - startTime
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 使用缓存的 rect 或获取新的
|
||||
const now = Date.now()
|
||||
const shouldRefreshRect =
|
||||
!cachedBodyRect || bodyRectInvalidated || now - bodyRectCacheTime > BODY_RECT_CACHE_DURATION
|
||||
@@ -885,36 +827,11 @@ const updateLinkDragCoordinatesNonReactive = (mouseX: number, mouseY: number) =>
|
||||
}
|
||||
currentDragX = mouseX - cachedBodyRect.left
|
||||
currentDragY = mouseY - cachedBodyRect.top
|
||||
}
|
||||
|
||||
if (ENABLE_PERF_MONITOR) {
|
||||
perfStats.coordUpdateCount++
|
||||
perfStats.coordUpdateTotalTime += performance.now() - startTime
|
||||
|
||||
// 每秒输出一次性能统计
|
||||
if (now - perfStats.lastReportTime > 1000) {
|
||||
const avgCoordTime =
|
||||
perfStats.coordUpdateCount > 0
|
||||
? (perfStats.coordUpdateTotalTime / perfStats.coordUpdateCount).toFixed(3)
|
||||
: 0
|
||||
const avgTargetTime =
|
||||
perfStats.targetDetectCount > 0
|
||||
? (perfStats.targetDetectTotalTime / perfStats.targetDetectCount).toFixed(3)
|
||||
: 0
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`[LinkDrag Perf] 坐标更新: ${perfStats.coordUpdateCount}次, 平均${avgCoordTime}ms | ` +
|
||||
`目标检测: ${perfStats.targetDetectCount}次, 平均${avgTargetTime}ms`,
|
||||
)
|
||||
|
||||
// 重置统计
|
||||
perfStats.coordUpdateCount = 0
|
||||
perfStats.coordUpdateTotalTime = 0
|
||||
perfStats.targetDetectCount = 0
|
||||
perfStats.targetDetectTotalTime = 0
|
||||
perfStats.lastReportTime = now
|
||||
}
|
||||
}
|
||||
// 🚀 非响应式坐标更新(完全绕过 Vue 响应式系统)
|
||||
const updateLinkDragCoordinatesNonReactive = (mouseX: number, mouseY: number) => {
|
||||
updateCoordinates(mouseX, mouseY)
|
||||
}
|
||||
|
||||
// 拖拽过程中更新鼠标位置(🚀 优化:使用统一 RAF 调度)
|
||||
@@ -964,25 +881,17 @@ const handleLinkDragEnd = (event: { task: Task; type: 'predecessor' | 'successor
|
||||
const ANCHOR_SIZE = 8 // 触点视觉大小(px)
|
||||
const ANCHOR_TOLERANCE = 4 // 碰撞容差(px),扩大点击区域
|
||||
|
||||
// 🚀 非响应式目标检测(完全绕过 Vue 响应式系统)
|
||||
// 缓存当前检测到的目标任务(用于 handleLinkDragEnd)
|
||||
let nonReactiveTargetTask: Task | null = null
|
||||
|
||||
const detectLinkTargetNonReactive = (mouseX: number, mouseY: number) => {
|
||||
const startTime = ENABLE_PERF_MONITOR ? performance.now() : 0
|
||||
|
||||
if (!linkDragSourceTask.value || !bodyContentRef.value) return
|
||||
|
||||
// 🚀 目标检测辅助函数(提取重复逻辑)
|
||||
const detectTargetTaskId = (mouseX: number, mouseY: number): number | null => {
|
||||
// 使用缓存的 rect
|
||||
if (!cachedBodyRect) {
|
||||
cachedBodyRect = bodyContentRef.value.getBoundingClientRect()
|
||||
cachedBodyRect = bodyContentRef.value!.getBoundingClientRect()
|
||||
bodyRectCacheTime = Date.now()
|
||||
}
|
||||
|
||||
const relativeX = mouseX - cachedBodyRect.left
|
||||
const relativeY = mouseY - cachedBodyRect.top
|
||||
|
||||
let foundTaskId: number | null = null
|
||||
const isPredecessorMode = dragLinkMode.value === 'predecessor'
|
||||
const halfSize = (ANCHOR_SIZE + ANCHOR_TOLERANCE) / 2
|
||||
const expandedHalfSize = halfSize + 10
|
||||
@@ -998,40 +907,27 @@ const detectLinkTargetNonReactive = (mouseX: number, mouseY: number) => {
|
||||
continue
|
||||
}
|
||||
|
||||
if (isPredecessorMode) {
|
||||
const anchorX = pos.left + pos.width
|
||||
if (relativeX < anchorX - expandedHalfSize || relativeX > anchorX + expandedHalfSize) {
|
||||
continue
|
||||
}
|
||||
const anchorY = pos.top + pos.height / 2
|
||||
if (
|
||||
relativeX >= anchorX - halfSize &&
|
||||
relativeX <= anchorX + halfSize &&
|
||||
relativeY >= anchorY - halfSize &&
|
||||
relativeY <= anchorY + halfSize
|
||||
) {
|
||||
foundTaskId = taskId
|
||||
break
|
||||
}
|
||||
} else {
|
||||
const anchorX = pos.left
|
||||
if (relativeX < anchorX - expandedHalfSize || relativeX > anchorX + expandedHalfSize) {
|
||||
continue
|
||||
}
|
||||
const anchorY = pos.top + pos.height / 2
|
||||
if (
|
||||
relativeX >= anchorX - halfSize &&
|
||||
relativeX <= anchorX + halfSize &&
|
||||
relativeY >= anchorY - halfSize &&
|
||||
relativeY <= anchorY + halfSize
|
||||
) {
|
||||
foundTaskId = taskId
|
||||
break
|
||||
}
|
||||
const anchorX = isPredecessorMode ? pos.left + pos.width : pos.left
|
||||
if (relativeX < anchorX - expandedHalfSize || relativeX > anchorX + expandedHalfSize) {
|
||||
continue
|
||||
}
|
||||
|
||||
const anchorY = pos.top + pos.height / 2
|
||||
if (
|
||||
relativeX >= anchorX - halfSize &&
|
||||
relativeX <= anchorX + halfSize &&
|
||||
relativeY >= anchorY - halfSize &&
|
||||
relativeY <= anchorY + halfSize
|
||||
) {
|
||||
return taskId
|
||||
}
|
||||
}
|
||||
|
||||
const foundTarget = foundTaskId !== null ? taskIdMap.get(foundTaskId) || null : null
|
||||
return null
|
||||
}
|
||||
|
||||
// 🚀 更新目标任务的状态
|
||||
const updateTargetTaskState = (foundTarget: Task | null): void => {
|
||||
const currentTargetId = nonReactiveTargetTask?.id ?? null
|
||||
const newTargetId = foundTarget?.id ?? null
|
||||
|
||||
@@ -1042,7 +938,6 @@ const detectLinkTargetNonReactive = (mouseX: number, mouseY: number) => {
|
||||
const validation = validateLink(linkDragSourceTask.value, foundTarget, dragLinkMode.value!)
|
||||
nonReactiveIsValidTarget = validation.valid
|
||||
nonReactiveErrorMessage = validation.error || ''
|
||||
// 🚀 同步更新响应式变量(仅在目标变化时,用于 handleLinkDragEnd)
|
||||
isValidLinkTarget.value = validation.valid
|
||||
linkDragTargetTask.value = foundTarget
|
||||
} else {
|
||||
@@ -1052,11 +947,19 @@ const detectLinkTargetNonReactive = (mouseX: number, mouseY: number) => {
|
||||
linkDragTargetTask.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ENABLE_PERF_MONITOR) {
|
||||
perfStats.targetDetectCount++
|
||||
perfStats.targetDetectTotalTime += performance.now() - startTime
|
||||
}
|
||||
// 🚀 非响应式目标检测(完全绕过 Vue 响应式系统)
|
||||
// 缓存当前检测到的目标任务(用于 handleLinkDragEnd)
|
||||
let nonReactiveTargetTask: Task | null = null
|
||||
|
||||
const detectLinkTargetNonReactive = (mouseX: number, mouseY: number) => {
|
||||
if (!linkDragSourceTask.value || !bodyContentRef.value) return
|
||||
|
||||
const foundTaskId = detectTargetTaskId(mouseX, mouseY)
|
||||
const foundTarget = foundTaskId !== null ? taskIdMap.get(foundTaskId) || null : null
|
||||
|
||||
updateTargetTaskState(foundTarget)
|
||||
}
|
||||
|
||||
// 验证连接是否有效(返回 { valid: boolean, error?: string })
|
||||
@@ -4395,13 +4298,13 @@ const handleAddSuccessor = (task: Task) => {
|
||||
:is-highlighted="highlightedTaskIds.has(task.id)"
|
||||
:is-primary-highlight="highlightedTaskId === task.id"
|
||||
:is-in-highlight-mode="isInHighlightMode"
|
||||
:drag-link-mode="staticDragLinkMode"
|
||||
:is-link-drag-source="staticLinkDragSourceTaskId === task.id"
|
||||
:drag-link-mode="dragLinkMode"
|
||||
:is-link-drag-source="linkDragSourceTask?.id === task.id"
|
||||
:is-valid-link-target="
|
||||
staticLinkDragTargetTaskId === task.id && staticIsValidLinkTarget === true
|
||||
linkDragTargetTask?.id === task.id && isValidLinkTarget === true
|
||||
"
|
||||
:is-invalid-link-target="
|
||||
staticLinkDragTargetTaskId === task.id && staticIsValidLinkTarget === false
|
||||
linkDragTargetTask?.id === task.id && isValidLinkTarget === false
|
||||
"
|
||||
@update:task="updateTask"
|
||||
@bar-mounted="handleBarMounted"
|
||||
|
||||
@@ -33,6 +33,38 @@ const drawStats: DrawStats = {
|
||||
|
||||
const MAX_RECENT_DRAWS = 100 // 保留最近 100 次绘制记录
|
||||
|
||||
// ==================== 连接线拖拽性能监控 ====================
|
||||
interface LinkDragStats {
|
||||
coordUpdateCount: number
|
||||
coordUpdateTotalTime: number
|
||||
targetDetectCount: number
|
||||
targetDetectTotalTime: number
|
||||
lastReportTime: number
|
||||
}
|
||||
|
||||
const linkDragStats: LinkDragStats = {
|
||||
coordUpdateCount: 0,
|
||||
coordUpdateTotalTime: 0,
|
||||
targetDetectCount: 0,
|
||||
targetDetectTotalTime: 0,
|
||||
lastReportTime: 0,
|
||||
}
|
||||
|
||||
// ==================== 帧时间监控 ====================
|
||||
interface FrameMonitorStats {
|
||||
frameMonitorId: number | null
|
||||
lastFrameTime: number
|
||||
longFrameCount: number
|
||||
frameCount: number
|
||||
}
|
||||
|
||||
const frameMonitorStats: FrameMonitorStats = {
|
||||
frameMonitorId: null,
|
||||
lastFrameTime: 0,
|
||||
longFrameCount: 0,
|
||||
frameCount: 0,
|
||||
}
|
||||
|
||||
export const perfMonitor = {
|
||||
/**
|
||||
* 记录性能日志(自动节流)
|
||||
@@ -260,4 +292,136 @@ export const perfMonitor = {
|
||||
)
|
||||
}, durationMs)
|
||||
},
|
||||
|
||||
// ==================== 连接线拖拽性能监控 API ====================
|
||||
|
||||
/**
|
||||
* 记录坐标更新操作
|
||||
* @param duration 操作耗时(毫秒)
|
||||
* @returns 是否应该输出性能报告(每秒输出一次)
|
||||
*/
|
||||
recordLinkDragCoordUpdate(duration: number): boolean {
|
||||
linkDragStats.coordUpdateCount++
|
||||
linkDragStats.coordUpdateTotalTime += duration
|
||||
|
||||
const now = Date.now()
|
||||
if (now - linkDragStats.lastReportTime > 1000) {
|
||||
const avgCoordTime =
|
||||
linkDragStats.coordUpdateCount > 0
|
||||
? (linkDragStats.coordUpdateTotalTime / linkDragStats.coordUpdateCount).toFixed(3)
|
||||
: 0
|
||||
const avgTargetTime =
|
||||
linkDragStats.targetDetectCount > 0
|
||||
? (linkDragStats.targetDetectTotalTime / linkDragStats.targetDetectCount).toFixed(3)
|
||||
: 0
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`[LinkDrag Perf] 坐标更新: ${linkDragStats.coordUpdateCount}次, 平均${avgCoordTime}ms | ` +
|
||||
`目标检测: ${linkDragStats.targetDetectCount}次, 平均${avgTargetTime}ms`,
|
||||
)
|
||||
|
||||
this.resetLinkDragStats()
|
||||
linkDragStats.lastReportTime = now
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
/**
|
||||
* 记录目标检测操作
|
||||
* @param duration 操作耗时(毫秒)
|
||||
*/
|
||||
recordLinkDragTargetDetect(duration: number): void {
|
||||
linkDragStats.targetDetectCount++
|
||||
linkDragStats.targetDetectTotalTime += duration
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置连接线拖拽性能统计
|
||||
*/
|
||||
resetLinkDragStats(): void {
|
||||
linkDragStats.coordUpdateCount = 0
|
||||
linkDragStats.coordUpdateTotalTime = 0
|
||||
linkDragStats.targetDetectCount = 0
|
||||
linkDragStats.targetDetectTotalTime = 0
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取连接线拖拽性能统计数据
|
||||
*/
|
||||
getLinkDragStats() {
|
||||
return {
|
||||
coordUpdateCount: linkDragStats.coordUpdateCount,
|
||||
coordUpdateTotalTime: linkDragStats.coordUpdateTotalTime,
|
||||
targetDetectCount: linkDragStats.targetDetectCount,
|
||||
targetDetectTotalTime: linkDragStats.targetDetectTotalTime,
|
||||
}
|
||||
},
|
||||
|
||||
// ==================== 帧时间监控 API ====================
|
||||
|
||||
/**
|
||||
* 启动帧时间监控(诊断主线程阻塞)
|
||||
* @param longFrameThreshold 长帧阈值(毫秒),默认 30ms
|
||||
*/
|
||||
startFrameMonitor(longFrameThreshold = 30): void {
|
||||
frameMonitorStats.lastFrameTime = performance.now()
|
||||
frameMonitorStats.longFrameCount = 0
|
||||
frameMonitorStats.frameCount = 0
|
||||
|
||||
const checkFrame = () => {
|
||||
const now = performance.now()
|
||||
const frameTime = now - frameMonitorStats.lastFrameTime
|
||||
frameMonitorStats.frameCount++
|
||||
|
||||
// 超过阈值的帧被认为是"长帧"
|
||||
if (frameTime > longFrameThreshold) {
|
||||
frameMonitorStats.longFrameCount++
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[Frame Monitor] 长帧检测: ${frameTime.toFixed(1)}ms`)
|
||||
}
|
||||
|
||||
frameMonitorStats.lastFrameTime = now
|
||||
frameMonitorStats.frameMonitorId = requestAnimationFrame(checkFrame)
|
||||
}
|
||||
|
||||
frameMonitorStats.frameMonitorId = requestAnimationFrame(checkFrame)
|
||||
},
|
||||
|
||||
/**
|
||||
* 停止帧时间监控并输出统计信息
|
||||
*/
|
||||
stopFrameMonitor(): void {
|
||||
if (frameMonitorStats.frameMonitorId !== null) {
|
||||
cancelAnimationFrame(frameMonitorStats.frameMonitorId)
|
||||
frameMonitorStats.frameMonitorId = null
|
||||
|
||||
const percentage =
|
||||
frameMonitorStats.frameCount > 0
|
||||
? (frameMonitorStats.longFrameCount / frameMonitorStats.frameCount * 100).toFixed(1)
|
||||
: 0
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`[Frame Monitor] 统计: ${frameMonitorStats.frameCount}帧, ` +
|
||||
`长帧数: ${frameMonitorStats.longFrameCount} (${percentage}%)`,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取帧时间监控统计数据
|
||||
*/
|
||||
getFrameMonitorStats() {
|
||||
return {
|
||||
frameCount: frameMonitorStats.frameCount,
|
||||
longFrameCount: frameMonitorStats.longFrameCount,
|
||||
percentage:
|
||||
frameMonitorStats.frameCount > 0
|
||||
? (frameMonitorStats.longFrameCount / frameMonitorStats.frameCount * 100).toFixed(1)
|
||||
: 0,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user