= ({ props, currentFrame, config }) => {
+ const style = useApplyEffects(props, currentFrame, config);
+
+ const imageUri = props.imageUri || "";
+ const isCircular = props.type === 'CircularMotionImageView';
+
+ return (
+
+ );
+};
diff --git a/web/web-sdui/src/views/MultiLyricsContainer.tsx b/web/web-sdui/src/views/MultiLyricsContainer.tsx
index 829a99c4..04bd3129 100644
--- a/web/web-sdui/src/views/MultiLyricsContainer.tsx
+++ b/web/web-sdui/src/views/MultiLyricsContainer.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import type { MotionViewProps } from '../infra/types';
+import type { MotionViewProps, MotionConfig } from '../infra/types';
import { useApplyEffects } from '../effects/useApplyEffects';
const FakeAudioChartView: React.FC<{ frame: number }> = ({ frame }) => {
@@ -32,8 +32,8 @@ const FakeAudioChartView: React.FC<{ frame: number }> = ({ frame }) => {
);
};
-export const MultiLyricsContainer: React.FC<{ props: MotionViewProps; currentFrame: number }> = ({ props, currentFrame }) => {
- const style = useApplyEffects(props, currentFrame);
+export const MultiLyricsContainer: React.FC<{ props: MotionViewProps; currentFrame: number; config: MotionConfig }> = ({ props, currentFrame, config }) => {
+ const style = useApplyEffects(props, currentFrame, config);
return (
= ({ props, currentFrame }) => {
- const style = useApplyEffects(props, currentFrame);
+export const PopUpTextView: React.FC<{ props: MotionViewProps; currentFrame: number; config: MotionConfig }> = ({ props, currentFrame, config }) => {
+ const style = useApplyEffects(props, currentFrame, config);
const text = props.text || "";
const writingSpeed = props.writingSpeed || 1;
@@ -16,8 +16,6 @@ export const PopUpTextView: React.FC<{ props: MotionViewProps; currentFrame: num
const words = text.split(" ");
const wordCount = words.length;
- // In Android, inferredEndFrame is used.
- // inferredEndFrame = startFrame + (endFrame - startFrame) / writingSpeed
const inferredEndFrame = endFrame !== -1 && writingSpeed > 0
? startFrame + (endFrame - startFrame) / writingSpeed
: endFrame;
diff --git a/web/web-sdui/src/views/TransparentTextView.tsx b/web/web-sdui/src/views/TransparentTextView.tsx
index 32f259a0..239e5967 100644
--- a/web/web-sdui/src/views/TransparentTextView.tsx
+++ b/web/web-sdui/src/views/TransparentTextView.tsx
@@ -1,9 +1,9 @@
import React from 'react';
-import type { MotionViewProps } from '../infra/types';
+import type { MotionViewProps, MotionConfig } from '../infra/types';
import { useApplyEffects } from '../effects/useApplyEffects';
-export const TransparentTextView: React.FC<{ props: MotionViewProps; currentFrame: number }> = ({ props, currentFrame }) => {
- const style = useApplyEffects(props, currentFrame);
+export const TransparentTextView: React.FC<{ props: MotionViewProps; currentFrame: number; config: MotionConfig }> = ({ props, currentFrame, config }) => {
+ const style = useApplyEffects(props, currentFrame, config);
return (
diff --git a/web/web-sdui/src/views/VideoFrameView.tsx b/web/web-sdui/src/views/VideoFrameView.tsx
index ad48f136..8f91077d 100644
--- a/web/web-sdui/src/views/VideoFrameView.tsx
+++ b/web/web-sdui/src/views/VideoFrameView.tsx
@@ -1,35 +1,29 @@
-import React, { useEffect, useRef } from 'react';
-import type { MotionViewProps } from '../infra/types';
+import React from 'react';
+import type { MotionViewProps, MotionConfig } from '../infra/types';
import { useApplyEffects } from '../effects/useApplyEffects';
-export const VideoFrameView: React.FC<{ props: MotionViewProps; currentFrame: number }> = ({ props, currentFrame }) => {
- const style = useApplyEffects(props, currentFrame);
- const videoRef = useRef(null);
-
- useEffect(() => {
- if (videoRef.current) {
- // Approximate frame to time
- // Assuming 24fps if not specified in config (though config should be available globally)
- const fps = 24;
- videoRef.current.currentTime = currentFrame / fps;
- }
- }, [currentFrame]);
+export const VideoFrameView: React.FC<{ props: MotionViewProps; currentFrame: number; config: MotionConfig }> = ({ props, currentFrame, config }) => {
+ const style = useApplyEffects(props, currentFrame, config);
+ // In a real implementation, we would seek the video to the current frame.
+ // For SDUI preview, we might just show a placeholder or the first frame.
return (
-
+ >
+ Video: {props.videoUri}
+
);
};
diff --git a/web/web-sdui/src/views/ViewRegistry.tsx b/web/web-sdui/src/views/ViewRegistry.tsx
index 3f8e40c3..6cdf5afc 100644
--- a/web/web-sdui/src/views/ViewRegistry.tsx
+++ b/web/web-sdui/src/views/ViewRegistry.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import type { MotionViewProps } from '../infra/types';
+import type { MotionViewProps, MotionConfig } from '../infra/types';
import { TransparentTextView } from './TransparentTextView';
import { GradientView } from './GradientView';
import { VideoFrameView } from './VideoFrameView';
@@ -7,20 +7,25 @@ import { WordWriterTextView } from './WordWriterTextView';
import { AudioWaveformView } from './AudioWaveformView';
import { PopUpTextView } from './PopUpTextView';
import { MultiLyricsContainer } from './MultiLyricsContainer';
+import { WordBlinkTextView } from './WordBlinkTextView';
+import { MotionImageView } from './MotionImageView';
-export const ViewRegistry: Record
> = {
+export const ViewRegistry: Record> = {
TransparentTextView: TransparentTextView,
TypeWriterTextView: WordWriterTextView,
WordWriterTextView: WordWriterTextView,
+ WordBlinkTextView: WordBlinkTextView,
GradientView: GradientView,
VideoFrameView: VideoFrameView,
+ MotionImageView: MotionImageView,
+ CircularMotionImageView: MotionImageView,
CircularAudioWaveformView: AudioWaveformView,
RadialAudioWaveformView: AudioWaveformView,
PopUpTextView: PopUpTextView,
MultiLyricsContainer: MultiLyricsContainer,
};
-export const MotionViewRenderer: React.FC<{ props: MotionViewProps; currentFrame: number }> = ({ props, currentFrame }) => {
+export const MotionViewRenderer: React.FC<{ props: MotionViewProps; currentFrame: number; config: MotionConfig }> = ({ props, currentFrame, config }) => {
const Component = ViewRegistry[props.type];
// Visibility check
@@ -32,5 +37,5 @@ export const MotionViewRenderer: React.FC<{ props: MotionViewProps; currentFrame
return Unknown View: {props.type}
;
}
- return ;
+ return ;
};
diff --git a/web/web-sdui/src/views/WordBlinkTextView.tsx b/web/web-sdui/src/views/WordBlinkTextView.tsx
new file mode 100644
index 00000000..fecb6aa8
--- /dev/null
+++ b/web/web-sdui/src/views/WordBlinkTextView.tsx
@@ -0,0 +1,49 @@
+import React from 'react';
+import type { MotionViewProps, MotionConfig } from '../infra/types';
+import { useApplyEffects } from '../effects/useApplyEffects';
+import { interpolateForRange, Easing } from '../infra/interpolation';
+
+export const WordBlinkTextView: React.FC<{ props: MotionViewProps; currentFrame: number; config: MotionConfig }> = ({ props, currentFrame, config }) => {
+ const style = useApplyEffects(props, currentFrame, config);
+
+ const text = props.text || "";
+ const startFrame = props.startFrame;
+ const endFrame = props.endFrame;
+
+ const words = text.split(" ");
+ const wordCount = words.length;
+
+ const progress = interpolateForRange(
+ Easing.LINEAR,
+ currentFrame,
+ startFrame,
+ endFrame,
+ 0,
+ wordCount
+ );
+
+ const visibleWordIndex = Math.max(0, Math.floor(progress - 0.00001));
+ const currentWord = words[visibleWordIndex] || "";
+
+ return (
+
+ {currentWord}
+
+ );
+};
diff --git a/web/web-sdui/src/views/WordWriterTextView.tsx b/web/web-sdui/src/views/WordWriterTextView.tsx
index 128f579a..70c4ebc7 100644
--- a/web/web-sdui/src/views/WordWriterTextView.tsx
+++ b/web/web-sdui/src/views/WordWriterTextView.tsx
@@ -1,16 +1,15 @@
import React from 'react';
-import type { MotionViewProps } from '../infra/types';
+import type { MotionViewProps, MotionConfig } from '../infra/types';
import { useApplyEffects } from '../effects/useApplyEffects';
-export const WordWriterTextView: React.FC<{ props: MotionViewProps; currentFrame: number }> = ({ props, currentFrame }) => {
- const style = useApplyEffects(props, currentFrame);
+export const WordWriterTextView: React.FC<{ props: MotionViewProps; currentFrame: number; config: MotionConfig }> = ({ props, currentFrame, config }) => {
+ const style = useApplyEffects(props, currentFrame, config);
const text = props.text || "";
const writingSpeed = props.writingSpeed || 1;
const startFrame = props.startFrame;
// Calculate how many characters to show
- // This is a simplification of the Android logic
const elapsedFrames = currentFrame - startFrame;
const charsToShow = Math.floor(elapsedFrames * writingSpeed);
diff --git a/web/web-sdui/tsconfig.app.json b/web/web-sdui/tsconfig.app.json
index 7f42e5f7..864e8e73 100644
--- a/web/web-sdui/tsconfig.app.json
+++ b/web/web-sdui/tsconfig.app.json
@@ -16,8 +16,8 @@
"jsx": "react-jsx",
/* Linting */
- "noUnusedLocals": true,
- "noUnusedParameters": true,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},