自定义视频轮播

165 阅读1分钟

自定义视频轮播

  • 需求: 第一个视频播放结束,再播放下一个,并且鼠标悬浮在当前描述文案,则当前视频播放

准备一个轮播

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, // pagination支持点击
        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, // pagination支持点击
                    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;