8000 test: add vidoe demo by xuanhun · Pull Request #260 · VisActor/VStory · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

test: add vidoe demo #260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@
// Typescript
"typescript.preferences.importModuleSpecifier": "project-relative",
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
}
}
75 changes: 75 additions & 0 deletions NumberScroll.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { useEffect, useState, useRef } from 'react';
import './NumberScroll.css';

interface NumberScrollProps {
endNumber: number; // 目标数字
duration?: number; // 动画持续时间(ms)
delay?: number; // 开始延迟时间(ms)
className?: string; // 自定义样式类名
isRunning?: boolean; // 控制是否开始滚动
}

export const NumberScroll: React.FC<NumberScrollProps> = ({
endNumber,
duration = 2000,
delay = 0,
className = '',
isRunning = false
}) => {
const [currentNumber, setCurrentNumber] = useState(0);
const startTime = useRef<number | null>(null);
const animationFrame = useRef<number>();

const formatNumber = (num: number) => {
return new Intl.NumberFormat('zh-CN').format(Math.round(num));
};

useEffect(() => {
// 重置状态
startTime.current = null;

if (!isRunning) {
if (animationFrame.current) {
cancelAnimationFrame(animationFrame.current);
}
setCurrentNumber(0);
return;
}

const animate = (timestamp: number) => {
if (!startTime.current) startTime.current = timestamp;

const progress = timestamp - startTime.current;
const percentage = Math.min(progress / duration, 1);

const easeOutExpo = 1 - Math.pow(2, -10 * percentage);
const currentValue = easeOutExpo * endNumber;

setCurrentNumber(currentValue);

if (percentage < 1 && isRunning) {
animationFrame.current = requestAnimationFrame(animate);
}
};

const timer = setTimeout(() => {
animationFrame.current = requestAnimationFrame(animate);
}, delay);

return () => {
clearTimeout(timer);
if (animationFrame.current) {
cancelAnimationFrame(animationFrame.current);
}
};
}, [endNumber, duration, delay, isRunning]);

// 如果不在运行状态,返回空内容
if (!isRunning) return null;

return (
<div className={`number-scroll ${className}`}>
{formatNumber(currentNumber)}
</div>
);
};
42 changes: 42 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dependencies": {
"number-flip": "^1.2.3"
< 8000 span class='blob-code-inner blob-code-marker ' data-code-marker="+"> }
}
10 changes: 10 additions & 0 deletions packages/vstory/demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ import { TableTheme } from './demos/table/runtime/theme';
import { TableStyle } from './demos/table/runtime/style';
import { TableVisible } from './demos/table/runtime/visible';
import { SpecMarker } from './demos/chart/runtime/spec-marker';
import { News } from './demos/works/News/News';
import { TariffWar } from './demos/works/tariff-war';

type MenuItem = {
name: string;
Expand Down Expand Up @@ -181,6 +183,10 @@ const App = () => {
name: 'VScreen',
component: VScreen
},
{
name: 'News',
component: News
},
{
name: 'LabelComponent',
component: LabelWorks
Expand All @@ -196,6 +202,10 @@ const App = () => {
{
name: 'NationalMemorial',
component: NationalMemorial
},
{
name: 'TariffWar',
component: TariffWar
}
]
},
Expand Down
136 changes: 136 additions & 0 deletions packages/vstory/demo/src/demos/works/News/News.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import React, { useEffect } from 'react';
import { IChartCharacterConfig, IStoryDSL, Player, Story } from '../../../../../../vstory-core/src';
import { registerAll } from '../../../../../src';
import { bar1, bar1Action } from './bar1';
import { arrow, arrowAction } from './arrow';
import { NumberScroll } from './NumberScroll';
import { progress, progressAction } from './progress';

registerAll();

async function loadDSL(): Promise<IStoryDSL> {
const dsl: IStoryDSL = {
characters: [bar1, arrow, progress],
acts: [
{
id: 'default-chapter',
scenes: [
{
id: 'scene0',
actions: [bar1Action, arrowAction, progressAction]
}
]
}
]
};

return new Promise(resolve => {
const video = document.getElementById('news-video');
if (video) {
video.addEventListener('canplay', () => {
console.log('canplay');
resolve(dsl);
});
} else {
resolve(dsl); // 如果没有找到视频元素,直接返回 DSL
}
});
}

export const News = () => {
const id = 'news';
const videoUrl = 'https://cdn.jsdelivr.net/gh/xuanhun/articles/visactor/vstory/20250312-163446.mp4';
const [showPlayButton, setShowPlayButton] = React.useState(true);
const [isRunning, setIsRunning] = React.useState(false);

useEffect(() => {
const canvas = document.getElementById('news-canvas');
const story = new Story(null, {
canvas,
width: 478,
height: 629,
background: 'rgba(0, 0, 0, 0)'
});

const player = new Player(story);
story.init(player);

loadDSL().then(dsl => {
story.load(dsl);
const video: HTMLVideoElement = document.getElementById('news-video') as HTMLVideoElement;

video.addEventListener('play', () => {
player.tickTo(0);
player.play(-1);
setTimeout(() => {
setIsRunning(true);
}, 12000);
});

// 添加视频结束事件监听
video.addEventListener('ended', () => {
video.currentTime = 0; // 回到第一帧
setShowPlayButton(true); // 显示播放按钮
});
});

return () => {
story.release();
};
}, []);

return (
<div style={{ width: '478px', height: '629px', position: 'relative' }} id={id}>
<canvas id="news-canvas" style={{ width: 478, height: 629, position: 'absolute', top: 0, left: 0, zIndex: 1 }} />
<video
id="news-video"
src={videoUrl}
autoPlay
width={478}
height={629}
style={{
position: 'absolute',
top: 0,
left: 0
}}
/>
<NumberScroll
startNumber={13000000000}
endNumber={13400000000}
duration={3000}
isRunning={isRunning}
=> {
setTimeout(() => {
setIsRunning(false);
}, 1000);
}}
/>

{showPlayButton && (
<button
=> {
const video = document.getElementById('news-video') as HTMLVideoElement;
video?.play();
setShowPlayButton(false);
}}
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
zIndex: 2,
padding: '12px 24px',
fontSize: '16px',
backgroundColor: 'rgba(0, 0, 0, 0.6)',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
播放
</button>
)}
</div>
);
};
16 changes: 16 additions & 0 deletions packages/vstory/demo/src/demos/works/News/NumberScroll.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.number-scroll {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-size: 2rem;
font-weight: bold;
color: #e2e7eb;
transition: color 0.3s ease;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color:rgba(0,0,0,0.5);
}

.number-scroll.active {
color: #1890ff;
}
Loading
Loading
0