Skip to content

Comments

feat: add pointer up down and move event#1243

Merged
yiiqii merged 11 commits intofeat/2.8from
feat/pointer-event
Nov 13, 2025
Merged

feat: add pointer up down and move event#1243
yiiqii merged 11 commits intofeat/2.8from
feat/pointer-event

Conversation

@wumaolinmaoan
Copy link
Contributor

@wumaolinmaoan wumaolinmaoan commented Oct 22, 2025

Summary by CodeRabbit

  • New Features

    • Pointer events (pointerdown/pointerup/pointermove) added with position, delta and raycast info; Player now emits pointer events.
  • Improvements

    • Click handling consolidated for consistent delivery; hit regions now include a direct item reference.
    • Player exposes skipPointerMovePicking to control pointer picking behavior.
    • Demos updated to log richer pointer interactions.
  • Deprecations

    • player, compositionId and compositionName fields marked deprecated in click/update payloads.

@wumaolinmaoan wumaolinmaoan requested a review from yiiqii October 22, 2025 11:25
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 22, 2025

Walkthrough

Adds pointer events and data models (pointerdown/pointerup/pointermove, PointerEventData, RaycastResult); renames Region field compContentitem; removes per-item click emission inside hitTest; centralizes pointer handling in EventSystem and re-emits through Player → Composition → Item.

Changes

Cohort / File(s) Summary
Hit-test / item behavior
packages/effects-core/src/comp-vfx-item.ts
hitTest no longer emits per-item click; Region objects now include item: VFXItem (replaces compContent).
Event types
packages/effects-core/src/events/types.ts
Imported PointerEventData; added PointerEvent type (pointerdown/pointerup/pointermove); ItemEvent and CompositionEvent now intersect with PointerEvent; click/end payloads updated with deprecated composition fields.
Click & pointer models
packages/effects-core/src/plugins/interact/click-handler.ts
Replaced compContent with item in Region; added exported PointerEventData and RaycastResult shapes; adjusted Vector2 import.
Event system core
packages/effects-core/src/plugins/interact/event-system.ts
Added PointerEventType enum and skipPointerMovePicking; added onPointerDown/Up/Move and centralized handlePointerEvent; new touch constants; exposed target field; native handler cleanup updated.
Engine surface
packages/effects-core/src/engine.ts
Added engine event signatures for click, pointerdown, pointerup, pointermove; removed legacy onClick callback from Engine API.
Player wiring
packages/effects/src/player.ts
Exposes skipPointerMovePicking getter/setter; subscribes to engine pointer events and re-emits pointerdown/pointerup/pointermove; performs hitTest and dispatches pointer events to Composition and Item.
Player types surface
packages/effects/src/types.ts
Imports PointerEvent; PlayerEvent now intersects PointerEvent; click and update payloads annotated with deprecations and adjusted shapes.
Demos — editor gizmo
plugin-packages/editor-gizmo/demo/src/gizmo.ts, plugin-packages/editor-gizmo/demo/src/gizmo-3d.ts, plugin-packages/editor-gizmo/demo/src/gizmo-wireframe.ts, plugin-packages/editor-gizmo/demo/src/scene.ts
Click handlers stop destructuring player from event payloads; use outer-scoped player instead (no behavioral change).
Interactive demo
web-packages/demo/src/interactive.ts
Added pointerdown/pointerup/pointermove listeners on player/composition/item and sets skipPointerMovePicking.
Tests
web-packages/test/unit/src/effects/player-event.spec.ts
Updated update event test to remove player from payload and removed related assertion.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Canvas
    participant EventSystem
    participant Player
    participant Composition
    participant Item

    rect rgb(200,230,255)
    note over User,Canvas: Pointer Down
    User->>Canvas: native pointerdown
    Canvas->>EventSystem: native event
    EventSystem->>EventSystem: build PointerEventData (position, delta, raycast)
    EventSystem->>Player: emit 'pointerdown'(PointerEventData)
    Player->>Player: hitTest -> Region (includes item)
    Player->>Composition: emit 'pointerdown'(PointerEventData, region)
    Composition->>Item: emit 'pointerdown'(PointerEventData, region.item)
    end

    rect rgb(220,255,220)
    note over User,Canvas: Pointer Move
    User->>Canvas: native pointermove
    Canvas->>EventSystem: native event
    EventSystem->>Player: emit 'pointermove'(PointerEventData)
    alt skipPointerMovePicking == false
        Player->>Player: hitTest -> Region
        Player->>Composition: emit 'pointermove'(PointerEventData, region)
        Composition->>Item: emit 'pointermove'(PointerEventData, region.item)
    else skipPointerMovePicking == true
        Player->>Composition: emit 'pointermove'(PointerEventData)  %% skip hitTest
    end
    end

    rect rgb(255,240,200)
    note over User,Canvas: Pointer Up / Click
    User->>Canvas: native pointerup
    Canvas->>EventSystem: native event
    EventSystem->>Player: emit 'pointerup'(PointerEventData)
    Player->>Player: hitTest -> Region
    Player->>Composition: emit 'pointerup'(PointerEventData, region)
    Composition->>Item: emit 'pointerup'(PointerEventData, region.item)
    alt region.type == NOTIFY
        Player->>Composition: emit 'click'(region with deprecated composition fields)
        Player->>Item: emit 'click'(region.item info)
    end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas needing extra attention:
    • hitTest change in packages/effects-core/src/comp-vfx-item.ts (verify Region.item correctness and all consumers).
    • New PointerEventData / RaycastResult shapes in click-handler.ts and their propagation.
    • Type intersections adding PointerEvent to ItemEvent/CompositionEvent and deprecation annotations in packages/effects-core/src/events/types.ts and packages/effects/src/types.ts.
    • Event dispatch and lifecycle changes in packages/effects-core/src/plugins/interact/event-system.ts and packages/effects/src/player.ts.
    • Tests and demos updated to match new payload shapes.

Poem

🐇 I hopped through rays and pointer light,
Regions now hold items tight,
Clicks no longer shouted by each sprite,
Pointer data guides each gentle bite,
A rabbit cheers the calmer night!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: add pointer up down and move event' directly reflects the main changes introduced across the codebase: adding support for pointerdown, pointerup, and pointermove events throughout the effects-core package and player API.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/pointer-event

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
packages/effects-core/src/comp-vfx-item.ts (1)

169-179: Remove redundant compContent field.

The region object now contains both compContent: this.item (line 171) and item (line 178), which is redundant. Since the Region type in click-handler.ts was updated to use the item field, the compContent field should be removed.

Apply this diff to remove the redundant field:

         if (success) {
           const region = {
-            compContent: this.item,
             id: item.id,
             name: item.name,
             position: hitPositions[hitPositions.length - 1],
             parentId: item.parentId,
             hitPositions,
             behavior: hitParams.behavior,
             item,
           };
packages/effects-core/src/composition.ts (1)

664-667: Fix 'end' payload to match new type {}.

CompositionEvent['end'] is now an empty object. Emitting { composition: this } breaks the type contract and downstream handlers.

Apply:

-      this.emit('end', { composition: this });
+      this.emit('end', {});
packages/effects-core/src/plugins/interact/event-system.ts (2)

60-83: Unreachable disposed-target guard; velocity calc likely wrong.

  • if (!this.target) can’t trigger under the non‑null contract; remove it.
  • vx/vy compute acceleration: (dx - lastTouch.dx)/dt. For pointer delta, use per‑event displacement/time: dx/dt, dy/dt. Also ensure lastTouch updates every move.

Apply:

-      if (!this.target) {
-        logger.warn('Trigger TouchEvent after EventSystem is disposed.');
-
-        return {
-          x, y, vx: 0, vy, dx, dy, ts, width: 0, height: 0, origin: event,
-        };
-      }
-      const { width, height } = this.target;
+      const { width, height } = this.target;
@@
-      if (lastTouch) {
-        const dt = ts - lastTouch.ts;
-
-        vx = ((dx - lastTouch.dx) / dt) || 0;
-        vy = ((dy - lastTouch.dy) / dt) || 0;
-        lastTouch = { dx, dy, ts };
-      }
+      if (lastTouch) {
+        const dt = ts - lastTouch.ts;
+        vx = (dx / dt) || 0;
+        vy = (dy / dt) || 0;
+      }
+      lastTouch = { dx, dy, ts };

115-121: Update currentTouch on move to report per‑event deltas.

dx = x - currentTouch.x uses the position from touchstart because currentTouch isn’t updated. Update it each move to avoid growing deltas.

Apply:

       [touchmove]: event => {
         if (currentTouch && this.enabled) {
-          const cood = getCoord(getTouch(event));
-
-          x = cood.x;
-          y = cood.y;
-          this.dispatchEvent(EVENT_TYPE_TOUCH_MOVE, getTouchEventValue(event, x, y, x - currentTouch.x, y - currentTouch.y));
+          const touch = getTouch(event);
+          const cood = getCoord(touch);
+          const nx = cood.x;
+          const ny = cood.y;
+          const dx = nx - currentTouch.x;
+          const dy = ny - currentTouch.y;
+          x = nx; y = ny;
+          this.dispatchEvent(EVENT_TYPE_TOUCH_MOVE, getTouchEventValue(event, x, y, dx, dy));
+          currentTouch = { ...currentTouch, x, y, clientX: touch.clientX, clientY: touch.clientY, ts: performance.now() };
         }
       },
🧹 Nitpick comments (4)
packages/effects-core/src/plugins/interact/event-system.ts (2)

41-43: Non‑null target contract — clean up downstream null checks.

With private target: HTMLCanvasElement, branches guarding !this.target and ?. optional chaining become dead code. Remove them or make target nullable again and set to null on dispose—pick one and be consistent.

Apply:

-  constructor (
-    private target: HTMLCanvasElement,
-    public allowPropagation = false,
-  ) { }
+  constructor (private target: HTMLCanvasElement, public allowPropagation = false) { }

Then (see below diffs) remove the dead guard in getTouchEventValue and ?. in dispose.


177-186: Dispose: remove optional chaining and redundant guard.

Align with non‑null target.

Apply:

-  dispose (): void {
-    if (this.target) {
-      this.handlers = {};
-
-      Object.keys(this.nativeHandlers).forEach(name => {
-        this.target?.removeEventListener(String(name), this.nativeHandlers[name]);
-      });
-      this.nativeHandlers = {};
-    }
-  }
+  dispose (): void {
+    this.handlers = {};
+    Object.keys(this.nativeHandlers).forEach(name => {
+      this.target.removeEventListener(String(name), this.nativeHandlers[name]);
+    });
+    this.nativeHandlers = {};
+  }
packages/effects-core/src/events/types.ts (1)

12-24: Item pointer events: types OK; fix pointerup doc.

Payloads align with usage (down/up no args, move carries PointerEventData). The comment for pointerup duplicates “按下”; should read “抬起”.

Apply:

   /**
-   * 元素按下事件
+   * 元素抬起事件
    */
   ['pointerup']: [],
packages/effects/src/player.ts (1)

58-61: Expose skipPointerMovePicking via config (optional).

Consider adding a PlayerConfig flag to control this at construction time.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 706a91f and 97c72ca.

📒 Files selected for processing (13)
  • packages/effects-core/src/comp-vfx-item.ts (1 hunks)
  • packages/effects-core/src/composition.ts (1 hunks)
  • packages/effects-core/src/events/types.ts (4 hunks)
  • packages/effects-core/src/plugins/interact/click-handler.ts (3 hunks)
  • packages/effects-core/src/plugins/interact/event-system.ts (3 hunks)
  • packages/effects/src/player.ts (5 hunks)
  • packages/effects/src/types.ts (3 hunks)
  • plugin-packages/editor-gizmo/demo/src/gizmo-3d.ts (1 hunks)
  • plugin-packages/editor-gizmo/demo/src/gizmo-wireframe.ts (1 hunks)
  • plugin-packages/editor-gizmo/demo/src/gizmo.ts (1 hunks)
  • plugin-packages/editor-gizmo/demo/src/scene.ts (1 hunks)
  • web-packages/demo/src/interactive.ts (2 hunks)
  • web-packages/test/unit/src/effects/player-event.spec.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
packages/effects-core/src/composition.ts (3)
packages/effects-core/src/events/event-emitter.ts (1)
  • EventEmitter (19-91)
packages/effects-core/src/events/types.ts (1)
  • CompositionEvent (34-72)
packages/effects-core/src/utils/index.ts (2)
  • Disposable (36-38)
  • LostHandler (44-46)
packages/effects-core/src/plugins/interact/event-system.ts (1)
packages/effects-core/src/utils/index.ts (1)
  • Disposable (36-38)
web-packages/demo/src/interactive.ts (1)
packages/effects-core/src/vfx-item.ts (2)
  • composition (184-186)
  • composition (191-199)
packages/effects-core/src/plugins/interact/click-handler.ts (1)
plugin-packages/model/src/runtime/math.ts (2)
  • Vector2 (10-10)
  • Vector3 (11-11)
packages/effects-core/src/events/types.ts (1)
packages/effects-core/src/plugins/interact/click-handler.ts (2)
  • PointerEventData (79-83)
  • Region (58-66)
packages/effects/src/player.ts (6)
packages/effects-core/src/events/event-emitter.ts (1)
  • EventEmitter (19-91)
packages/effects/src/types.ts (1)
  • PlayerEvent (160-217)
packages/effects-core/src/plugins/interact/event-system.ts (5)
  • EVENT_TYPE_CLICK (4-4)
  • EVENT_TYPE_TOUCH_START (5-5)
  • EVENT_TYPE_TOUCH_END (7-7)
  • EVENT_TYPE_TOUCH_MOVE (6-6)
  • TouchEventType (9-20)
packages/effects-core/src/plugins/interact/click-handler.ts (2)
  • Region (58-66)
  • PointerEventData (79-83)
packages/effects-core/src/vfx-item.ts (2)
  • composition (184-186)
  • composition (191-199)
packages/effects-core/src/composition.ts (1)
  • Composition (132-1028)
packages/effects/src/types.ts (1)
packages/effects-core/src/plugins/interact/click-handler.ts (2)
  • Region (58-66)
  • PointerEventData (79-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (22)
plugin-packages/editor-gizmo/demo/src/gizmo-3d.ts (1)

16-23: LGTM! Simplified event destructuring.

The change correctly removes player from the event payload destructuring and relies on the outer-scoped player variable. This aligns with the PR's goal of simplifying event payloads.

plugin-packages/editor-gizmo/demo/src/gizmo.ts (1)

21-28: LGTM! Consistent with other demo files.

The event payload destructuring is correctly updated to use only id, relying on the outer-scoped player variable for composition and item access.

plugin-packages/editor-gizmo/demo/src/gizmo-wireframe.ts (1)

16-23: LGTM! Event destructuring properly updated.

The change correctly simplifies the event payload by extracting only id, consistent with the updated event payload structure across the codebase.

plugin-packages/editor-gizmo/demo/src/scene.ts (1)

12-19: LGTM! Consistent event payload handling.

The event destructuring is correctly updated in line with the other demo files, removing the redundant player extraction.

packages/effects-core/src/plugins/interact/click-handler.ts (3)

1-2: LGTM! More specific import.

The direct import of Vector2 from the specific module is appropriate and may help with tree-shaking.


58-66: LGTM! Clearer field naming.

The rename from compContent to item in the Region type improves clarity and better represents the field's purpose.


79-88: LGTM! Well-structured pointer event types.

The new PointerEventData and RaycastResult classes provide a clean structure for pointer event handling, with clear property types and appropriate defaults.

web-packages/test/unit/src/effects/player-event.spec.ts (1)

25-33: LGTM! Test correctly updated for simplified payload.

The test handler signature is properly updated to reflect the removal of player from the update event payload, checking only the playing boolean property as expected.

web-packages/demo/src/interactive.ts (3)

22-30: LGTM! Player-level pointer events added.

The new pointer event handlers (pointerdown, pointerup, pointermove) properly demonstrate the new pointer event functionality, accessing the appropriate payload properties (pointerCurrentRaycast.item, position, delta).


57-65: LGTM! Composition-level pointer events added.

The updated click handler and new pointer event handlers properly demonstrate composition-level event handling with the new event payload structure.


69-74: LGTM! Item-level pointer events added.

The new pointer event handlers for the item level properly demonstrate pointer interaction tracking at the item scope.

packages/effects-core/src/composition.ts (1)

132-132: Type alignment with new event map looks good.

Extending EventEmitter with the non‑generic CompositionEvent matches the updated event typings.

packages/effects-core/src/plugins/interact/event-system.ts (2)

28-32: New PointerEventType enum — OK.

Enum values and names are clear and consistent with usage.


146-147: Binding without optional chaining — good.

Safe under the non‑null target contract.

packages/effects-core/src/events/types.ts (2)

34-51: Composition pointer events: types OK.

Non‑generic CompositionEvent with pointer payloads looks consistent.


66-66: 'end' payload changed to empty object — ensure emitters updated.

See Composition.update; emit {} instead of { composition: this }.

packages/effects/src/types.ts (3)

1-1: Expanded imports — OK.

PointerEventData import matches new event payloads.


160-179: PlayerEvent adds pointer events — OK.

Shapes align with core. Click keeps composition metadata, which is useful at Player level.


201-201: Update event payload simplified — OK.

Matches emitter usage in Player.

packages/effects/src/player.ts (3)

30-30: Type change to non‑generic PlayerEvent — OK.

Matches updated public types.


158-164: New event bindings — OK.

Wires click/touch to new pointer handlers as expected.


861-893: Click flow — OK; confirm multi‑hit semantics.

Emits once per hit region across compositions. Ensure consumers expect multiple click events for a single pointer action.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/effects/src/player.ts (1)

925-963: Fix pointermove payload to item and correct delta to use dx/dy.

  • ItemEvent['pointermove'] requires PointerEventData; currently hitItem.emit(eventName) omits it.
  • eventData.delta should use per-event displacement e.dx/e.dy, not velocity e.vx/e.vy.

Apply:

-    eventData.position.x = (x + 1) / 2 * width;
-    eventData.position.y = (y + 1) / 2 * height;
-    eventData.delta.x = e.vx * width;
-    eventData.delta.y = e.vy * height;
+    eventData.position.x = (x + 1) / 2 * width;
+    // TODO: verify origin; if 0 at top-left is desired, map Y via (1 - y) / 2 * height.
+    eventData.position.y = (y + 1) / 2 * height;
+    eventData.delta.x = e.dx * width;
+    eventData.delta.y = e.dy * height;
@@
-      hitComposition.emit(eventName, eventData);
-      hitItem.emit(eventName);
+      hitComposition.emit(eventName, eventData);
+      if (eventName === 'pointermove') {
+        hitItem.emit(eventName, eventData);
+      } else {
+        hitItem.emit(eventName);
+      }

Optional improvements:

  • Use the actual hit point if available.
-    if (hitRegion) {
-      raycast.point = hitRegion.position;
-      raycast.item = hitRegion.item;
-    }
+    if (hitRegion) {
+      raycast.point = hitRegion.hitPositions?.[hitRegion.hitPositions.length - 1] ?? hitRegion.position;
+      raycast.item = hitRegion.item;
+    }
  • Consider resolving top-most by render order when scanning compositions (e.g., iterate this.getCompositions() or sort by getIndex()).
🧹 Nitpick comments (2)
packages/effects-core/src/events/types.ts (1)

12-23: Confirm payload design: ItemEvent pointerdown/up vs pointermove.

ItemEvent has [] for pointerdown/up but PointerEventData for pointermove. Is this asymmetry intentional? If consumers need coordinates on press/release too, consider carrying PointerEventData for all three for consistency. If not, please document the rationale.

packages/effects/src/player.ts (1)

863-897: Click emits for every hit region; confirm intent vs top-most only.

You collect all hit results across compositions and emit per hit. Pointer path, however, targets a single (last/top-most) region. Please confirm if click should:

  • emit once for the top-most region (consistent with pointer), or
  • emit for all hits (current behavior).

If single target is desired, select the last entry only:

-  for (const hitResult of hitResults) {
+  const hitResult = hitResults.at(-1);
+  if (hitResult) {
     const behavior = hitResult.behavior || spec.InteractBehavior.NOTIFY;
     const hitComposition = hitResult.item.composition;
     if (!hitComposition) { 
-      continue;
+      return;
     }
     if (behavior === spec.InteractBehavior.NOTIFY) {
       const clickInfo = { player: this, ...hitResult, compositionId: hitComposition.id, compositionName: hitComposition.name };
       this.emit('click', clickInfo);
       hitComposition.emit('click', clickInfo);
     } else if (behavior === spec.InteractBehavior.RESUME_PLAYER) {
       void this.resume();
     }
     hitResult.item.emit('click', hitResult);
-  }
+  }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97c72ca and ebf52f5.

📒 Files selected for processing (3)
  • packages/effects-core/src/events/types.ts (4 hunks)
  • packages/effects/src/player.ts (4 hunks)
  • packages/effects/src/types.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/effects/src/types.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/effects/src/player.ts (4)
packages/effects-core/src/plugins/interact/event-system.ts (1)
  • TouchEventType (9-20)
packages/effects-core/src/plugins/interact/click-handler.ts (2)
  • Region (58-66)
  • PointerEventData (79-83)
packages/effects-core/src/vfx-item.ts (2)
  • composition (184-186)
  • composition (191-199)
packages/effects-core/src/composition.ts (1)
  • Composition (132-1028)
packages/effects-core/src/events/types.ts (1)
packages/effects-core/src/plugins/interact/click-handler.ts (1)
  • PointerEventData (79-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
packages/effects/src/player.ts (1)

160-164: Event bindings look good.

Click and touch listeners are registered on EventSystem as expected.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/effects/src/player.ts (1)

929-930: Critical: Use dx/dy for per-event delta, not vx/vy.

The delta calculation uses e.vx and e.vy, which represent velocity values, not per-event pixel deltas. According to the TouchEventType definition, the correct fields are e.dx and e.dy.

Note: This issue was previously flagged and marked as "Addressed in commits d6c3037 to ebf52f5", but the code still uses vx/vy. Please verify whether the fix was applied or reverted.

Apply this diff to use the correct delta fields:

-    eventData.delta.x = e.vx * width;
-    eventData.delta.y = e.vy * height;
+    eventData.delta.x = e.dx * width;
+    eventData.delta.y = e.dy * height;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebf52f5 and 9528c5d.

📒 Files selected for processing (2)
  • packages/effects-core/src/events/types.ts (4 hunks)
  • packages/effects/src/player.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/effects-core/src/events/types.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/effects/src/player.ts (4)
packages/effects-core/src/plugins/interact/event-system.ts (5)
  • EVENT_TYPE_CLICK (4-4)
  • EVENT_TYPE_TOUCH_START (5-5)
  • EVENT_TYPE_TOUCH_END (7-7)
  • EVENT_TYPE_TOUCH_MOVE (6-6)
  • TouchEventType (9-20)
packages/effects-core/src/plugins/interact/click-handler.ts (2)
  • Region (58-66)
  • PointerEventData (79-83)
packages/effects-core/src/vfx-item.ts (2)
  • composition (184-186)
  • composition (191-199)
packages/effects-core/src/composition.ts (1)
  • Composition (132-1028)
🔇 Additional comments (4)
packages/effects/src/player.ts (4)

1-17: LGTM!

The new imports for pointer event types and constants are correctly structured and align with the enhanced pointer event handling functionality.


58-61: LGTM!

The skipPointerMovePicking optimization is well-implemented and defaults to true for performance. This means pointer move events will skip hit testing by default, which is reasonable for reducing overhead in high-frequency events.


160-163: LGTM!

Event listener bindings correctly map touch events to the new pointer event handlers.


863-897: LGTM!

The refactored onClick handler correctly aggregates hit test results across all compositions and propagates click events with proper payloads to player, composition, and item targets. The null check for composition is appropriate.

@wumaolinmaoan wumaolinmaoan marked this pull request as draft October 23, 2025 02:06
@yiiqii yiiqii changed the base branch from feat/2.7 to feat/2.8 November 4, 2025 11:33
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
packages/effects/src/player.ts (2)

702-703: Fix delta calculation: use dx/dy instead of vx/vy.

The delta should represent the per-event pixel movement, not velocity. According to the TouchEventType definition, dx and dy are the appropriate fields for this purpose, while vx and vy represent velocity.

-    eventData.delta.x = e.vx * width;
-    eventData.delta.y = e.vy * height;
+    eventData.delta.x = e.dx * width;
+    eventData.delta.y = e.dy * height;

729-736: Add defensive null check for composition.

While hitItem.composition should exist when a region is hit, the cast on line 731 lacks defensive validation. Consider adding a null check to prevent potential runtime errors.

     if (hitRegion) {
       const hitItem = hitRegion.item;
       const hitComposition = hitItem.composition as Composition;
 
+      if (!hitComposition) {
+        return;
+      }
+
       this.emit(eventName, eventData);
🧹 Nitpick comments (2)
packages/effects/src/player.ts (2)

225-228: Remove commented-out code.

The old click handler is commented out but should be removed entirely to keep the codebase clean.

-      // this.event.addEventListener(EVENT_TYPE_CLICK, this.onClick.bind(this));
       this.event.addEventListener(EVENT_TYPE_TOUCH_START, this.onPointerDown.bind(this));

688-696: Consider simplifying the hit-testing condition.

The double negative !(type === PointerEventType.PointerMove && this.skipPointerMovePicking) is harder to read than necessary.

-    if (!(type === PointerEventType.PointerMove && this.skipPointerMovePicking)) {
+    const shouldSkipPicking = type === PointerEventType.PointerMove && this.skipPointerMovePicking;
+    
+    if (!shouldSkipPicking) {
       for (const composition of this.compositions) {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9528c5d and 1dc52a1.

📒 Files selected for processing (6)
  • packages/effects-core/src/comp-vfx-item.ts (1 hunks)
  • packages/effects-core/src/events/types.ts (3 hunks)
  • packages/effects-core/src/plugins/interact/click-handler.ts (3 hunks)
  • packages/effects-core/src/plugins/interact/event-system.ts (2 hunks)
  • packages/effects/src/player.ts (4 hunks)
  • packages/effects/src/types.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/effects/src/types.ts
  • packages/effects-core/src/comp-vfx-item.ts
  • packages/effects-core/src/plugins/interact/event-system.ts
  • packages/effects-core/src/plugins/interact/click-handler.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-05T07:51:18.728Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:162-164
Timestamp: 2025-08-05T07:51:18.728Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the //ts-expect-error usage for accessing siblingComponent.splits (around lines 162-164) is temporary debugging code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects/src/player.ts
🧬 Code graph analysis (2)
packages/effects/src/player.ts (3)
packages/effects-core/src/plugins/interact/event-system.ts (4)
  • EVENT_TYPE_TOUCH_START (9-9)
  • EVENT_TYPE_TOUCH_END (11-11)
  • EVENT_TYPE_TOUCH_MOVE (10-10)
  • TouchEventType (13-24)
packages/effects-core/src/plugins/interact/click-handler.ts (2)
  • Region (59-68)
  • PointerEventData (81-85)
packages/effects-core/src/composition.ts (1)
  • Composition (132-1032)
packages/effects-core/src/events/types.ts (1)
packages/effects-core/src/plugins/interact/click-handler.ts (2)
  • PointerEventData (81-85)
  • Region (59-68)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
packages/effects-core/src/events/types.ts (1)

1-77: LGTM! Well-structured pointer event integration.

The new PointerEvent type is properly integrated into both ItemEvent and CompositionEvent, maintaining backward compatibility while adding new pointer interaction capabilities. The deprecation annotations follow proper conventions and provide clear migration signals.

packages/effects/src/player.ts (3)

1-49: LGTM! Pointer event infrastructure properly set up.

The imports and skipPointerMovePicking field are correctly added to support the new pointer event system. Defaulting to true for skipping pointermove hit-testing is a sensible performance optimization.


672-682: LGTM! Handler methods properly delegate to unified logic.

The three handler methods follow a clean delegation pattern, keeping the event-specific logic centralized in handlePointerEvent.


733-733: Verify: Should Player emit pointer events globally?

Currently, the Player only emits pointer events when hitRegion exists (line 733 is inside the if (hitRegion) block). Depending on requirements, consumers might expect the Player to emit these events regardless of whether any interactive region was hit, allowing global pointer tracking.

Please confirm whether this is the intended behavior or if Player should emit pointer events unconditionally.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/effects-core/src/plugins/interact/event-system.ts (2)

248-249: Use per-event delta (dx/dy) instead of velocity (vx/vy).

e.vx and e.vy represent velocity (pixels per millisecond), not per-event delta. For PointerEventData.delta, you should use e.dx and e.dy which represent the pixel delta since the last event.

Apply this diff:

     eventData.position.x = (x + 1) / 2 * width;
     eventData.position.y = (y + 1) / 2 * height;
-    eventData.delta.x = e.vx * width;
-    eventData.delta.y = e.vy * height;
+    eventData.delta.x = e.dx * width;
+    eventData.delta.y = e.dy * height;

275-282: Add defensive null check for composition.

While hitRegion.item.composition should typically exist when a region is hit, adding a null check prevents potential runtime errors if the composition reference is unexpectedly missing.

Apply this diff:

     if (hitRegion) {
       const hitItem = hitRegion.item;
       const hitComposition = hitItem.composition as Composition;
 
+      if (!hitComposition) {
+        return;
+      }
+
       hitItem.emit(eventName, eventData);
       hitComposition.emit(eventName, eventData);
       this.engine.emit(eventName, eventData);
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1dc52a1 and 2ff1f45.

📒 Files selected for processing (4)
  • packages/effects-core/src/engine.ts (2 hunks)
  • packages/effects-core/src/plugins/interact/event-system.ts (5 hunks)
  • packages/effects/src/player.ts (3 hunks)
  • web-packages/demo/src/interactive.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-10-18T09:15:08.038Z
Learnt from: wumaolinmaoan
Repo: galacean/effects-runtime PR: 691
File: packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts:39-42
Timestamp: 2024-10-18T09:15:08.038Z
Learning: In `packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts`, when accumulating color components, avoid using `clone()` methods because it can cause additional garbage collection overhead.

Applied to files:

  • packages/effects/src/player.ts
📚 Learning: 2025-08-05T07:51:18.728Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:162-164
Timestamp: 2025-08-05T07:51:18.728Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the //ts-expect-error usage for accessing siblingComponent.splits (around lines 162-164) is temporary debugging code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects/src/player.ts
🧬 Code graph analysis (3)
packages/effects-core/src/engine.ts (1)
packages/effects-core/src/plugins/interact/click-handler.ts (2)
  • Region (59-68)
  • PointerEventData (81-85)
packages/effects-core/src/plugins/interact/event-system.ts (3)
packages/effects-core/src/utils/index.ts (1)
  • Disposable (36-38)
packages/effects-core/src/plugins/interact/click-handler.ts (2)
  • Region (59-68)
  • PointerEventData (81-85)
packages/effects-core/src/composition.ts (1)
  • Composition (132-1032)
packages/effects/src/player.ts (1)
packages/effects-core/src/plugins/interact/click-handler.ts (2)
  • Region (59-68)
  • PointerEventData (81-85)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
packages/effects/src/player.ts (1)

45-54: LGTM: Clean public API for pointer move optimization.

The skipPointerMovePicking property is well-documented and correctly delegates to the underlying engine event system. The JSDoc clearly explains the performance trade-off.

packages/effects-core/src/engine.ts (1)

40-43: LGTM: Pointer event signatures are well-typed.

The new event signatures correctly use Region for click events and PointerEventData for pointer lifecycle events, providing proper type safety for consumers.

packages/effects-core/src/plugins/interact/event-system.ts (1)

275-282: Verify intended behavior: pointer events only emitted when over a hit target.

The current implementation only emits pointer events (pointerdown/pointerup/pointermove) to the engine when hitRegion exists (lines 275-282). This means:

  • Player/composition listeners won't receive pointer events when the pointer is not over an interactive item
  • For pointermove, users tracking cursor position may miss events when moving over non-interactive areas

Is this the intended behavior? Many pointer event systems emit events regardless of hit targets, with the hit information being null/empty when nothing is hit.

If you want pointer events to always emit (with or without a hit target), consider emitting to engine outside the if (hitRegion) block:

if (hitRegion) {
  const hitItem = hitRegion.item;
  const hitComposition = hitItem.composition as Composition;

  if (hitComposition) {
    hitItem.emit(eventName, eventData);
    hitComposition.emit(eventName, eventData);
  }
}
// Always emit to engine, regardless of hit
this.engine.emit(eventName, eventData);

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/effects-core/src/plugins/interact/event-system.ts (2)

192-192: Translate comment to English.

For codebase consistency, comments should be in English.

-    // 收集所有的点击测试结果,click 回调执行可能会对 composition 点击结果有影响,放在点击测试执行完后再统一触发。
+    // Collect all hit test results first. Click callbacks may affect composition hit test results, so emit events after all tests complete.

256-271: Consider simplifying event name mapping.

The switch statement could be replaced with a more concise lookup approach.

-    let eventName: 'pointerdown' | 'pointerup' | 'pointermove' = 'pointerdown';
-
-    switch (type) {
-      case PointerEventType.PointerDown:
-        eventName = 'pointerdown';
-
-        break;
-      case PointerEventType.PointerUp:
-        eventName = 'pointerup';
-
-        break;
-      case PointerEventType.PointerMove:
-        eventName = 'pointermove';
-
-        break;
-    }
+    const eventNameMap = {
+      [PointerEventType.PointerDown]: 'pointerdown',
+      [PointerEventType.PointerUp]: 'pointerup',
+      [PointerEventType.PointerMove]: 'pointermove',
+    } as const;
+    const eventName = eventNameMap[type];
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ff1f45 and 098fba5.

📒 Files selected for processing (1)
  • packages/effects-core/src/plugins/interact/event-system.ts (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/effects-core/src/plugins/interact/event-system.ts (4)
packages/effects-core/src/utils/index.ts (1)
  • Disposable (36-38)
packages/effects-core/src/plugins/interact/click-handler.ts (2)
  • Region (59-68)
  • PointerEventData (81-85)
packages/effects-core/src/vfx-item.ts (2)
  • composition (184-186)
  • composition (191-199)
packages/effects-core/src/composition.ts (1)
  • Composition (132-1032)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
packages/effects-core/src/plugins/interact/event-system.ts (2)

39-39: Verify pointer move event emission behavior.

With skipPointerMovePicking = true (the default), pointer move events skip hit testing (line 232), resulting in hitRegion being null. Since events are only emitted when hitRegion is not null (lines 273-280), pointer move events are never emitted by default.

Confirm whether this is intentional or if pointer move events should be emitted to the engine even when they don't hit any items.


155-158: The bound method references are properly stored and managed.

The .bind(this) methods added at lines 155-158 are stored in this.handlers by the addEventListener method and can be properly removed during dispose(). Additionally, bindListeners is never called multiple times on the same EventSystem instance in the codebase—in engine.ts it's called once during initialization, and in three-display-object.ts a new EventSystem is instantiated each time rather than reusing the same instance. The listener cleanup concern does not apply.

Likely an incorrect or invalid review comment.

@wumaolinmaoan wumaolinmaoan marked this pull request as ready for review November 11, 2025 10:21
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/effects-core/src/events/types.ts (1)

37-50: Add explicit migration guidance in JSDoc comments for deprecated fields.

The deprecated compositionName and compositionId fields are not used in the codebase. The implicit migration path is to use Region.name and Region.id instead, which are already available in the clickInfo object. While the deprecation is safe, consider adding explicit JSDoc @deprecated comments with migration instructions to help users update their code.

Example:

/**
 * @deprecated 2.8.0 - Use clickInfo.name instead
 */
compositionName: string,
/**
 * @deprecated 2.8.0 - Use clickInfo.id instead
 */
compositionId: string,
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69f1158 and 4ffc453.

📒 Files selected for processing (2)
  • packages/effects-core/src/events/types.ts (3 hunks)
  • packages/effects/src/types.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-10-18T09:15:08.038Z
Learnt from: wumaolinmaoan
Repo: galacean/effects-runtime PR: 691
File: packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts:39-42
Timestamp: 2024-10-18T09:15:08.038Z
Learning: In `packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts`, when accumulating color components, avoid using `clone()` methods because it can cause additional garbage collection overhead.

Applied to files:

  • packages/effects/src/types.ts
🧬 Code graph analysis (2)
packages/effects/src/types.ts (2)
packages/effects-core/src/events/types.ts (1)
  • PointerEvent (4-17)
packages/effects-core/src/plugins/interact/click-handler.ts (1)
  • Region (59-68)
packages/effects-core/src/events/types.ts (1)
packages/effects-core/src/plugins/interact/click-handler.ts (2)
  • PointerEventData (81-85)
  • Region (59-68)
🔇 Additional comments (6)
packages/effects-core/src/events/types.ts (3)

2-2: LGTM!

The import correctly adds PointerEventData, which is used by the new PointerEvent type definition.


4-17: LGTM!

The PointerEvent type is well-structured with appropriate payload types and clear JSDoc documentation for all three pointer events.


22-22: LGTM!

Extending ItemEvent with PointerEvent via intersection type correctly adds pointer events while preserving existing click and message events.

packages/effects/src/types.ts (3)

1-1: LGTM!

The import correctly adds PointerEvent from @galacean/effects-core, which is used to extend the PlayerEvent type.


161-178: Deprecation structure verified—code change is sound.

The PlayerEvent type correctly extends PointerEvent and the deprecated fields (player, compositionId, compositionName) are properly marked with @deprecated 2.8.0. Verification confirms:

  • Migration path is clear: The implementation (packages/effects/src/player.ts:212-213) demonstrates the replacement pattern—use e.composition.id instead of e.compositionId and e.composition.name instead of e.compositionName.
  • Region type provides all necessary data: The Region object includes the composition property, ensuring users have direct access to replacement values.
  • Deprecation is consistent: Matches the pattern applied to CompositionEvent and ItemEvent.
  • Backwards compatible: Deprecated fields are still populated, allowing gradual migration.

200-206: The review comment is incorrect.

The update event has always used an object payload, not a tuple. Evidence from the codebase shows:

  1. Actual emit pattern (player.ts lines 235–236, 538–539): this.emit('update', { player: this, playing: ... })
  2. Listener patterns throughout the codebase already expect objects:
    • player.on('update', (info: { playing: boolean }) => { ... }) (player-event.spec.ts:25)
    • player.on('update', e => { ... e.playing }) (interactive.ts:36)

The old tuple pattern you described ((player, playing) => { ... }) does not exist in the codebase. The @deprecated 2.8.0 marker on the player field is a managed deprecation, not an unannounced breaking change. Existing listeners are already compatible with the current structure.

Likely an incorrect or invalid review comment.

@yiiqii yiiqii merged commit 0117866 into feat/2.8 Nov 13, 2025
2 checks passed
@yiiqii yiiqii deleted the feat/pointer-event branch November 13, 2025 02:17
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.

2 participants