自定义视频轮播
- 需求: 第一个视频播放结束,再播放下一个,并且鼠标悬浮在当前描述文案,则当前视频播放
准备一个轮播
import SwiperCore, {
Navigation,
Pagination,
Scrollbar,
A11y,
Autoplay,
EffectCoverflow,
EffectCube,
EffectFade,
} from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/react';
import 'swiper/swiper-bundle.css';
import styles from './index.module.scss';
const [current, setCurrent] = useState<number>(0);
const swiperRef = useRef<SwiperCore | null>(null);
const container = useRef<HTMLDivElement | null>(null);
const Props: any = {
observeParents: true,
observer: true,
watchOverflow: true,
effect: 'fade',
onInit: (swiper: any) => {
swiperRef.current = swiper;
},
onSlideChange: (e: any) => {
setCurrent(e.realIndex);
},
};
SwiperCore.use([
Navigation,
Pagination,
Scrollbar,
A11y,
Autoplay,
EffectCoverflow,
EffectCube,
EffectFade,
]);
{
swiperList && swiperList.length > 0 && (
<Swiper {...Props}>
</div>
</Swiper>
);
}
准备视频描述文案相关
...
swiperList.map((item: SwiperItem, index: number) => (
<div
key={index}
className={`${styles['desc-content']} ${
current === index ? `${styles['current-video']}` : null
}`}
onMouseOver={(e) =>
triggerMode === 'hover' && mouseOverCurrentVideo(e, index)
}
>
<div className={styles['desc-content-items']}>
<p>{item.title}</p>
<p>{item.subTitle}</p>
<div className={`${styles['desc-content-items-line']}`} />
</div>
</div>
));
const mouseOverCurrentVideo = (e: any, index: number) => {
e.stopPropagation();
swiperRef.current?.slideTo(index);
setCurrent(index);
};
准备视频位置
{
swiperList.map((item: SwiperItem, index: number) => (
<SwiperSlide key={index} className={styles['swiper-video']}>
<BannerVideo
src={item.video}
play={current === index}
onEnded={handleEnded}
poster={item.poster}
width={width}
height={height}
fit={fit}
/>
</SwiperSlide>
));
}
const handleEnded = () => {
if (current < swiperList.length - 1) {
setCurrent(current + 1);
swiperRef.current?.slideNext();
} else {
swiperRef.current?.slideTo(0);
}
};
const BannerVideo: FC<BannerItem> = (props) => {
const {
play,
poster,
src,
onEnded,
width = '100%',
height = 'auto',
fit = 'cover',
} = props;
const ref = useRef < HTMLVideoElement > null;
const [showVideo, setShowVideo] = useState(false);
const togglePlay = (startPlay: boolean) => {
ref.current && toggleVideoPlay(ref.current, startPlay);
};
useEffect(() => {
ref.current && (ref.current.currentTime = 0);
if (play && showVideo) {
setTimeout(() => {
togglePlay(true);
}, 200);
} else if (!play) {
togglePlay(false);
}
}, [play, showVideo]);
useEffect(() => {
if (ref.current) {
ref.current.src = src;
videoAction(() => {
ref.current?.load();
});
ref.current.onloadeddata = () => {
setTimeout(() => {
setShowVideo(true);
}, 200);
};
}
}, [ref.current]);
return (
<div className={styles['banner-section-item']}>
<video
ref={ref}
autoPlay={false}
controls={false}
muted
playsInline
width={width || '100%'}
height={height || 'auto'}
preload="auto"
poster={poster}
onEnded={onEnded}
src={src}
className={styles[`${fit}`]}
/>
</div>
);
};
另外新增了支持分页(圆点)展示
switch (pageType) {
case 'round':
Object.assign(Props, {
pagination: {
clickable: true,
bulletActiveClass: 'pagination-item-current',
bulletClass: 'pagination-item swiper-pagination-bullet',
},
});
break;
default:
break;
}
支持分页(圆点)时,手动滑动滑不到第一个
const Props: any = {
...,
onTouchMove: (swiper: any, event: any) => {
setTimeout(() => {
if (swiper.swipeDirection === 'prev' && current === 0) {
swiperRef.current?.slideTo(swiperList.length - 1);
}
}, 100);
setTimeout(() => {
if (
swiper.swipeDirection === 'next' &&
current === swiperList.length - 1
) {
swiperRef.current?.slideTo(0);
}
}, 100);
},
};
完整组件
import { FC, useEffect, useRef, useState } from 'react';
import SwiperCore, {
Navigation,
Pagination,
Scrollbar,
A11y,
Autoplay,
EffectCoverflow,
EffectCube,
EffectFade,
} from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/react';
import 'swiper/swiper-bundle.css';
import styles from './index.module.scss';
const BannerVideo: FC<BannerItem> = (props) => {
const { play, poster, src, onEnded, width = '100%', height = 'auto', fit = 'cover' } = props;
const ref = useRef<HTMLVideoElement>(null);
const [showVideo, setShowVideo] = useState(false);
const togglePlay = (startPlay: boolean) => {
ref.current && toggleVideoPlay(ref.current, startPlay);
};
useEffect(() => {
ref.current && (ref.current.currentTime = 0);
if (play && showVideo) {
setTimeout(() => {
togglePlay(true);
}, 200);
} else if (!play) {
togglePlay(false);
}
}, [play, showVideo]);
useEffect(() => {
if (ref.current) {
ref.current.src = src;
videoAction(() => {
ref.current?.load();
});
ref.current.onloadeddata = () => {
setTimeout(() => {
setShowVideo(true);
}, 200);
};
}
}, [ref.current]);
return (
<div className={styles['banner-section-item']}>
<video
ref={ref}
autoPlay={false}
controls={false}
muted
playsInline
width={width || '100%'}
height={height || 'auto'}
preload="auto"
poster={poster}
onEnded={onEnded}
src={src}
className={styles[`${fit}`]}
/>
</div>
);
};
const SwiperVideo = (props: DataType<PropsType>) => {
const { data } = props;
const {
swiperList,
theme = 'darkness',
width = '100%',
height = 'auto',
margin = '0 auto',
padding = '0',
fit = 'cover',
pageType = 'card',
triggerMode = 'default',
} = data;
const [current, setCurrent] = useState<number>(0);
const swiperRef = useRef<SwiperCore | null>(null);
const Props: any = {
observeParents: true,
observer: true,
watchOverflow: true,
effect: 'fade',
onInit: (swiper: any) => {
swiperRef.current = swiper;
},
onSlideChange: (e: any) => {
setCurrent(e.realIndex);
},
onTouchMove: (swiper: any, event: any) => {
setTimeout(() => {
if (swiper.swipeDirection === 'prev' && current === 0) {
swiperRef.current?.slideTo(swiperList.length - 1);
}
}, 100);
setTimeout(() => {
if (swiper.swipeDirection === 'next' && current === swiperList.length - 1) {
swiperRef.current?.slideTo(0);
}
}, 100);
},
};
switch (pageType) {
case 'round':
Object.assign(Props, {
pagination: {
clickable: true,
bulletActiveClass: 'pagination-item-current',
bulletClass: 'pagination-item swiper-pagination-bullet',
},
});
break;
default:
break;
}
SwiperCore.use([
Navigation,
Pagination,
Scrollbar,
A11y,
Autoplay,
EffectCoverflow,
EffectCube,
EffectFade,
]);
const isLoop = !!(swiperList && swiperList.length > 1);
const handleEnded = () => {
if (current < swiperList.length - 1) {
setCurrent(current + 1);
swiperRef.current?.slideNext();
} else {
swiperRef.current?.slideTo(0);
}
};
const mouseOverCurrentVideo = (e: any, index: number) => {
e.stopPropagation();
swiperRef.current?.slideTo(index);
setCurrent(index);
};
return (
<div
style={{
margin,
padding,
}}
>
{swiperList && swiperList.length > 0 && (
<Swiper {...Props}>
<div className={`${styles['swiper-video-content']} ${styles[theme] || null}`}>
{pageType === 'card' &&
swiperList.map((item: SwiperItem, index: number) => (
<div
key={index}
className={`${styles['desc-content']} ${
current === index ? `${styles['current-video']}` : null
}`}
onMouseOver={(e) =>
triggerMode === 'hover' && mouseOverCurrentVideo(e, index)
}
>
<div className={styles['desc-content-items']}>
<p>{item.title}</p>
<p>{item.subTitle}</p>
<div className={`${styles['desc-content-items-line']}`} />
</div>
</div>
))}
</div>
{swiperList.map((item: SwiperItem, index: number) => (
<SwiperSlide key={index} className={styles['swiper-video']}>
<BannerVideo
src={item.video}
play={current === index}
onEnded={handleEnded}
poster={item.poster}
width={width}
height={height}
fit={fit}
/>
</SwiperSlide>
))}
</Swiper>
)}
</div>
);
};
export default SwiperVideo;