diff --git a/packages/2d/src/lib/scenes/Scene2D.ts b/packages/2d/src/lib/scenes/Scene2D.ts index 5921a0f1..5a50e82a 100644 --- a/packages/2d/src/lib/scenes/Scene2D.ts +++ b/packages/2d/src/lib/scenes/Scene2D.ts @@ -76,9 +76,12 @@ export class Scene2D extends GeneratorScene implements Inspectable { } public inspectPosition(x: number, y: number): InspectedElement | null { - return this.execute( - () => this.getView().hit(new Vector2(x, y))?.key ?? null, - ); + const node = this.getNodeByPosition(x, y); + return node?.key; + } + + public getNodeByPosition(x: number, y: number): Node | null { + return this.execute(() => this.getView().hit(new Vector2(x, y)) ?? null); } public validateInspection( diff --git a/packages/player-react/src/index.tsx b/packages/player-react/src/index.tsx index 5c676e07..43f0bff0 100644 --- a/packages/player-react/src/index.tsx +++ b/packages/player-react/src/index.tsx @@ -1,5 +1,6 @@ 'use client'; -import {ComponentProps, useEffect, useRef, useState} from 'react'; +import {Player as CorePlayer} from '@revideo/core'; +import {ComponentProps, useCallback, useEffect, useRef, useState} from 'react'; import {Controls} from './controls'; import './styles.css'; import {shouldShowControls} from './utils'; @@ -41,6 +42,8 @@ interface PlayerProps { onDurationChange?: (duration: number) => void; onTimeUpdate?: (currentTime: number) => void; + onPlayerReady?: (player: CorePlayer) => void; + onPlayerResize?: (rect: DOMRectReadOnly) => void; } export function Player({ @@ -60,6 +63,8 @@ export function Player({ onDurationChange = () => {}, onTimeUpdate = () => {}, + onPlayerReady = () => {}, + onPlayerResize = () => {}, }: PlayerProps) { const [playingState, setPlaying] = useState(playing); const [isMouseOver, setIsMouseOver] = useState(false); @@ -67,7 +72,11 @@ export function Player({ const [duration, setDuration] = useState(-1); const focus = useRef(false); - const playerRef = useRef(null); + const playerRef = useRef(null); + const wrapperRef = useRef(null); + const lastRect = useRef(null); + + const onClickHandler = controls ? () => setPlaying(prev => !prev) : undefined; /** * Sync the playing prop with the player's own state when it changes. @@ -114,6 +123,44 @@ export function Player({ } }; + const handlePlayerReady = (event: Event) => { + const player = (event as CustomEvent).detail; + if (player) { + onPlayerReady(player); + } + }; + + const handlePlayerResize = useCallback( + (entries: ResizeObserverEntry[]) => { + const [firstEntry] = entries; + if (!firstEntry || !wrapperRef.current) { + return; + } + + const newRect = firstEntry.contentRect; + const sameWidth = newRect.width === lastRect.current.width; + const sameHeight = newRect.height === lastRect.current.height; + if (lastRect.current && sameWidth && sameHeight) { + return; + } + + lastRect.current = newRect; + onPlayerResize(newRect); + }, + [onPlayerResize], + ); + + useEffect(() => { + if (!wrapperRef.current) return; + + const resizeObserver = new ResizeObserver(handlePlayerResize); + resizeObserver.observe(wrapperRef.current); + + return () => { + resizeObserver.disconnect(); + }; + }, [handlePlayerResize]); + /** * Import the player and add all event listeners. */ @@ -122,11 +169,13 @@ export function Player({ playerRef.current?.addEventListener('timeupdate', handleTimeUpdate); playerRef.current?.addEventListener('duration', handleDurationUpdate); + playerRef.current?.addEventListener('playerready', handlePlayerReady); document.addEventListener('keydown', handleKeyDown); return () => { playerRef.current?.removeEventListener('timeupdate', handleTimeUpdate); playerRef.current?.removeEventListener('duration', handleDurationUpdate); + playerRef.current?.removeEventListener('playerready', handlePlayerReady); document.removeEventListener('keydown', handleKeyDown); const frameElement = document.getElementById('revideo-2d-frame'); if (frameElement) { @@ -149,6 +198,7 @@ export function Player({ return (
(focus.current = true)} onBlur={() => (focus.current = false)} @@ -161,7 +211,7 @@ export function Player({ ref={playerRef} src={src} playing={String(playingState)} - onClick={() => setPlaying(prev => !prev)} + onClick={onClickHandler} variables={JSON.stringify(variables)} looping={looping ? 'true' : 'false'} width={width} diff --git a/packages/player-react/src/internal.ts b/packages/player-react/src/internal.ts index 63889f40..95c9d228 100644 --- a/packages/player-react/src/internal.ts +++ b/packages/player-react/src/internal.ts @@ -177,6 +177,7 @@ class RevideoPlayer extends HTMLElement { this.player.togglePlayback(this.playing); this.setState(State.Ready); + this.dispatchEvent(new CustomEvent('playerready', {detail: this.player})); } public attributeChangedCallback(name: string, _: any, newValue: any) {