react-vegas
v0.4.0
Published
Vegas.js for React
Readme
React Vegas 幻灯片组件
jQuery 插件 Vegas Background SlideShow 的 React 部分移植版本。组件铺满父元素轮播图片和视频,过渡动画由 Motion 实现。哪些已移植、哪些没有,见与 vegas.js 的差异。
简体中文 | English
功能特性
- 支持图片和视频幻灯片
- 27 种内置过渡效果(upstream 的 13 种基础效果——
fade、blur、flash、negative、burn、slideLeft、slideRight、slideUp、slideDown、zoomIn、zoomOut、swirlLeft、swirlRight——各自有一个 plain 名称和一个2变体,再加上本移植版本特有的zoomInOut), 可全局设置,也可按页设置,支持'random'和名称数组 - 自定义过渡:
transitionRegister登记的名称完全靠 CSS 类生效 (vegas-transition-{name}/-in/-out),样式完全由使用者的 CSS 定义 - Ken Burns 缓慢缩放/平移(
animation,9 种内置效果),也支持animationRegister登记自定义名称 - 自动播放,停留时间可全局设置,也可按页设置
- 水平、垂直对齐,
cover、contain,或平铺的'repeat'填充 - 随机播放和循环播放
- 图片和视频预加载,可显示加载进度
- 遮罩层(纯色或重复的图案图片)、单张幻灯片的倒计时进度条,以及承载
children的内容层 - 通过 ref 调用
play、pause、toggle、next、previous、jump、current、playing - 浏览器标签页隐藏时自动暂停
- 遵循系统的"减少动态效果"(
prefers-reduced-motion)设置:过渡效果回退为纯fade,Ken Burns 整体禁用 - TypeScript 类型:
VegasProps、SlideProps、SlideVideo、SlideVideoConfig、VegasHandle、TransitionName、AnimationName、TransitionSetting、AnimationSetting、DurationSetting、VegasPhase、HorizontalAlign、VerticalAlign,以及TRANSITION_NAMES/ANIMATION_NAMES常量与isTransitionName/isAnimationName类型守卫
安装说明
npm
npm install react-vegasyarn
yarn add react-vegaspnpm
pnpm add react-vegas需要 React 18 或 19。包只提供 ESM 格式;样式全部内联,无需引入 CSS 文件。
基础用法
import { Vegas } from "react-vegas";
import type { SlideProps } from "react-vegas";
const slides: SlideProps[] = [
{
src: '/images/slide1.jpg',
transition: 'fade'
},
{
src: '/images/slide2.jpg',
transition: 'slideLeft'
}
];
const App = () => {
return (
<div style={{ height: '100vh' }}>
<Vegas
slides={slides}
delay={5000}
autoplay={true}
>
{/* 可选:渲染在幻灯片/遮罩层/进度条之上的内容层 */}
<h1>Hello</h1>
</Vegas>
</div>
);
};Vegas 会占满父元素的宽度和高度,所以父元素自身需要有高度。
slides 不需要保持稳定的引用:每次渲染都重新创建的内联数组字面量不会让幻灯片重新开始,也不会重置自动播放计时器。启动流程(预加载、defaultBackground、第一张幻灯片)每次挂载只执行一次。之后 slides.length 或 shuffle 发生变化时只会重建播放顺序,并尽量保留当前正在显示的幻灯片。
高级用法
import { Vegas } from "react-vegas";
import type { SlideProps } from "react-vegas";
const slides: SlideProps[] = [
{
src: '/images/slide1.jpg',
align: 'center', // 水平对齐
valign: 'center', // 垂直对齐
transition: 'fade', // 过渡效果
transitionDuration: 2000, // 本页过渡时长
delay: 5000 // 本页停留时间
},
{
src: '/videos/video1.mp4',
video: {
src: [ // 多格式支持
'/videos/video1.mp4',
'/videos/video1.webm'
],
muted: true, // 静音播放
loop: false // 不循环,播放结束后切换到下一张
},
transition: 'zoomIn' // 缩放进入效果
}
];
<Vegas
slides={slides}
autoplay={true} // 启用自动播放
overlay={true} // 启用遮罩层
timer={false} // 隐藏进度条
delay={7000} // 全局停留时间
shuffle={true} // 随机播放
preload={true} // 预加载图片和视频
transitionDuration={3000} // 全局过渡时长
firstTransitionDuration={5000} // 第一张幻灯片的过渡时长
defaultBackground="/images/loading.jpg" // 加载背景图
defaultBackgroundDuration={2000} // 加载背景显示时间
debug={false} // 关闭调试日志
color="#000" // 背景色
/>组件属性
容器属性
| 属性 | 类型 | 默认值 | 说明 |
|-----------|---------------|-----------|----|
| className | string | undefined | 根容器元素的 className |
| style | CSSProperties | undefined | 内联样式,覆盖在容器默认样式(position/size/overflow)之上 |
| children | ReactNode | undefined | 渲染在幻灯片/遮罩层/进度条之上的内容层(对应 upstream 的 .vegas-content) |
内容层本身是 overflow: auto(对齐 upstream 的 .vegas-content-scrollable),所以比容器更高的 children 会自己滚动,而不会被容器的 overflow: hidden 直接裁掉。
基础属性
| 属性 | 类型 | 默认值 | 说明 |
|----------|--------------|-------|----|
| slides | SlideProps[] | 必填 | 幻灯片对象数组,字段见幻灯片对象属性。空数组时什么都不渲染 |
| slide | number | 0 | 首张显示的幻灯片索引(会被收窄到合法范围内) |
| delay | number | 5000 | 每张幻灯片作为当前幻灯片的停留时间(毫秒),从切换到它的那一刻开始计时,因此包含过渡动画的时间。可被幻灯片自身的 delay 覆盖 |
| videoMaxDelay | number | 300000 | delay: 'video'(见视频幻灯片)的幻灯片卡在缓冲时的兜底上限(毫秒)。本移植版本特有,upstream 没有对应实现 |
| autoplay | boolean | true | 第一张幻灯片一展示出来就开始自动播放。设为 false 时展示第一张并保持暂停,调用 play() 才开始播放。挂载后修改这个属性只会播放或暂停,不会让幻灯片重新开始 |
| loop | boolean | true | 播完最后一张后回到第一张。设为 false 时,在最后一张调用 next() 会触发 onEnd 并停止播放,在第一张调用 previous() 不做任何事 |
| shuffle | boolean | false | 随机打乱播放顺序,首张仍由 slide 决定。配合 loop 时,每完整播放完一轮就会为下一轮重新抽取一个随机顺序(本移植版本特有,见与 vegas.js 的差异) |
| debug | boolean | false | 在浏览器控制台输出日志 |
过渡效果
解析出来的过渡时长(不论是第一张还是之后的幻灯片)都会被收窄到目标幻灯片自己的有效延迟
(slide.delay ?? delay)以内,对齐 upstream 的 _goto。
'random'/数组/transitionRegister 登记的自定义名称都只在每一次真正提交的切换发生时解析
一次——不是每次渲染都重新算——所以跟这次切换无关的重新渲染不会让正在播放的过渡突然变成另一种。
完整名单、'random'/数组的语义、自定义过渡见可用过渡效果。
| 属性 | 类型 | 默认值 | 说明 |
|-------------------------|-----------------------|-----------------------|----|
| transition | TransitionSetting | 'fade' | 默认过渡效果:具体名称、'random',或名称数组。未知且未登记的名称按 fade 处理 |
| transitionDuration | number | 'auto' | 1000 | 过渡时长(毫秒),或 'auto'(等于目标幻灯片的有效延迟) |
| firstTransition | TransitionSetting | null | null | 第一张幻灯片的过渡效果,不设置时沿用该页或全局的 transition |
| firstTransitionDuration | number | 'auto' | null | 同 transitionDuration | 第一张幻灯片的过渡时长(毫秒),或 'auto'。不设置时沿用第一张幻灯片本来的时长(它自己的 transitionDuration,否则全局值),与 upstream 一致 |
| transitionRegister | string | string[] | undefined | 登记自定义过渡名称,跟 27 个内置名称同等有效,见自定义过渡 |
Ken Burns(animation)相关属性
Ken Burns 是施加在幻灯片自己的图片/视频元素上的缓慢缩放/平移效果(不是外层的过渡包裹元素,
两者永远不会争抢 transform)。跟过渡一样只在每次真正提交的切换时解析一次;系统开启
"减少动态效果"时整体禁用。
| 属性 | 类型 | 默认值 | 说明 |
|------------------|------------------------|-------|----|
| animation | AnimationSetting | null | null | Ken Burns 效果:具体名称、'random'、名称数组,或 null 表示不使用,见Ken Burns |
| animationDuration | number | 'auto' | 'auto' | 时长(毫秒),或 'auto'(等于这张幻灯片的有效延迟)。跟 transitionDuration 不同,这个值从不被收窄到延迟以内 |
| animationRegister | string | string[] | undefined | 登记自定义 Ken Burns 名称,跟 9 个内置名称同等有效,见Ken Burns |
视觉属性
timer 对齐 upstream:固定在容器底部的 2px 白色进度条,在当前幻灯片的有效延迟(slide.delay ?? delay)
内从 0% 动画到 100%,每次切换都会重新开始。暂停/挂起时,以及幻灯片数量少于 2 张时,一直停在 0%;
第一张幻灯片准备好之前不会渲染。
overlay 接受 true(本移植版本自己的纯色遮罩,用 overlayColor——因为没有内置对齐 upstream
默认值的图案素材)或者一个 URL 字符串(渲染成重复、居中、透明度 50% 的背景图案,对齐 upstream
.vegas-overlay 的 CSS 与 _init 里对字符串 overlay 的处理)。false/空字符串不渲染任何东西。
align/valign 除了下面列出的关键字,也接受任意合法的 CSS 位置值(比如 "20%"、
"10px")——跟 upstream 一样不做校验;本移植版本把它们原样传给图片/视频的 object-position。
| 属性 | 类型 | 默认值 | 说明 |
|--------------|-------------------|-------------------|----|
| overlay | boolean | string | false | 显示遮罩层:true 是纯色遮罩,字符串是重复背景图案的 URL |
| overlayColor | string | 'rgba(0,0,0,0.3)' | overlay={true} 时遮罩的颜色,可用任意 CSS background 值。overlay 是 URL 字符串时无效 |
| timer | boolean | true | 显示单张幻灯片的倒计时进度条 |
| color | string | null | null | 容器和每张幻灯片底下的背景色 |
| cover | boolean | 'repeat' | true | true 铺满区域(object-fit: cover),false 完整显示图片或视频(contain),'repeat' 按原始尺寸平铺图片(幻灯片自己的视频不支持 'repeat',会退化为 cover,见与 vegas.js 的差异) |
| align | 'left' \| 'center' \| 'right' 或任意 CSS 位置值 | 'center' | 图片或视频的水平位置 |
| valign | 'top' \| 'center' \| 'bottom' 或任意 CSS 位置值 | 'center' | 图片或视频的垂直位置 |
加载配置
preload 对齐 upstream 语义:它是"图片 AND 视频"的总开关;preloadImage/preloadVideo 各自独立生效,
所以单独 preload={true} 就会同时预加载图片和视频,单独 preloadImage={true} 也不需要 preload 就能
预加载图片。三者只要有一个开启,启动流程就会经过预加载阶段,等待所有需要预加载的图片加载完成
(视频会在离屏的 <video> 元素里开始预加载,但不会等待它们),并在 showLoading 开启时显示带进度的
加载指示器——这种"阻塞式启动 + 进度展示"是本移植版本特有的行为,详见《与 vegas.js 的差异》。
| 属性 | 类型 | 默认值 | 说明 |
|---------------------------|---------|--------------|----|
| preload | boolean | false | 在第一张幻灯片之前同时预加载图片和视频 |
| preloadImage | boolean | false | 预加载图片(独立于 preload 生效) |
| preloadImageBatch | number | 3 | 同时加载的图片数量。非有限数或 < 1 会回退到 3;小数会向下取整 |
| preLoadImageBatch | number | undefined | preloadImageBatch 的旧属性名,已废弃(会触发一次性开发环境警告);设置了 preloadImageBatch 时此项无效 |
| preloadVideo | boolean | false | 预加载视频(独立于 preload 生效)。预加载阶段不会等待视频加载完成 |
| showLoading | boolean | false | 预加载阶段显示加载框和图片加载进度 |
| loadingText | string | 'Loading...' | 加载框中的文字,后面跟百分比 |
| defaultBackground | string | undefined | 预加载结束后、第一张幻灯片出现前显示的背景图 |
| defaultBackgroundDuration | number | 3000 | defaultBackground 的显示时长(毫秒) |
图片加载门控
对齐 upstream 的 _goto(img.onload = go):一次切换(自动播放、next/previous/jump、
play() 的立即切换、或每完整播放完一轮的重新洗牌)在目标图片幻灯片加载完成之前永远不会
提交——跟这次切换有关的一切(状态、切换锁、onWalk、delay 计时器、进度条)都不会发生,
之前展示的幻灯片原样保持不变。这个规则同样适用于第一张幻灯片:幻灯片图层只有在它的图片
准备好之后才会出现(之前容器只显示 color)。视频幻灯片永远不需要等待。已经加载过的图片
(被之前某次切换加载过,或者被 preload/preloadImage 预加载过)会同步提交,不会增加任何
延迟。目标还在加载期间,新的切换请求会像正在切换动画中一样被忽略。
如果图片加载失败,这次切换仍然会提交(upstream 遇到 onerror 会永远卡住;这个移植版本选择
记录错误并触发一次性的开发环境警告,而不是卡死播放)。pause() 会取消一个自动播放还在等待
中的切换(这样它加载完成后就不会再发生);用户自己发起的切换(next()/previous()/jump()/
play())即便在等待期间组件被暂停了,加载完成后仍然会照常提交,对齐 upstream 的 next()
在暂停时依然生效。
回调函数
onPlay/onPause/onWalk/onEnd 会收到触发那一刻的当前幻灯片:(index: number, slide: SlideProps) => void。
零参数的回调函数依然可以正常工作(JS/TS 允许用比声明的参数更多的实参调用函数)。
onWalk 会在每一次真正的切换(自动播放、next/previous/jump)时触发,并且额外为第一张幻灯片触发一次——
就在它真正展示出来的那一刻(对齐 upstream:每一次 _goto 都会 trigger('walk'),包括最初那一次)。
这次"首帧"触发只会发生一次,在任何导航之前,且在 StrictMode 下也安全(不会被重复触发)。
onEnd 会在 loop={false} 且已经停在最后一张时调用 next()(不管是手动调用还是自动播放触发的)
时触发——对齐 upstream 的 _end()。播放会停止,但这一次停止不会额外触发 onPause
(upstream 的 _end() 只 trigger end,从不 trigger pause);之后再显式调用 pause()/play()
则完全正常触发。
onInit 收到挂载时解析好的完整配置对象(所有 props 都套用过默认值,transitionRegister/
animationRegister 已经归一化成数组),对齐 upstream 的 init 事件/回调收到合并后的 settings
对象(trigger('init'))。即便在 React.StrictMode 下 effect 被重复调用两次,也只会触发一次。
| 属性 | 类型 | 说明 |
|---------|--------------------------------------------|----|
| onInit | (settings: VegasProps) => void | 组件挂载时触发一次,参数是解析好的完整配置对象 |
| onPlay | (index: number, slide: SlideProps) => void | 每次开始或恢复播放时触发,包括 autoplay 的自动开始,以及隐藏的标签页重新可见后的恢复 |
| onPause | (index: number, slide: SlideProps) => void | 从播放变为暂停时触发:调用 pause()、toggle()、autoplay 变为 false、或标签页隐藏。因为刚触发过 onEnd 而停止时不会触发 |
| onWalk | (index: number, slide: SlideProps) => void | 幻灯片切换时触发,参数是切换到的目标幻灯片;也会为展示出来的第一张幻灯片触发一次 |
| onEnd | (index: number, slide: SlideProps) => void | loop={false} 时 next() 到达最后一张时触发 |
幻灯片对象属性
SlideProps 类型可从包中导入:
interface SlideProps {
src: string; // 图片地址。视频幻灯片也必须填写,但不会被渲染
color?: string | null; // 本页背景色
delay?: number | 'video' | null; // 本页停留时间,覆盖全局 delay;'video' 让视频幻灯片
// 完整播放一遍再切换(见下面的"视频幻灯片")。图片幻灯片
// 上设置 'video' 等同未设置(本移植版本特有,upstream 无对应实现)
align?: 'left' | 'center' | 'right' | string; // 水平对齐(也接受任意 CSS 位置值)
valign?: 'top' | 'center' | 'bottom' | string; // 垂直对齐(也接受任意 CSS 位置值)
transition?: TransitionSetting | null; // 本页过渡效果:具体名称、'random',或数组
transitionDuration?: number | 'auto' | null; // 本页过渡时长;同时决定被它替换的旧幻灯片的离场时长
animation?: AnimationSetting | null; // 本页 Ken Burns 效果,覆盖全局配置
animationDuration?: number | 'auto' | null; // 本页 Ken Burns 时长(毫秒),或 'auto'
cover?: boolean | 'repeat'; // 覆盖全局 cover;'repeat' 平铺图片
video?: SlideVideo; // 视频配置,见下面的"视频幻灯片"
}
// 数组简写(等价于 { src: [...] },muted/loop 都取默认值 true),或完整形式
interface SlideVideoConfig {
src: string[]; // 多格式源,浏览器播放第一个支持的
muted?: boolean; // 静音,不传时默认 true(对齐 upstream
// `'muted' in videoSettings ? videoSettings.muted : true`)
loop?: boolean; // 循环,不传时默认 true(对齐 upstream
// `'loop' in videoSettings ? videoSettings.loop : true`)——
// 只有显式传 false 才会在播放结束时切换到下一张
}
type SlideVideo = string[] | SlideVideoConfig;SlideVideo/SlideVideoConfig 与 SlideProps 一样从包中导出。
视频幻灯片
const slides: SlideProps[] = [
{
src: '/img/poster.jpg',
video: {
src: ['/video/intro.mp4', '/video/intro.webm'],
muted: true,
loop: false
}
},
{ src: '/img/photo.jpg', transition: 'fade' }
];muted/loop 都默认 true,所以上面的 muted: true 其实可以省略,这里写出来只是为了清楚。
设置 loop: false 时,视频播放结束会切换到下一张。如果视频播完时幻灯片正处于暂停状态,切换会被
挂起,等播放恢复后才真正执行——跟下文"播完再切"(delay: 'video')的排队规则完全一致。
只需要设置源地址时,可以用继承自 upstream Vegas.js 的数组简写,等价于 { src: [...] }(muted/
loop 都取默认值):
const slides: SlideProps[] = [
{ src: '/img/poster.jpg', video: ['/video/intro.mp4', '/video/intro.webm'] }
];源列表为空时(
video: []或video: { src: [] }),这张幻灯片会按图片幻灯片渲染。一个没有 任何<source>的<video>既放不出画面,也永远不会触发ended,配合delay: 'video'只能干等videoMaxDelay兜底;upstream 会照样渲染一个这样的空<video>(_video),这个移植版本不会。
音量淡入淡出
一次切换涉及 muted: false 的视频时,它的音量会淡入或淡出,对齐 upstream 的
_fadeInSound/_fadeOutSound:
- 进入的视频音量从
0淡入到满,时长是这次切换实际生效的过渡时长。 - 离开的视频音量淡出到
0,淡出结束时暂停——不暂停的话它会在新视频的声音之上继续播放, 直到离场动画结束、元素真正被移除为止。 muted: true(默认)的视频音量不受影响,只在离场时暂停;只有真正不静音的视频才会淡变。 跟 upstream 不同,这个移植版本从不在淡出前把静音视频的音量强制设为1——那个 upstream 的小瑕疵不会被照搬(见与 vegas.js 的差异)。
浏览器的自动播放策略会拦截带声音的自动播放。
muted: false通常需要用户先与页面发生一次交互, 否则play()会被拒绝(开启debug可以在控制台看到警告)。
播完再切(delay: 'video')
视频幻灯片默认跟图片幻灯片一样:停留时间到了就切换,不管视频播完没有。想让视频幻灯片播完
一遍再切走,把该页的 delay 设为 'video'(本移植版本特有,upstream 无对应实现):
<Vegas
videoMaxDelay={120000}
slides={[
{ src: '/img/poster.jpg', delay: 'video', video: { src: ['/video/intro.mp4'] } },
{ src: '/img/photo.jpg' }
]}
/>- 视频完整播放一遍后切到下一张;
video.loop在这里不起作用(一段真的循环播放的视频永远不会 触发ended)。 - 如果视频卡在缓冲、迟迟播不完,会在
videoMaxDelay(默认 5 分钟,复用既有的自动播放定时器 兜底,不是一个独立的计时器)后切走;如果所有视频源都加载失败,或者播放途中出现致命错误, 会立即切走。 - 只有一张幻灯片时没有下一张可切,会按
video.loop正常播放。 - 进度条,以及
'auto'的transitionDuration/animationDuration,都基于这张幻灯片自己的 基准delay(即全局delay,因为'video'不是数字),从不受videoMaxDelay兜底上限影响。 - 播放暂停期间是否会切走,遵循跟其他任何一次切换相同的规则:见"图片加载门控"与 组件方法。
可用过渡效果
transition/firstTransition/幻灯片自己的 transition 都接受 TransitionSetting:27 个内置
TransitionName 之一(也从包中导出,连同冻结的 TRANSITION_NAMES 数组与 isTransitionName
类型守卫)、经 transitionRegister 登记的名称、'random',或名称数组。既不是内置名称、也没有
登记过的名称会静默回退到 fade,并触发一次性的开发环境控制台警告。
13 个共有的基础效果各自都有一个 plain(不带后缀) 名称和一个 2 后缀 名称,跟 upstream
vegas.css 里的过渡规则完全对应:
- plain 名称(
fade、blur、flash、negative、burn、slideLeft、slideRight、slideUp、slideDown、zoomIn、zoomOut、swirlLeft、swirlRight)只让进入的幻灯片产生 动画。离场的旧幻灯片完全没有动画效果:它会原样保持在原来的样子、完全可见地留在新幻灯片下方, 贯穿整段切换过程(upstream 的vegas.css对这些名称根本没有定义-out规则)。 2名称(fade2、blur2、flash2、negative2、burn2、slideLeft2、slideRight2、slideUp2、slideDown2、zoomIn2、zoomOut2、swirlLeft2、swirlRight2)额外用 upstream 的-out规则给离场的旧幻灯片也加上动画。
各效果说明:
fade/fade2: 新幻灯片淡入(0 → 1)。fade2的旧幻灯片会淡出(1 → 0)。blur/blur2: 新幻灯片从模糊变清晰地淡入(blur(32px)→blur(0px),并略微提亮)。blur2的旧幻灯片只是淡出,模糊度保持在 0。flash/flash2: 新幻灯片从过曝的白色闪光淡入(brightness(25)→brightness(1))。flash2的旧幻灯片会闪光退场。negative/negative2: 新幻灯片从完全反色淡入(invert(100%)→invert(0))。negative2的旧幻灯片会反色退场。burn/burn2: 新幻灯片从过曝、过饱和的效果淡入(contrast(1000%) saturate(1000%)→ 正常)。burn2的旧幻灯片会以同样的效果退场。slideLeft/slideLeft2: 新幻灯片从右侧进入(100% → 0%)。slideLeft2的旧幻灯片会继续 向左移出屏幕(0% → -100%)。slideRight/slideRight2: 新幻灯片从左侧进入(-100% → 0%)。slideRight2的旧幻灯片会继续 向右移出屏幕(0% → 100%)。slideUp/slideUp2: 新幻灯片从下方进入(y 轴 100% → 0%)。slideUp2的旧幻灯片会继续 向上移出屏幕。slideDown/slideDown2: 新幻灯片从上方进入(y 轴 -100% → 0%)。slideDown2的旧幻灯片会继续 向下移出屏幕。zoomIn/zoomIn2: 进入时从 0 倍放大到 1 倍并淡入。zoomIn2的旧幻灯片会继续放大到 2 倍(--vegas-zoom-scale)并淡出。zoomOut/zoomOut2: 进入时从 2 倍缩小到 1 倍并淡入。zoomOut2的旧幻灯片会继续缩小到 0 倍并淡出。swirlLeft/swirlLeft2: 进入时从放大且旋转的状态恢复(scale(2) rotate(35deg)→scale(1) rotate(0))并淡入。swirlLeft2的旧幻灯片会朝相反方向旋转退场。swirlRight/swirlRight2: 旋转方向与swirlLeft相反。zoomInOut: 本移植版本特有效果(upstream 没有对应变体)——进入时淡入并从 1 倍放大到 1.25 倍, 离场时缩回 1 倍并淡出。
离场的旧幻灯片到底播放哪一种退场效果、持续多久,取决于接替它的那张新幻灯片自己解析出的过渡和
时长,而不是旧幻灯片自己的配置——比如一张 slideLeft2 的幻灯片被配置成 plain fade 的新幻灯片
接替时,旧幻灯片会按 fade(静态离场)退场,而不是按自己的 slideLeft2 出场规则移动。
'random' 会从"内置的 27 个名称 + transitionRegister 登记过的名称"这个合集里现场抽一个;
数组则直接从数组本身抽一个(不会跟 register 合并),对齐 upstream 的 _goto。无论哪种,抽取都
只在一次切换真正提交的那一刻发生一次,之后这次切换全程固定不变——绝不会被无关的重新渲染重新抽取。
firstTransition/firstTransitionDuration 只应用在整个组件生命周期里"第一次真正展示出来"的那张
幻灯片上,且必须发生在任何一次切换(next/previous/jump)之前——一旦发生过一次切换,之后的每一张
幻灯片永久使用自己/全局配置的 transition(一次切换永远不可能是第一张幻灯片)。这跟等待时长毫无关系:
第一张幻灯片的自动播放 delay 在它展示出来的那一刻就开始计时,跟之后的每一张完全一样(跟 upstream
一样,没有任何等待窗口)。
如果系统/浏览器开启了"减少动态效果"(prefers-reduced-motion),所有过渡效果都会渲染成纯 fade
(没有位移/缩放/滤镜),但时长不受影响。
自定义过渡
传给 transitionRegister 的名称(单个字符串,或数组)在任何用到内置名称的地方都同等有效——
作为 transition、firstTransition、幻灯片自己的 transition,出现在 'random' 的候选池里,
或出现在数组里。它完全不使用 Motion:进入的元素会先加上 vegas-transition-{name} 类,
(强制一次重排、让浏览器先记住起始样式后)再加上 vegas-transition-{name}-in 类,外加一个 inline
的 transition: all {duration}ms;离场的元素会在下一次切换的过渡开始的那一刻被加上
vegas-transition-{name}-out(由那次切换真正解析出的过渡驱动,跟上面"内置过渡的离场规则"完全
一样)。真正的视觉效果完全由使用者自己的 CSS 决定——这跟 upstream 的类名约定完全一致,因为
upstream 和这个包都不附带默认样式表让你覆盖。
/* 你自己的 CSS,用任何你平时加载 CSS 的方式引入 */
.vegas-transition-customWipe { clip-path: inset(0 100% 0 0); }
.vegas-transition-customWipe-in { clip-path: inset(0 0 0 0); }
.vegas-transition-customWipe-out { clip-path: inset(0 0 0 100%); }<Vegas
slides={[{ src: '/a.jpg', transition: 'customWipe' }]}
transitionRegister="customWipe"
transitionDuration={1200}
/>Ken Burns
animation 给每张幻灯片自己的图片/视频元素施加缓慢的缩放/平移(不是外层的过渡包裹元素,两者
永远不会争抢 transform)。9 个内置名称——kenburns、kenburnsUp、kenburnsDown、
kenburnsLeft、kenburnsRight、kenburnsUpLeft、kenburnsUpRight、kenburnsDownLeft、
kenburnsDownRight——移植自 upstream vegas.css 的 @keyframes,但改用 Motion 直接驱动
(动画媒体元素的 scale/x/y)而不是注入样式表,因为这个包不附带任何 CSS。kenburns 只做
缩放(1.5 倍 → 1 倍);带方向的名称还会从一个偏移位置平移回中心。跟 upstream(只给 <img>
幻灯片加动画)不同,这个移植版本对视频幻灯片同样生效。
animationDuration 默认 'auto'(等于这张幻灯片自己的有效延迟),且跟 transitionDuration
不同,从不被收窄——如果配置的时长比延迟更长,Ken Burns 会一直动画到下一张幻灯片的过渡开始。
animation/slide.animation 同样支持 'random'/数组,解析时机跟过渡完全一样(每次切换只解析
一次)。prefers-reduced-motion 会整体禁用 Ken Burns(不包裹、不产生任何动画)。
传给 animationRegister 的名称用法跟自定义过渡一样:不使用 Motion,而是给媒体元素加上
vegas-animation-{name} 类,外加一个 inline 的 animationDuration: {ms}ms——@keyframes
和其余的 CSS 动画属性由你自己提供(跟 upstream 的 vegas-animation-{name} 类名约定完全一致):
@keyframes vegas-animation-customPan {
0% { transform: scale(1.2) translateX(-5%); }
100% { transform: scale(1) translateX(0); }
}
.vegas-animation-customPan { animation-name: vegas-animation-customPan; animation-timing-function: ease-out; }<Vegas slides={[{ src: '/a.jpg', animation: 'customPan' }]} animationRegister="customPan" />调节效果强度
6 个 CSS 自定义属性(数值对齐 upstream vegas.css 的 :root 默认值),用来调节几个内置效果的强度:
| 变量 | 默认值 | 用于 |
|------------------------------|--------|----|
| --vegas-kenburns-scale | 1.5 | Ken Burns 的起始缩放比例 |
| --vegas-kenburns-translate | 10% | Ken Burns 的起始平移偏移量 |
| --vegas-blur-value | 32px | blur/blur2 的起始模糊半径 |
| --vegas-swirl-degree | 35deg | swirlLeft/swirlRight 的起始旋转角度 |
| --vegas-swirl-scale | 2 | swirlLeft/swirlRight 的起始缩放比例 |
| --vegas-zoom-scale | 2 | zoomIn/zoomOut 的外层缩放比例 |
Ken Burns 是原生 CSS animation,浏览器会照常在你设置这些变量的任何地方解析 var();但
blur/zoom/swirl 这几个过渡效果是由 Motion 的 JS 计算关键帧驱动的,它无法平滑地对一个
仍然包含未解析 var(...) 引用的值做插值——像纯 CSS 移植版本那样直接把 var() 字符串嵌进去,
只会让 Motion 瞬间跳变而不是真正播放动画。为了在不破坏动画的前提下仍然支持调节,这个移植版本
改为在每次解析过渡时,通过 Vegas 组件自己渲染的根 <div> 的 getComputedStyle 读取一次这个
变量的当前数值,把这个数字直接写进 Motion 的关键帧里——因为自定义属性会沿 DOM 继承链向下传递,
你可以像使用 upstream/纯 CSS 移植版本一样,把变量设置在 Vegas 容器本身、它的任意祖先节点,
或 :root 上;组件挂载早期 ref 还没有真正指向 DOM 节点时(例如服务端渲染阶段)会回退到
document.documentElement。实际使用时:在你想要影响的那次切换开始之前设置变量;切换动画
播放过程中修改它不会追溯性地改变这次正在播放的动画。
组件方法
方法暴露在 ref 上(VegasHandle,也从包中导出),方法名对齐 upstream:
import { useRef } from "react";
import { Vegas } from "react-vegas";
import type { VegasHandle } from "react-vegas";
const Slideshow = () => {
const vegasRef = useRef<VegasHandle>(null);
return (
<>
<div style={{ height: '80vh' }}>
<Vegas ref={vegasRef} slides={slides} />
</div>
<button onClick={() => vegasRef.current?.toggle()}>播放 / 暂停</button>
<button onClick={() => vegasRef.current?.previous()}>上一张</button>
<button onClick={() => vegasRef.current?.next()}>下一张</button>
</>
);
};VegasHandle 方法:
| 方法 | 返回值 | 说明 |
|-----|-------|-----|
| play() | void | 从暂停恢复:立即切换到下一张,再开始播放(已经在播放时是空操作) |
| pause() | void | 暂停播放 |
| toggle() | void | 在播放/暂停之间切换(跟 play() 一样有立即切换的行为) |
| playing() | boolean | 当前是否正在播放 |
| next() | boolean | 下一张(提交前会先等目标图片加载完成,见"图片加载门控") |
| previous() | boolean | 上一张(跟 next() 同样的图片加载门控) |
| jump(index) | boolean | 跳转到指定索引的幻灯片(同样的图片加载门控) |
| current() | number | 当前幻灯片在 slides 中的索引 |
| current(true) | { slide: number; data: SlideProps } | 当前幻灯片索引及其配置,而不只是索引 |
next()、previous()、jump(index) 返回 true 表示请求被接受——已经提交,或者正在等待
目标图片加载完成(见"图片加载门控");返回 false 表示被拒绝:索引越界(仅 jump)、等于当前
幻灯片、正在切换动画中、已经有另一个切换在等待图片加载,或者(仅 next())在 loop={false}
时到达了终点。返回 true 不代表切换已经真的发生了——要确认发生,请看 onWalk。
暂停时仍可调用 next()、previous() 和 jump()。
在第一张幻灯片出现之前(预加载阶段或显示 defaultBackground 期间)调用 play() 和 pause() 不会丢失:播放意图会被记住,第一张幻灯片一出现就生效。
autoplay={false} 时组件依然会展示首张幻灯片(有预加载和 defaultBackground 时排在它们之后,并且要等它的图片加载完成;如果是视频或者图片已经加载过,就会立即展示——见上面的"图片加载门控"),并保持暂停状态,适合手动控制场景。
play()/toggle() "立即切换再播放"的行为(对齐 upstream 的 play())只发生在这个命令式调用上:
不影响 autoplay 的初始值、之后的 autoplay 属性变化、或标签页重新可见时的恢复——它们
会让当前幻灯片重新获得一整个 delay。如果这次立即切换被拒绝(正在切换动画中,或另一次切换还在
等待图片加载),播放仍然会恢复;如果它到达了终点(loop={false} 时已经在最后一张),会触发 onEnd
并保持暂停(参见上面的 onEnd)。
浏览器标签页隐藏时播放会暂停。标签页重新可见时会自动恢复,除非之前是被 pause() 或 autoplay={false} 暂停的——用户主动的暂停永远不会被覆盖。
暂不支持
- 同时保留多张可见幻灯片:
slidesToKeep
与 vegas.js 的差异
这是一个部分移植版本,在少数地方与 jQuery 原版有意(或不可避免地)不同:
- 没有
slidesToKeep。 - 6 个效果调节用的 CSS 自定义属性从
Vegas容器上读取,不再局限于:root。 upstream (以及像 vue3-vegas 这样的纯 CSS 移植版本)可以在任意祖先节点上解析var(--vegas-zoom-scale)之类的变量,因为浏览器的 CSS 引擎会在使用它的地方原生处理。这个移植版本的blur/zoomIn/zoomOut/swirlLeft/swirlRight过渡由 Motion 的 JS 计算关键帧驱动,没法对一个仍然包含var(...)引用的值做平滑插值,所以改为在每次解析对应过渡时,通过Vegas容器自身的getComputedStyle读取一次这个变量的当前数值——因为自定义属性会沿 DOM 继承链向下传递, 这等效于"容器本身、它的任意祖先节点,或:root",跟 upstream/vue3-vegas 实际效果一致。 完整说明见《调节效果强度》。Ken Burns 本身不受影响(它是原生 CSSanimation,不经过 Motion)。 - Ken Burns 对视频幻灯片同样生效,不只是图片(upstream 的
_goto只给图片的包裹<div>加vegas-animation-{name}类,从不给<video>加)。 shuffle会在每完整播放完一轮之后重新随机排序,不是只在初始化时随机一次。upstream 的shuffle()只在_init()之前调用一次;本移植版本在loop也开启的情况下,每次next()跨越一轮边界时都会抽一个全新的随机顺序,并且新顺序不会让第一张跟刚播放完的那一张相同(不会 紧接着重复)。previous()往回跨越边界不会触发重新排序。这是有意为之的本移植版本特有行为, 不是对齐 upstream。- 本移植版本特有、upstream 没有对应实现的功能:启动时等待图片预加载完成(upstream 的
_preload只创建Image对象,从不等待它们)、带加载进度的 loader(showLoading/loadingText)、 首张幻灯片之前的defaultBackground/defaultBackgroundDuration过渡背景、标签页可见性变化时自动 暂停/恢复、zoomInOut过渡效果,以及delay: 'video'/videoMaxDelay(让视频幻灯片播完再切, 而不是固定停留时间后切走,见视频幻灯片)。 - 没有默认的遮罩图案。 upstream 的
overlay: true会显示一张内置的图案素材 (./overlays/02.png);本移植版本没有打包任何图片资源,所以overlay: true改为显示纯色遮罩 (overlayColor,本移植版本特有)。传字符串 URL 时行为跟 upstream 的overlay: '<url>'完全一致 (重复、居中、透明度 50%)。 - 暂停播放会连带暂停当前视频。 upstream 的
pause()只停止计时器/自动播放,从不 触碰视频的播放状态;本移植版本额外在暂停时调用video.pause()(恢复播放时再play()),这是 有意为之的、本移植版本特有的差异。 - 视频幻灯片的
cover: 'repeat'会退化为cover,而不是字面量。 upstream 把'repeat'原样传给<video>的objectFit样式(vegas.js:394),这不是合法的object-fit值,浏览器 只会忽略它;本移植版本显式改用cover,不照搬这个没有实际效果的行为。 - 未静音视频的音量淡变不会把静音的离场视频强制拉到满音量。 upstream 的离场代码
(
vegas.js:466)会在淡出之前把它找到的每一个离场<video>都设置vid.volume = 1,包括 静音的——除了留下一个偏离默认值的volume之外没有任何实际效果;本移植版本只对真正不静音的 视频改动volume。 - 图片加载失败不会卡住播放。 upstream 的
_goto只设置img.onload = go,从不处理onerror,加载失败会让那次切换永远卡住;本移植版本遇到加载失败仍然会提交这次切换(见上面的 "图片加载门控"),只是记录错误并触发一次性的开发环境警告,而不是卡死播放。
浏览器支持
- Chrome (最新版)
- Firefox (最新版)
- Safari (最新版)
- Edge (最新版)
性能优化建议
组件直接使用 src 指向的文件,不做缩放,所以请事先把图片和视频压缩到实际显示的尺寸。图片可以考虑 WebP 格式。
开启 preload 或 preloadImage 后,所有图片加载完才显示第一张幻灯片,之后的切换会立即提交,不需要再等图片加载(见"图片加载门控")。网速慢时首张会出现得更晚。preloadImageBatch 控制同时加载的图片数量。
已知问题
- 浏览器通常禁止有声视频在用户与页面交互前自动播放。视频幻灯片默认静音,所以只有显式设置了
muted: false的幻灯片会受影响。开启debug后,播放被阻止时控制台会输出警告。 - Safari 不支持所有视频格式。在
video.src中提供多个格式(例如 MP4 和 WebM),浏览器会选第一个能播放的。
致谢
本组件灵感来源于 Jay Salvat 开发的 jQuery 插件 Vegas Background SlideShow,使用 React 和 TypeScript 重新实现。
