Skip to content

交互状态

InteractionController 是图表的交互中枢,将 DOM 事件转化为业务操作。其内部状态分为以下几类:


一、拖拽状态

状态类型用途
isDraggingboolean标记是否正在拖拽,决定 mousemove 时执行平移还是 hover 逻辑
dragStartXnumber拖拽起点 X,用于计算水平平移距离
dragStartYnumber拖拽起点 Y,用于计算垂直平移增量
scrollStartXnumber拖拽起始的 scrollLeft,平移时以此为基准加减偏移
activePaneIdOnDragstring | null拖拽发生时鼠标所在面板,垂直拖拽时只平移该面板的价格轴

二、触摸状态

状态类型用途
isTouchSessionboolean标记当前为触摸会话,阻止触摸触发的模拟 mouse 事件干扰鼠标处理器

三、十字线与悬浮状态

状态类型用途
crosshairPos{x,y} | null十字线绘制坐标(像素),传给渲染层画十字光标
crosshairIndexnumber | null十字线指向的 K 线在数据中的绝对索引,用于显示价格、时间等
hoveredIndexnumber | null鼠标实际命中 candle 的 K 线索引,用于显示 tooltip
activePaneIdstring | null当前鼠标所在面板 ID,十字线只显示在该面板**(@Todo:疑似无用状态)**
tooltipPos{x,y}tooltip 最终显示位置,经过防溢出计算
tooltipSize{width,height}tooltip 尺寸,用于防溢出定位

四、标记(Marker)状态

状态类型用途
hoveredMarkerIdstring | null当前悬浮的 marker ID,用于高亮渲染
hoveredMarkerDataMarkerEntity | null悬浮 marker 的完整数据,传给外部回调
clickedMarkerIdstring | null当前点击的 marker ID
clickedMarkerDataMarkerEntity | null点击 marker 的完整数据,传给外部回调
hoveredCustomMarkerCustomMarkerEntity | null悬浮的自定义标记数据

五、帧缓存

状态类型用途
kLinePositionsnumber[] | null当前帧每根 K 线起始 X 坐标,用于二分查找鼠标指向的 K 线
visibleRange{start,end} | null当前帧可视 K 线范围,将局部索引转为绝对索引
kWidthPxnumber | nullK 线物理宽度,用于计算十字线吸附位置

六、回调函数

状态类型用途
onMarkerHoverCallbackFunction量价标记悬浮时通知外部(如显示 tooltip)
onMarkerClickCallbackFunction量价标记点击时通知外部
onCustomMarkerHoverCallbackFunction自定义标记悬浮时通知外部
onCustomMarkerClickCallbackFunction自定义标记点击时通知外部

状态流转简图

用户操作 → 事件处理器 → 更新状态 → scheduleDraw → 渲染层读取状态绘制
                ↑                        ↓
          reset() ←——— updateData() ←——— 外部数据变更

数据更新时调用 reset(),将拖拽、触摸、十字线、标记、帧缓存五类状态全部清零,避免基于旧数据的脏状态导致渲染异常。

交互事件

InteractionController 通过以下事件处理器承接 DOM 事件,转化为状态变更和图表操作。


1. 滚轮缩放

方法触发逻辑
onWheel(e)wheel计算鼠标在容器内的 X 坐标 → 清除 hover 状态 → 调用 chart.zoomAt() 以鼠标位置为中心缩放

2. 鼠标事件(非触摸)

方法触发逻辑
onMouseDown(e)mousedown触摸会话中忽略;优先做 marker 命中测试,命中则触发 click 回调并返回;未命中则进入拖拽模式,记录起点坐标和 scrollLeft
onMouseMove(e)mousemove拖拽中 → 水平更新 scrollLeft,垂直调用 translatePrice 平移价格轴;非拖拽 → 调用 updateHover 更新十字线和悬浮状态
onMouseUp()mouseup退出拖拽模式
onMouseLeave()mouseleave退出拖拽模式 + 清除 hover 状态 + 重绘

3. 指针事件(统一触屏和鼠标)

方法触发逻辑
onPointerDown(e)pointerdown只处理主指针;标记 isTouchSession;优先 marker 命中测试,命中则触发 click 回调;未命中则进入拖拽模式
onPointerMove(e)pointermove只处理主指针;拖拽中处理水平和垂直平移;非拖拽中调用 updateHoverFromPoint 更新悬浮状态
onPointerUp(e)pointerup退出拖拽模式
onPointerLeave(e)pointerleave退出拖拽模式 + 清除触摸会话标记 + 清除 hover 状态 + 重绘

双事件体系说明onPointer* 覆盖所有指针设备,onMouse* 专门处理鼠标。通过 isTouchSession 标记防止触屏事件同时触发两套处理器。


4. 滚动事件

方法触发逻辑
onScroll()scroll清空 kLinePositionsvisibleRange 缓存 → 清除 hover → 请求重绘

滚动时 K 线坐标缓存失效,清空后下一帧 draw() 会重新计算并注入。


5. Hover 更新流程

updateHover / updateHoverFromPoint 的优先级链:

1. 边界检查(鼠标在绘图区域内?)

2. 量价 marker 命中测试 → 命中则更新 marker hover,跳过十字线

3. 自定义标记命中测试 → 命中则更新 custom marker hover,跳过十字线

4. 二分查找鼠标对应的 K 线索引

5. 确定鼠标所在 pane → 更新 activePaneId

6. 计算十字线吸附位置(吸附到 K 线中心)

7. Candle 命中测试(body / wick)→ 更新 hoveredIndex 和 tooltip 防溢出位置

6. 外部回调

回调触发时机用途
onMarkerHoverCallbackmarker 悬浮/离开通知外部显示/隐藏 marker tooltip
onMarkerClickCallbackmarker 被点击通知外部处理 marker 点击
onCustomMarkerHoverCallback自定义标记悬浮/离开通知外部显示/隐藏自定义标记 tooltip
onCustomMarkerClickCallback自定义标记被点击通知外部处理自定义标记点击

scheduleDraw 重绘

定位

scheduleDraw 是图表重绘的唯一入口,负责以 RAF 合并多次重绘请求,避免同一帧内重复绘制。


实现

typescript
scheduleDraw() {
    if (this.raf != null) cancelAnimationFrame(this.raf)  // 取消未执行的旧请求
    this.raf = requestAnimationFrame(() => {
        this.raf = null
        this.draw()  // 在下一帧执行真正的绘制
    })
}

核心机制

RAF 合并:短时间内多次调用 scheduleDraw,只有最后一次生效。

例如用户在拖拽时连续触发 mousemove(每帧多次),每次都会取消上一帧的请求,最终只在下一帧执行一次 draw(),避免无效绘制。


调用来源

来源场景
交互事件缩放、平移、十字线移动、滚动、拖拽
数据变更updateDataupdateOptionsupdatePaneLayout
外部操作标记更新、副图增删、价格轴平移
尺寸变化resize

设计要点

  • 不直接调用 draw():所有触发方都通过 scheduleDraw 间接请求,由 RAF 统一调度
  • 幂等安全:重复调用不会堆积多个 draw,已排队的请求会被新请求覆盖
  • 异步执行:在当前帧的状态变更全部完成后,下一帧才开始绘制,保证状态一致性