taro3+react使用西瓜播放器(xgplayer)封装跨端(H5、微信小程序)视频播放组件及进阶封装(xgplayerSwiper)

安装xgplayer

# 最新稳定版
$ npm install xgplayer

封装H5端视频播放组件:index.h5.js

import './index.scss'
import Player from 'xgplayer'
import 'xgplayer/dist/index.min.css'
import { View, Image, Text } from '@tarojs/components'
import { nextTick } from '@tarojs/taro'
import { forwardRef, useImperativeHandle, useState, useEffect } from 'react'
import ReplayIcon from '@/images/xgplayer/video-replay.png'
import NextIcon from '@/images/xgplayer/video-next.png'

const Index = forwardRef((props, ref) => {
  const { id = 'xgVideo', videoUrl = '', posterUrl = '', autoplay = false, isNextBtn = false, nextClick = '' } = props
  // 西瓜视频实例
  const [xgPlayer, setXgPlayer] = useState(null)
  // 是否播放结束
  const [isEnded, setIsEnded] = useState(false)

  // 实例化播放器
  useEffect(() => {
    if (videoUrl) {
      nextTick(() => {
        let player = new Player({
          id: id, // 选择器 id
          url: videoUrl, // 视频源 url
          poster: posterUrl, // 封面图地址
          autoplay: autoplay, // 是否自动播放,不是所有场景配置了自动播放都可以成功自动起播的
          height: '100%',
          width: '100%',
          // rotate: {   //视频旋转按钮配置项
          //   innerRotate: true, //只旋转内部video
          //   clockwise: false // 旋转方向是否为顺时针
          // },
          rotateFullscreen: true, // 样式横屏全屏
          enableVideoDbltouch: true, // 使移动端支持双击暂停/播放
          videoInit: true, // 是否默认初始化video,当autoplay为true时,该配置为false无效
          playsinline: true, // 是否启用内联播放模式
          'x5-video-player-type': 'h5', // 微信同层播放
          'x5-video-player-fullscreen': false, // 微信全屏播放
          'webkit-playsinline': true,
          'x5-playsinline': true,
          loop: false, // 是否循环播放
          fluid: true, // 是否启用流式布局,启用流式布局时根据width、height计算播放器宽高比,若width和height不是Number类型,默认使用16:9比例// 菜单控件
          ignores: ['volume', 'fullscreen', 'playbackrate', 'pip', 'replay'], // 禁用插件
          lang: 'zh-cn'
        })

        setXgPlayer(player)

        // 监听是否播放结束
        player.on('ended', () => {
          setIsEnded(true)
        })

        // 监听是否播放
        player.on('play', () => {
          setIsEnded(false)
        })
      })
    }
  }, [videoUrl])
  // 遮罩点击
  const coverBoxClick = (e) => {
    e.stopPropagation()
    if (xgPlayer && !isEnded) {
      if (xgPlayer.paused) {
        xgPlayer.play()
      } else {
        xgPlayer.pause()
      }
    }
  }
  // 销毁video实例
  const destroyVideo = () => {
    if (xgPlayer) {
      xgPlayer.destroy() // 销毁播放器
      setXgPlayer(null) // 将实例引用置空
    }
  }
  // 重播按钮点击
  const replayBtn = (e) => {
    e.stopPropagation()
    if (xgPlayer) {
      xgPlayer.replay()
    }
  }
  // 暂停视频方法
  const pauseVideo = () => {
    if (xgPlayer && !isEnded && !xgPlayer.paused) {
      xgPlayer.pause()
    }
  }
  // 下一个点击
  const nextBtn = (e) => {
    e.stopPropagation()

    nextClick && nextClick()
  }

  // 导出方法
  useImperativeHandle(ref, () => ({
    destroyVideo, // 销毁video实例
    pauseVideo // 暂停视频
  }))

  // Html结构部分
  return (
    <View className="xgplayerBox">
      <View id={id}></View>

      {/*遮罩部分*/}
      <View onClick={(e) => coverBoxClick(e)} className="xgplayerBox-coverBox">
        {
          isEnded && <View onClick={(e) => replayBtn(e)} className="btnBox">
            <Image className="btnBox-icon" src={ReplayIcon}/>
            <Text>重播</Text>
          </View> || ''
        }

        {
          isEnded && isNextBtn && <View onClick={(e) => nextBtn(e)} className="btnBox">
            <Image className="btnBox-icon" src={NextIcon}/>
            <Text>下一个</Text>
          </View>
        }
      </View>
    </View>
  )
})

export default Index

封装微信小程序端视频播放组件:index.js

import './index.scss'
import { View, Video, Image, Text } from '@tarojs/components'
import { createVideoContext } from '@tarojs/taro'
import { forwardRef, useImperativeHandle, useState } from 'react'
import ReplayIcon from '@/images/xgplayer/video-replay.png'
import NextIcon from '@/images/xgplayer/video-next.png'

const Index = forwardRef((props, ref) => {
  const { id = 'xgVideo', videoUrl = '', posterUrl = '', autoplay = false, isNextBtn = false, nextClick = '' } = props
  // 视频实例
  let videoContext = createVideoContext(id)
  // 播放状态
  const [paused, setPaused] = useState(true)
  // 是否播放结束
  const [isEnded, setIsEnded] = useState(false)

  // 遮罩点击
  const coverBoxClick = (e) => {
    e.stopPropagation()
    if (paused) {
      videoContext.play()
    } else {
      videoContext.pause()
    }
  }
  // 监听播放
  const onPlay = () => {
    setPaused(false)
    setIsEnded(false)
  }
  // 监听暂停
  const onPause = () => {
    setPaused(true)
  }
  // 监听播放结束
  const onEnded = () => {
    setIsEnded(true)
  }
  // 重播按钮点击
  const replayBtn = (e) => {
    e.stopPropagation()
    videoContext.play()
  }
  // 暂停视频方法
  const pauseVideo = () => {
    if (!isEnded && !paused) {
      videoContext.pause()
    }
  }
  // 下一个点击
  const nextBtn = (e) => {
    e.stopPropagation()

    nextClick && nextClick()
  }

  // 导出方法
  useImperativeHandle(ref, () => ({
    pauseVideo
  }))

  // Html结构部分
  return (
    <View className="xgplayerBox">
      <Video className="xgplayerBox-video" id={id} src={videoUrl} poster={posterUrl} autoplay={autoplay}
             initialTime={0} controls enablePlayGesture loop={false} muted={false} showFullscreenBtn={false}
             onPlay={() => onPlay()} onPause={() => onPause()} onEnded={() => onEnded()}/>

      {/*遮罩部分*/}
      <View onClick={(e) => coverBoxClick(e)} className="xgplayerBox-coverBox">
        {
          isEnded && <View onClick={(e) => replayBtn(e)} className="btnBox">
            <Image className="btnBox-icon" src={ReplayIcon} mode="heightFix"/>
            <Text>重播</Text>
          </View> || ''
        }

        {
          isEnded && isNextBtn && <View onClick={(e) => nextBtn(e)} className="btnBox">
            <Image className="btnBox-icon" src={NextIcon} mode="heightFix"/>
            <Text>下一个</Text>
          </View> || ''
        }
      </View>
    </View>
  )
})

export default Index

双端的样式:index.scss

.xgplayerBox {
  font-size: 12px;
  line-height: 1.2;
  overflow: hidden;
  background: #FFFFFF;
  position: relative;

  // H5中使用
  .xgplayer {
    background: transparent;
  }

  // 小程序中使用
  @at-root &-video {
    width: 100%;
  }

  //遮罩部分
  @at-root &-coverBox {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 50px;
    z-index: 10;
    background: rgba(0, 0, 0, 0);
    display: flex;
    align-items: center;
    justify-content: center;


    .btnBox {
      display: flex;
      align-items: center;
      justify-content: center;
      background: rgba(22, 24, 35, 0.6);
      margin: 50px 5px 0;
      border-radius: 50px;
      padding: 0 10px;
      font-size: 14px;
      color: #FFFFFF;

      @at-root &-icon {
        height: 30px;
        width: auto;
        vertical-align: middle;
      }
    }
  }
}

使用组件xgplayerSwiper:index.js

import './index.scss'
import { View, Swiper, SwiperItem } from '@tarojs/components'
import { pxTransform } from '@tarojs/taro'
import { forwardRef, useRef, useState } from 'react'
import Xgplayer from '@/components/xgplayer/index'

const Index = forwardRef((props, ref) => {
  // 环境标识
  const isH5 = process.env.TARO_ENV === 'h5'

  const { videoUrlList = [], itemStyle = '', swiperHeightH5 = 'auto', swiperHeightMini = 202 } = props
  const refs = []
  videoUrlList.forEach((item, index) => {
    // eslint-disable-next-line react-hooks/rules-of-hooks
    const itemRef = useRef(null) // 创建新的ref对象
    refs[index] = itemRef // 将ref添加到数组中
  })

  // 激活的index
  const [activeIndex, setActiveIndex] = useState(0)

  // swiper改变暂停视频
  const onXgplayerSwiperChange = (e) => {
    refs.forEach((item, index) => {
      item.current.pauseVideo()
    })

    setActiveIndex(e.detail.current)
  }
  // 下一个点击
  const nextClick = () => {
    if (activeIndex === videoUrlList.length - 1) {
      setActiveIndex(0)
    } else {
      setActiveIndex(activeIndex + 1)
    }
  }

  // Html结构部分
  return (
    <View className="xgplayerSwiperBox">
      {
        videoUrlList.length &&
        <Swiper className="swiper" current={activeIndex} indicatorDots={videoUrlList.length > 1}
                style={`height: ${isH5 ? swiperHeightH5 !== 'auto' ? pxTransform(swiperHeightH5) : 'auto' : pxTransform(swiperHeightMini + 26)}`}
                onChange={(e) => onXgplayerSwiperChange(e)}>
          {
            videoUrlList.map((item, index) => {
              return (
                <SwiperItem key={`SwiperItem-${index}`}>
                  <View style={itemStyle}>
                    <Xgplayer id={`video-${index}`} ref={refs[index]} videoUrl={item.videoUrl}
                              posterUrl={item.posterUrl} isNextBtn={videoUrlList.length > 1} nextClick={nextClick}/>
                  </View>
                </SwiperItem>
              )
            })
          }
        </Swiper> || ''
      }
    </View>
  )
})

export default Index

使用组件xgplayerSwiper:index.scss

.xgplayerSwiperBox {
  font-size: 12px;
  line-height: 1.2;
  overflow: hidden;
}

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值