Skip to content

画布 Transform 手柄:拖拽移动/缩放选中片段(1:1 上游)#176

Merged
appergb merged 1 commit into
mainfrom
feat/canvas-transform-handles
Jul 2, 2026
Merged

画布 Transform 手柄:拖拽移动/缩放选中片段(1:1 上游)#176
appergb merged 1 commit into
mainfrom
feat/canvas-transform-handles

Conversation

@appergb

@appergb appergb commented Jul 1, 2026

Copy link
Copy Markdown
Owner

概要

画布上直接操作 Transform:选中单个可视片段时,在合成预览画布上显示带 4 个角手柄 + 移动面的包围盒,拖拽即改片段 transform。1:1 复刻上游 TransformOverlayView.swift实际行为。

关键 1:1 发现

上游只有 4 个角手柄,没有边手柄,也没有旋转手柄(旋转仅经 Inspector 数值字段设;全上游仓库 grep atan2/rotationHandle 零匹配)。故均未发明。角拖拽的屏幕 delta 经旋转祖先 = 已在局部系,move 命中区是独立未旋转兄弟 = 屏幕系——用 rotateDeltaIntoLocalFrame(R(-θ))精确复刻这个非对称。

变更

  • clip.tsmoveTransformByDelta(归一平移 + 未旋转时画布边/中心吸附,保留上游"单一宽度阈值"怪癖)、rotateDeltaIntoLocalFramesampledTransform(= 上游 clip.transformAt(frame:),跟随关键帧位置/缩放/旋转)、findSelectedVisualClip。复用现有 resizeTransformFromCorner。20 单测。
  • TransformOverlay.tsx(新):包围盒 + 手柄;容器 pointerEvents:none,仅手柄/移动面可交互(绝不挡预览);local-optimistic 实时预览,松手才 setClipProperties(避撤销栈刷屏 / IPC 延迟,同 KeyframesLaneRow)。6 渲染测试。
  • Preview.tsx:surgical 挂载于既有画布 div 内,仅单选可视片段时渲染。4 负路径测试。

范围切分(代码内标注)

  • 上游粉色中心吸附引导线(功能吸附已移植,引导线渲染未做,可分离)。
  • 文本片段 fontScale 缩放(applyTextStyle 未接线,文本暂经 transform 缩放)。
  • 画布拖拽的真机视觉验证:headless / Dock 拦截无法做,需真机过一遍;tsc + 单测已绿。

测试

pnpm build(tsc)绿;pnpm test 239 通过(+30)。

…selected clip

Port upstream TransformOverlayView: when a single visual clip is selected, draw a
bounding box with 4 corner resize handles + a move surface over the composited
preview canvas, committing to the clip's transform. Faithful to what upstream
ACTUALLY has — corners only (no edge handles) and NO rotation handle (upstream
sets rotation only via the Inspector field; grep atan2/rotationHandle across the
upstream repo = zero hits), so neither is invented.

- clip.ts: moveTransformByDelta (normalized center translate + canvas-edge/center
  snap when unrotated, preserving upstream's single-width-threshold quirk),
  rotateDeltaIntoLocalFrame (R(-theta) so a screen-space corner drag maps into the
  rotated box's local frame; move deltas stay raw, rotation-invariant),
  sampledTransform (topLeft/size/rotation samplers -> center Transform = upstream
  clip.transformAt(frame:), so the box tracks keyframed transform), and
  findSelectedVisualClip. resizeTransformFromCorner reused unchanged. 20 tests.
- TransformOverlay.tsx: the box + handles; container pointerEvents:none with only
  the handles/move-surface interactive (never blocks the preview); local-optimistic
  drag preview, ONE setClipProperties on pointerup (avoids per-move undo-stack
  spam / IPC latency — KeyframesLaneRow's pattern). 6 render tests.
- Preview.tsx: surgical mount inside the canvas div, gated on a single selected
  visual clip. 4 negative-mount tests.

Scope cuts (documented in code): pink center-snap GUIDE LINES (functional snap
ported, guide rendering not); text-clip fontScale resize (no applyTextStyle wired
yet — text resizes via transform). On-canvas drag still needs a real-machine
visual pass (headless/Dock-blocked here); tsc + unit tests are green.

Verified: pnpm build (tsc) clean; pnpm test 239 passed (+30).
@appergb appergb merged commit ceb1074 into main Jul 2, 2026
2 checks passed
@appergb appergb deleted the feat/canvas-transform-handles branch July 2, 2026 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant