feat: playbackrate menu

This commit is contained in:
shinya
2025-07-03 19:35:37 +08:00
parent 6e7bbf2d5a
commit 59155e6082
2 changed files with 59 additions and 12 deletions

View File

@@ -152,3 +152,22 @@ div[data-media-provider] video {
height: 100%; height: 100%;
object-fit: contain; object-fit: contain;
} }
/* Vidstack Menu 自定义样式:背景黑色,文字白色 */
.vds-menu-items {
background-color: #000;
color: #fff;
}
.vds-menu-items .vds-radio {
padding: 0rem 1rem; /* 减少单项内边距 */
margin: 0; /* 清除默认外边距 */
}
.vds-menu-items .vds-radio + .vds-radio {
margin-top: 2px; /* 相邻项之间仅 2px 间隔 */
}
.vds-menu-items .vds-radio-label {
font-size: 0.875rem; /* 调小字号 */
}

View File

@@ -8,10 +8,13 @@ import {
isHLSProvider, isHLSProvider,
MediaPlayer, MediaPlayer,
MediaProvider, MediaProvider,
Menu,
RadioGroup,
SeekButton, SeekButton,
} from '@vidstack/react'; } from '@vidstack/react';
import { import {
AirPlayIcon, AirPlayIcon,
CheckIcon,
SeekBackward10Icon, SeekBackward10Icon,
SeekForward10Icon, SeekForward10Icon,
} from '@vidstack/react/icons'; } from '@vidstack/react/icons';
@@ -1433,7 +1436,10 @@ function PlayPageClient() {
> >
<AdBlockIcon enabled={blockAdEnabled} /> <AdBlockIcon enabled={blockAdEnabled} />
</button> </button>
<PlaybackRateButton playerRef={playerRef} /> <PlaybackRateButton
playerRef={playerRef}
playerContainerRef={playerContainerRef}
/>
{/* 自定义 AirPlay 按钮 */} {/* 自定义 AirPlay 按钮 */}
<AirPlayButton className='vds-button'> <AirPlayButton className='vds-button'>
<AirPlayIcon className='vds-icon' /> <AirPlayIcon className='vds-icon' />
@@ -1787,8 +1793,10 @@ function PlayPageClient() {
const PlaybackRateButton = ({ const PlaybackRateButton = ({
playerRef, playerRef,
playerContainerRef,
}: { }: {
playerRef: React.RefObject<any>; playerRef: React.RefObject<any>;
playerContainerRef: React.RefObject<HTMLDivElement>;
}) => { }) => {
const [rate, setRate] = useState(1); const [rate, setRate] = useState(1);
const rates = [0.75, 1.0, 1.25, 1.5, 2.0, 3.0]; const rates = [0.75, 1.0, 1.25, 1.5, 2.0, 3.0];
@@ -1803,18 +1811,38 @@ const PlaybackRateButton = ({
return unsubscribe; return unsubscribe;
}, [playerRef]); }, [playerRef]);
const cycleRate = () => {
const player = playerRef.current;
if (!player) return;
const currentIndex = rates.indexOf(rate);
const nextIndex = (currentIndex + 1) % rates.length;
player.playbackRate = rates[nextIndex];
};
return ( return (
<button className='vds-button' onClick={cycleRate}> <Menu.Root className='vds-menu'>
{rate === 1 ? '倍速' : `${rate.toFixed(2)}x`} <Menu.Button className='vds-menu-button vds-button' aria-label='Settings'>
</button> <span></span>
</Menu.Button>
<Menu.Items className='vds-menu-items' placement='top' offset={0}>
<RadioGroup.Root
className='vds-radio-group'
aria-label='Custom Options'
value={rate.toString()}
onChange={(value) => {
const player = playerRef.current;
if (!player) {
return;
}
player.playbackRate = Number(value);
playerContainerRef.current?.focus();
}}
>
{rates.reverse().map((rate) => (
<RadioGroup.Item
className='vds-radio'
value={rate.toString()}
key={rate}
>
<CheckIcon className='vds-icon' />
<span className='vds-radio-label'>{rate}</span>
</RadioGroup.Item>
))}
</RadioGroup.Root>
</Menu.Items>
</Menu.Root>
); );
}; };