利用typescript和webpack封装播放器

1 篇文章 0 订阅
1 篇文章 0 订阅

我们公司主要做的产品就是面向各种媒体,做播放器需求的时候总是调用公司底层封装好的功能,今天也来讲讲如何封装一个属于自己的简单的播放器(需要webpack配置文件私信给我)

播放器分为两个部分,弹框组件和播放器组件,播放器的控件不再使用浏览器自带的控件,用我们自定义的控件

整体结构:一个弹层,弹层中包裹video标签

弹层需要的属性:

弹层是否有遮罩

弹层的高,弹层的宽,弹层标题

需要一个回调函数,用于存放video节点挂载

弹层在页面中的位置(居左,居右,居中)

弹层核心代码:

// import './popup.css'  全局css操作,导致污染,ts的操作模式,ts文件不识别css后缀的名称,那么就无法使用
// 用模块化的方式引入,webpack的方式到入的,和ts无关(方式一)
// let styles = require('./popup.css').default //(方式二,ts的声明文件)
//如果使用import方式引入,需要声明文件,不需要对每个类名都声明,使用ts任意属性配置即可
import styles from './popup.css'
interface Ipopup {
  width?:string;
  height?:string;
  title?:string;
  pos?:string;
  mask?:boolean;
  content?: (ele:HTMLElement) => void;
}
// 用来约束类
interface Icomponent {
  tempContainer: HTMLElement
  init: () => void;//组件必须有初始化函数
  template:() => void;
  handle:() => void
}
// 属性都是可选,都是默认值
function popup (options:Ipopup){
  return new Popup(options)
}
class Popup implements Icomponent{
  tempContainer;
  mask;
  constructor ( private settings: Ipopup) {
    // 让形参变成类型的属性,加上修饰符就可以直接调用settings
    // 前面是默认值,用户不传则使用默认值
    this.settings = Object.assign({
      width:'100%',
      height:'100%',
      title:'',
      pos:'center',
      mask:true,
      content:function(){}
    },this.settings)
    this.init()
  }
  // 初始化
  init(){
    this.template()
    this.settings.mask && this.createMask()
    this.handle()
    this.contentCallback()
  }
  // 创建模板
  template(){
    this.tempContainer = document.createElement('div')
    this.tempContainer.style.width = this.settings.width
    this.tempContainer.style.height = this.settings.height
    this.tempContainer.className = styles.popup
    this.tempContainer.innerHTML = `
      <div class="${styles['popup-title']}">
        <h3>${this.settings.title}</h3>
        <i class="iconfont icon-close"></i>
      </div>
      <div class="${styles['popup-content']}"></div>
    `
    document.body.appendChild(this.tempContainer)
    switch(this.settings.pos){
      case 'left':
        this.tempContainer.style.left = 0
        this.tempContainer.style.top = (window.innerHeight - this.tempContainer.offsetHeight) + 'px';
        break;
      case 'right':
        this.tempContainer.style.right = 0
        this.tempContainer.style.top = (window.innerHeight - this.tempContainer.offsetHeight) + 'px';
        break;
      default:
        // 不传或传错,默认就是居中
        this.tempContainer.style.left = (window.innerWidth - this.tempContainer.offsetWidth) / 2 +'px'
        this.tempContainer.style.top = (window.innerHeight - this.tempContainer.offsetHeight) / 2 + 'px'
        break;
    }
  }
  // 事件操作
  handle(){
    let popupClose = this.tempContainer.querySelector(`.${styles['popup-title']} .icon-close`)
    popupClose.addEventListener('click',()=>{
      document.body.removeChild(this.tempContainer)
      this.settings.mask && document.body.removeChild(this.mask)
    })
  }
  // 创建碳=弹层
  createMask () {
    this.mask = document.createElement('div')
    this.mask.className = styles.mask
    document.body.appendChild(this.mask)
  }
  contentCallback(){
    let popupContent = this.tempContainer.querySelector(`.${styles['popup-content']}`)
    this.settings.content(popupContent)
  }
}
export default popup

css样式文件引入的两种方式:require方式引入和使用impor 方式引入,使用import方式引入需要使用ts的声明文件,声明文件如下:

// // css的声明文件,有多少个类名就要声明多少个
// declare const styles : {
//   readonly "popup" : string;
//   readonly "popupTitle" : string;
// }
// export default styles
// 每个都要指定,很麻烦
declare const styles : {

  [key: string]: string

}

export default styles;

播放器的功能包括: 暂停/播放   拖拽播放,静音,播放速率更改, 全屏, 时间渲染等

播放器核心代码

let styles = require('./video.css').default
// 定义播放器接口
interface Ivideo {
  url:string;
  elem:string | HTMLElement;
  width?:string;
  height?:string;
  autoplay?:boolean;
  muted?:boolean;//是否静音播放
}
// 约束类
// 用来约束类
interface Icomponent {
  tempContainer: HTMLElement
  init: () => void;//组件必须有初始化函数
  template:() => void;
  handle:() => void
}
function video (options:Ivideo) {
  return new Video(options)
}
class Video implements Icomponent {
  tempContainer;
  constructor (private settings: Ivideo) {
    this.settings = Object.assign({
      width:'100%',
      height:'100%',
      autoplay:false
    },this.settings)
    this.init()
  }
  init(){
    this.template()
    this.handle()
  }
  template(){
    this.tempContainer = document.createElement('div')
    this.tempContainer.className = styles['videocontainer']
    this.tempContainer.style.width = this.settings.width
    this.tempContainer.style.height = this.settings.height
    this.tempContainer.innerHTML = `
      <video class="${styles['video-content']}"
      src="${this.settings.url}"></video>
      <div class="${styles['video-controls']}">
        <div class="${styles['video-progress']}">
          <div class="${styles['video-progress-now']}"></div>
          <div class="${styles['video-progress-suc']}"></div>
          <div class="${styles['video-progress-bar']}"></div>
        </div>
        <div class="${styles['video-play']}">
          <i class="iconfont icon-play"></i>
        </div>
        <div class="${styles['video-time']}">
          <span>00:00</span> / <span>00:00</span>
        </div>
        <div style="float:right">
          <div class="${styles['video-playbackRate']}">
            <div class="${styles['video-playbackRate-btn']}">
              倍速
              <ul class="${styles['video-playbackRate-list']}">
                <li>2X</li>
                <li>1.5X</li>
                <li>1.25X</li>
                <li class="${styles['selected']}">1X</li>
                <li>0.75X</li>
                <li>0.5X</li>
              </ul>
            </div>
          </div>
          <div class="${styles['video-volume']}">
            <i class="iconfont icon-shengyin_shiti"></i>
            <div class="${styles['video-volprogress']}">
              <div class="${styles['video-volprogress-now']}"></div>
              <div class="${styles['video-volprogress-bar']}"></div>
            </div>
          </div>
          <div class="${styles['video-full']}">
            <i class="iconfont icon-quanping_o"></i>
          </div>
        </div>
      </div>
    `
    // 可以直接在页面上给定元素,如果没有给定,使用内部创建的dom结构
    if(typeof this.settings.elem == 'object') {
      this.settings.elem.appendChild(this.tempContainer)
    } else  {
      document.querySelector(`${this.settings.elem}`).appendChild(this.tempContainer)
    }
    
  }
  handle(){
    // 获取video事件
    let videoContent:HTMLVideoElement = this.tempContainer.querySelector(`.${styles['video-content']}`)
    // 获取工具栏按钮dom结构(类名获取,获取的是第一个i,后面的i不会被获取到))
    let videoControls = this.tempContainer.querySelector(`.${styles['video-controls']}`)
    let videoPlay = this.tempContainer.querySelector(`.${styles['video-controls']} i`)
    let videoTimes = this.tempContainer.querySelectorAll(`.${styles['video-time']} span`)
    let videoFull = this.tempContainer.querySelector(`.${styles['video-full']} i`)
    let videoProgress = this.tempContainer.querySelectorAll(`.${styles['video-progress']} div`)
    console.log(videoProgress)
    let videoVolProgress = this.tempContainer.querySelectorAll(`.${styles['video-volprogress']} div`)
    let videoplaybackRateUl = this.tempContainer.querySelector(`.${styles['video-playbackRate-list']}`)
    // 获取控制播放速率的元素
    let videoplaybackRateLi = this.tempContainer.querySelectorAll(`.${styles['video-playbackRate-list']} li`)
    // 获取倍速按钮,控制倍速选项出现
    let videoPlayBackRate = this.tempContainer.querySelector(`.${styles['video-playbackRate']}`)
    // 设置静音
    let videoMuted = this.tempContainer.querySelector(`.${styles['video-volume']} i`)
    // 指定默认音量
    videoContent.volume = 0.5
    // 制定默认播放速率
    videoContent.playbackRate = 1
    // 当前播放事件实时变化
    let timer;
    // 判断视频是否加载完毕
    console.log(videoPlay)
    videoContent.addEventListener('canplay',()=>{
      console.log('canplay,视频是否加载完毕')
      // 等待文佳加载完完毕之后渲染总时长
      videoTimes[1].innerHTML= formatTime(videoContent.duration)
    })
    // 是否静音播放
    if(this.settings.muted){
      videoContent.muted = true
      videoMuted.className ="iconfont icon-jingyin"
    }
    // 是否自动播放
    if(this.settings.autoplay){
      // 说明用户希望自动播放,那么进度条要动起来,
      videoContent.play()
    }
    videoPlayBackRate.addEventListener('mouseover',function(e:MouseEvent){
      videoplaybackRateUl.style.display = 'block'
    })
    videoPlayBackRate.addEventListener('mouseleave',function(e:MouseEvent){
      // setTimeout(() => {
        videoplaybackRateUl.style.display = 'none'
      // }, 2000);
    })
    // videoplaybackRateUl.addEventListener('mousemove',function(e:MouseEvent){
    //   videoplaybackRateUl.style.display = 'block'
    // })
    // 设置静音
    videoMuted.addEventListener('click',function(e:MouseEvent){
      videoContent.muted = !videoContent.muted
      if(videoContent.muted){//此时为静音状态
        videoMuted.className ="iconfont icon-jingyin"
      } else {
        videoMuted.className ="iconfont icon-shengyin_shiti"
      } 
    })
    // 给每个速率绑定事件
    for (let i = 0; i < videoplaybackRateLi.length; i++) {
      videoplaybackRateLi[i].addEventListener('click',function(e:MouseEvent){
        let playbackRate = videoplaybackRateLi[i].innerHTML
        // 每次点击之后,给当前元素设置独有的选中样式
        for (let j = 0; j < videoplaybackRateLi.length; j++) {
          videoplaybackRateLi[j].classList.remove(`${styles['selected']}`)
        }
        videoplaybackRateLi[i].classList.add(`${styles['selected']}`)
        playbackRate = playbackRate.slice(0,playbackRate.length-1)
        console.log(Number(playbackRate))
        videoContent.playbackRate = Number(playbackRate)
      })
    }
    // 控制控件区域消失与隐藏
    this.tempContainer.addEventListener('mouseenter',function(e:MouseEvent){
      videoControls.style.bottom = 0
      // 不是一直出现,当过5秒后,控件自动小时
      // let timer = setTimeout(() => {
      //   videoControls.style.bottom = -60+'px'
      //   // this.mouseleave()
      //   clearTimeout(timer)
      // }, 3000);
    })
    // this.tempContainer.addEventListener('mouseleave',function(e:MouseEvent){
    //   videoControls.style.bottom = -60+'px'
    // })
    // 当控件消失,用户再次操作鼠标时,控件出现
    // this.tempContainer.addEventListener('mousemove',function(e:MouseEvent){
    //   videoControls.style.bottom = 0
    //   let timer = setTimeout(() => {
    //     videoControls.style.bottom = -60+'px'
    //     // this.mouseleave()
    //     clearTimeout(timer)
    //   }, 3000);
    // })
    // 视频播放事件
    videoContent.addEventListener('play',()=>{
      videoPlay.className = "iconfont icon-zanting"
      timer = setInterval(playing,100)
    })
    videoContent.addEventListener('pause',()=>{
      videoPlay.className = "iconfont icon-play"
      clearInterval(timer)
    })
    // 视频的播放与暂定
    videoPlay.addEventListener('click',()=>{
      if(videoContent.paused){
        videoContent.play()
      } else {
        videoContent.pause()
      }
    })
    // 全屏
    videoFull.addEventListener('click',()=>{
      videoContent.requestFullscreen()
    })
    // 进度条拖拽事件
    videoProgress[2].addEventListener('mousedown',function(ev:MouseEvent){
      let downX = ev.pageX;
      let downL = this.offsetLeft;
      console.log(downX,downL,'小球的位置')
      document.onmousemove = (e:MouseEvent)=>{
        let scale = (e.pageX - downX + downL+8) / this.parentNode.offsetWidth;
        if(scale<0){
          scale = 0;
        } else if (scale > 1){
          scale = 1
        }
        videoProgress[0].style.width = scale * 100 +'%'
        // 缓存的进度条不用拖拽
        this.style.left = scale * 100 + '%'
        videoContent.currentTime = scale * videoContent.duration
      }
      // 鼠标弹起的时候解绑事件
      document.onmouseup = () =>{
        document.onmousemove = document.onmouseup = null
      }
      // 阻止默认事件,防止小bug
      ev.preventDefault()
    })
    // 控制视频音量
    videoVolProgress[1].addEventListener('mousedown',function(ev:MouseEvent){
      let downX = ev.pageX;
      let downL = this.offsetLeft;
      document.onmousemove = (e:MouseEvent)=>{
        let scale = (e.pageX - downX + downL+8) / this.parentNode.offsetWidth;
        if(scale<0){
          scale = 0;
        } else if (scale > 1){
          scale = 1
        }
        videoVolProgress[0].style.width = scale * 100 +'%'
        // 缓存的进度条不用拖拽
        this.style.left = scale * 100 + '%'
        videoContent.volume = scale
      }
      // 鼠标弹起的时候解绑事件
      document.onmouseup = () =>{
        document.onmousemove = document.onmouseup = null
      }
      // 阻止默认事件,防止小bug
      ev.preventDefault()
    })
    // 改变视频播放速率的时候会触发
    videoContent.addEventListener('ratechange',function(e:MouseEvent){
      console.log('是否修改了视频播放速率')
    })
    // 播放器进度(正在播放中)
    function playing(){
      let scale = videoContent.currentTime / videoContent.duration
      // 缓存时间
      let scaleSuc = videoContent.buffered.end(0) / videoContent.duration
      videoTimes[0].innerHTML = formatTime(videoContent.currentTime)
      // 设置当前播放进度
      videoProgress[0].style.width = scale * 100 + '%'
      // 设置缓存播放条
      videoProgress[1].style.width = scaleSuc *100 +'%'
      // 设置小球的位置
      videoProgress[2].style.left = scale * 100 +'%'
    }
    function formatTime(number:number):string {
      console.log(number,'当前是多少秒')
      number = Math.round(number)
      let max = Math.floor(number/60/60)
      let min = Math.floor((number-max*3600)/60)
      let sec = Math.floor((number-max*3600)%60)
      return setZero(max)+":"+setZero(min)+":"+setZero(sec)
    }
    // 补0操作
    function setZero(number:number):string {
      if(number<10){
        return '0'+number
      } else {
        return ''+number
      }
    }
  }
}
export default video;

最后看下效果:

 静音下自动播放

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
什么是TypeScriptTypeScript是JavaScript的加强版,它给JavaScript添加了可选的静态类型和基于类的面向对象编程,它拓展了JavaScript的语法。不过,你不必担心TypeScript跟浏览器不兼容,因为在编译时,它产生的都是JavaScript代码。为什么要学TypeScritpt?TypeScript是一门很有前景和钱景的语言;它能大幅的提高工作效率,并且减少错误;这堂课我们能学到什么?随着前端行业越来越受到重视,前端的逻辑也越来越复杂,对前端从业者的职业要求也越来越高,Vue、Angular、React和一些新的框架层出不穷,而作为Angular等框架的推荐语言TypeScript也在市场中得到了认可和追捧,不管是前端、还是游戏引擎、以及一些大型项目开发都中开始展露头角。但是这方面学习资源相对较少。课程特点:本堂课程通过深入浅出的讲解,幽默风趣的风格; 让大家在3个小时的课程中能够掌握大部分TypeScript的核心知识; 同时能够使用TypeScript进行React等框架的项目开发; 为大家的学习和在工作中使用TypeScript打下坚实的基础。课程大纲:1.TS的初步配置2.TS 数据类型 any 枚举3.函数的参数和返回值类型4.类非常重要 非常重要5.静态类属性和方法 Math6.泛型7.模块化 systemjs8.项目TS+react+webpack结合的工作流应类型管理 js->tsx 2.0如何用TS开发react->TSX(难点->官网 项目)工作流 package.json -> npm start npm run build 适用人群:1.Typescript零基础想掌握ts正确学习姿势和入门的初学者2.经验丰富的jser想拓宽自己知识掌握ES6和ES7新功能的从业者3.想在工作和项目中使用ts结合VAR框架的人员4.有志于成为全栈开发人员却苦于无法入门后端和跟后端沟通不畅的开发者5.想深入了解和使用angular的人员

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值