css3 移动端video视频全屏,横屏展示,适配微信/打包成app

参考链接:
全屏旋转 https://blog.csdn.net/nidunlove/article/details/51944527
ios进度条滑动方向判断 https://www.cnblogs.com/yiyi17/p/7687273.html

项目基于vue-cli3+vant ui,使用cordova打包成app

css3 移动端video视频全屏,横屏展示,适配微信/打包成app_第1张图片

前言:无可避免的bug

1. 无可避免的在浏览器被X5内核劫持视频(微信浏览器,手机qq浏览器等):
就算自己写了标题栏和控制条,也会变成被劫持的样式,并在webview最顶层显示,就算是dailog/弹出层等都不能在视频上方显示,会被覆盖遮挡,此bug无解,并且时有时没有,有解请留言帮助。
2. 在被劫持后重复点击被劫持后的全屏按钮,会出现横屏不能变成竖屏的情况,小心操作即可,没办法就暴力退出软件,唉。
3. ios 打包app后全屏横屏后,本文使用的手势滑动拉取进度的体验感并不好,可能是我计算的比较粗糙。可以选择其他方式去模拟拉取进度条。

css3 移动端video视频全屏,横屏展示,适配微信/打包成app_第2张图片

一、html结构

包裹视频大盒子为红框video-box,因为自己写了头部标题栏&控制条栏,并且全屏也需要显示,所以里面包含三部分:

1. van-nav-bar 头部标题栏部分
2. video 视频盒子video标签
3. video_control 控制条栏

先附上data代码

data() {
    return {
      title: '', // 视频标题
      isPlay: false, // 是否在播放
      isFullscreen: false, // 是否全屏
      //视频
      initVideo: {
        play: false,//播放还是暂停 true播放中
        videoLength: 3600,//时长
        url: "",//视频课件Url
        currentTime: 0,//当前播放时间
      },
      isShowControl: true, // 是否显示标题栏
      isMove: false, //是否显示视频进度箱
      isIphone: false, //是否ios设备
    }
  },

1.van-nav-bar 头部标题栏部分

isFullscreen 是否全屏状态,全屏/非全屏时显示不同的van-nav-bar高度样式等
这边用v-if=‘isFullscreen’控制,你们也可以使用类名控制

<!-- 非全屏状态van-nav-bar -->
      <van-nav-bar :title="title" left-arrow @click-left="goBack" class="title" v-show='isShowControl' v-if='!isFullscreen'>
        <div slot="right">
        <!-- 收藏/非收藏 -->
          <van-icon name="star-o" v-if='isCollected==0' @click="addCollectCourse" />
          <van-icon name="star" v-else color='#fff' @click="delCollectCourse" />
           <!-- 分享按钮 -->
          <img v-if="isApp" class="btn-share" src="../../assets/img/home-details-share-blank.png" @click="showShareSheet=true" />
        </div>
      </van-nav-bar>
      
<!-- 全屏状态van-nav-bar -->
      <van-nav-bar :title="title" left-arrow @click-left="goBack" class="title-full" v-show='isShowControl' v-if='isFullscreen'>
        <div slot="right">
          <van-icon name="star-o" v-if='isCollected==0' @click="addCollectCourse" />
          <van-icon name="star" v-else color='#fff' @click="delCollectCourse" />
          <img v-if="isApp" class="btn-share" src="../../assets/img/home-details-share-blank.png" @click="fullShare" />
        </div>
      </van-nav-bar>
      

2.video 视频盒子video标签
特别注意:打包成app的时候 需要在打包设置里面设置以下
webview.allowsInlineMediaPlayback=YES,这样才可以实现ios app中内联播放,否则一点击视频就进入ios的全屏层
在这里插入图片描述
代码

  <!-- video API相关获取与调用 -->
      <video class="video" ref="video" webkit-playsinline playsinline x5-playsinline="" :src="initVideo.url" @pause="handPlay(2,2)" @play="handPlay(2,1)" @loadedmetadata="getAudioLength(2)" @timeupdate="videoTimeUpdate" @click="clickVideo" v-if='initVideo.url' poster="../../assets/img/video-default-pictures.png">
        该浏览器不支持video
      </video>

3.video_control 控制条栏
isFullscreen 也是分为了全屏/非全屏状态
另外isFullscreen 全屏状态下,又分isIphone控制是ios的全屏还是安卓的全屏。因为本人解决ios全屏的方法,ios全屏状态下进度条不能使用,所以ios全屏状态下禁用进度条并增加滑动进度箱

<!-- 非全屏状态video_control -->
      <div class="video_control" data-way='0' v-show='isShowControl' v-if='!isFullscreen'>
        <div class="progress">
          <!-- 播放按钮 -->
          <van-icon v-if='!initVideo.play' name="play" color='#fff' class="icon-play" @click.native="playVideo" />
          <!-- 暂停 -->
          <van-icon v-else name="pause" color='#fff' class="icon-play" @click.native="playVideo" />
          <!-- 当前播放时间 -->
          <span class="time-num">{{initVideo.currentTime | videoTime}}</span>
          <!-- 进度条 -->
          <van-slider v-model="initVideo.currentTime" :step='0.01' @change="changeVideoTime" :max='initVideo.videoLength' class="percentage" />
          <!-- videoLength 总时间,currentTime 当前时间,videoTime 自定义过滤器 -->
          <span class="time-num">{{initVideo.videoLength | videoTime}}</span>
		  <!-- 非全屏状态下全屏按钮-->
          <img class="fullscreen" src="../../assets/img/quanpin.png" alt="" @click="full">
        </div>
      </div>

<!-- 全屏状态video_control-full  安卓设备 -->
      <div class="video_control-full" data-way='0' v-show='isShowControl&&!isIphone' v-if='isFullscreen'>
        <div class="progress">
          <van-icon v-if='!initVideo.play' name="play" color='#fff' class="icon-play" @click.native="playVideo" />
          <van-icon v-else name="pause" color='#fff' class="icon-play" @click.native="playVideo" />
          <span class="time-num">{{initVideo.currentTime | videoTime}}</span>
          <van-slider v-model="initVideo.currentTime" :step='0.01' @change="changeVideoTime" :max='initVideo.videoLength' class="percentage" />
          <span class="time-num">{{initVideo.videoLength | videoTime}}</span>
          <img class="fullscreen" src="../../assets/img/quanpin.png" alt="" @click="full">
        </div>
      </div>

<!-- 全屏状态video_control-full  ios设备 -->
      <div class="video_control-full" data-way='0' v-show='isShowControl&&isIphone' v-if='isFullscreen'>
        <div class="progress">
          <van-icon v-if='!initVideo.play' name="play" color='#fff' class="icon-play" @click.native="playVideo" />
          <van-icon v-else name="pause" color='#fff' class="icon-play" @click.native="playVideo" />
          <span class="time-num">{{initVideo.currentTime | videoTime}}</span>
          <!-- 进度条  禁用滑动 -->
          <van-slider v-model="initVideo.currentTime" :step='0.01' @change="changeVideoTime" :max='initVideo.videoLength' :disabled='true' class="percentage" />
          <span class="time-num">{{initVideo.videoLength | videoTime}}</span>
          <img class="fullscreen" src="../../assets/img/quanpin.png" alt="" @click="full">
        </div>
      </div>

<!-- 全屏状态  ios设备增加的视频进度箱forward-box -->
      <div class="forward-box" v-show='isFullscreen&&isIphone' v-if='isMove'>
        <span>{{initVideo.currentTime | videoTime}}/{{initVideo.videoLength|videoTime}}</span>
      </div>

全部html代码

<div class="course-detail">
<!-- 包裹视频的大盒子video-box-->
 <div class="video-box">
 
<!-- 非全屏状态van-nav-bar -->
      <van-nav-bar :title="title" left-arrow @click-left="goBack" class="title" v-show='isShowControl' v-if='!isFullscreen'>
        <div slot="right">
        <!-- 收藏/非收藏 -->
          <van-icon name="star-o" v-if='isCollected==0' @click="addCollectCourse" />
          <van-icon name="star" v-else color='#fff' @click="delCollectCourse" />
           <!-- 分享按钮 -->
          <img v-if="isApp" class="btn-share" src="../../assets/img/home-details-share-blank.png" @click="showShareSheet=true" />
        </div>
      </van-nav-bar>
      
<!-- 全屏状态van-nav-bar -->
      <van-nav-bar :title="title" left-arrow @click-left="goBack" class="title-full" v-show='isShowControl' v-if='isFullscreen'>
        <div slot="right">
          <van-icon name="star-o" v-if='isCollected==0' @click="addCollectCourse" />
          <van-icon name="star" v-else color='#fff' @click="delCollectCourse" />
          <img v-if="isApp" class="btn-share" src="../../assets/img/home-details-share-blank.png" @click="fullShare" />
        </div>
      </van-nav-bar>

      <!-- video API相关获取与调用 -->
      <video class="video" ref="video" webkit-playsinline playsinline x5-playsinline="" :src="initVideo.url" @pause="handPlay(2,2)" @play="handPlay(2,1)" @loadedmetadata="getAudioLength(2)" @timeupdate="videoTimeUpdate" @click="clickVideo" v-if='initVideo.url' poster="../../assets/img/video-default-pictures.png">
        该浏览器不支持video
      </video>

<!-- 非全屏状态video_control -->
      <div class="video_control" data-way='0' v-show='isShowControl' v-if='!isFullscreen'>
        <div class="progress">
          <!-- 播放按钮 -->
          <van-icon v-if='!initVideo.play' name="play" color='#fff' class="icon-play" @click.native="playVideo" />
          <!-- 暂停 -->
          <van-icon v-else name="pause" color='#fff' class="icon-play" @click.native="playVideo" />
          <!-- 当前播放时间 -->
          <span class="time-num">{{initVideo.currentTime | videoTime}}</span>
          <!-- 进度条 -->
          <van-slider v-model="initVideo.currentTime" :step='0.01' @change="changeVideoTime" :max='initVideo.videoLength' class="percentage" />
          <!-- videoLength 总时间,currentTime 当前时间,videoTime 自定义过滤器 -->
          <span class="time-num">{{initVideo.videoLength | videoTime}}</span>
		  <!-- 非全屏状态下全屏按钮-->
          <img class="fullscreen" src="../../assets/img/quanpin.png" alt="" @click="full">
        </div>
      </div>

<!-- 全屏状态video_control-full  安卓设备 -->
      <div class="video_control-full" data-way='0' v-show='isShowControl&&!isIphone' v-if='isFullscreen'>
        <div class="progress">
          <van-icon v-if='!initVideo.play' name="play" color='#fff' class="icon-play" @click.native="playVideo" />
          <van-icon v-else name="pause" color='#fff' class="icon-play" @click.native="playVideo" />
          <span class="time-num">{{initVideo.currentTime | videoTime}}</span>
          <van-slider v-model="initVideo.currentTime" :step='0.01' @change="changeVideoTime" :max='initVideo.videoLength' class="percentage" />
          <span class="time-num">{{initVideo.videoLength | videoTime}}</span>
          <img class="fullscreen" src="../../assets/img/quanpin.png" alt="" @click="full">
        </div>
      </div>

<!-- 全屏状态video_control-full  ios设备 -->
      <div class="video_control-full" data-way='0' v-show='isShowControl&&isIphone' v-if='isFullscreen'>
        <div class="progress">
          <van-icon v-if='!initVideo.play' name="play" color='#fff' class="icon-play" @click.native="playVideo" />
          <van-icon v-else name="pause" color='#fff' class="icon-play" @click.native="playVideo" />
          <span class="time-num">{{initVideo.currentTime | videoTime}}</span>
          <!-- 进度条  禁用滑动 -->
          <van-slider v-model="initVideo.currentTime" :step='0.01' @change="changeVideoTime" :max='initVideo.videoLength' :disabled='true' class="percentage" />
          <span class="time-num">{{initVideo.videoLength | videoTime}}</span>
          <img class="fullscreen" src="../../assets/img/quanpin.png" alt="" @click="full">
        </div>
      </div>

<!-- 全屏状态  ios设备增加的视频进度箱forward-box -->
      <div class="forward-box" v-show='isFullscreen&&isIphone' v-if='isMove'>
        <span>{{initVideo.currentTime | videoTime}}/{{initVideo.videoLength|videoTime}}</span>
      </div>
    </div>
    <van-tabs class="tab-bar"> tab栏 </van-tabs>
 </div>

css样式

.course-detail {
  height: 100vh;
  width: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  
  .video-box {
    width: 100%;
    height: 216px;
    background: #eeeeee;
    position: relative;
    overflow: hidden;
    
    .video {
      width: 100%;
      height: 100%;
      position: absolute;
      // position: relative;
      left: 0;
      top: 0;
    }
    .forward-box {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate3d(-50%, -50%, 0);
      width: 100px;
      height: 50px;
      background-color: rgba(0, 0, 0, 0.3);
      display: flex;
      justify-content: center;
      align-items: center;
      color: #e9e8e8;
    }
    
    img {
      width: 100%;
      height: 100%;
    }
    
    .van-nav-bar {
      background-color: rgba(0, 0, 0, 0.3);
      height: 40px;
      line-height: 40px;
      width: 100%;
      position: absolute;
      z-index: 9999999999999;

      .van-icon {
        color: #fff;
        font-size: 16px;
      }

      .van-nav-bar__title {
        color: #fff;
        font-size: 16px;
      }

      .van-nav-bar__text {
        color: #fff;
        font-size: 14px;
      }

      .van-nav-bar__right {
        .van-icon-cart-o {
          font-size: 20px;
        }

        .van-icon-ellipsis {
          font-size: 20px;
          margin-left: 20px;
          transform: rotateZ(90deg);
        }
      }

      &[class*="van-hairline"]:after {
        border-color: transparent;
      }
    }
  }

  .video_control {
    position: absolute;
    height: 40px;
    // top: 240px;
    left: 0;
    bottom: 0;
    width: 100%;
    z-index: 555 !important;
    
    .progress {
      height: 100%;
      padding: 0 5px;
      background-color: rgba(0, 0, 0, 0.3);
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    
    .percentage {
      margin-left: 5px;
      margin-right: 5px;
      width: 55%;
      // flex: 1;
    }
    
    .time-num {
      color: #fff;
    }
    
    .icon-play {
      width: 25px;
      height: 25px;
      font-size: 25px;
    }
    
    .fullscreen {
      width: 20px;
      height: 20px;
      margin-left: 5px;
    }
  }
  
  .video_control-full {
    position: absolute;
    height: 30px;
    left: 0;
    bottom: 0;
    width: 100%;
    z-index: 9999999999999999 !important;
    transform: translate3d(0%, 0%, 0);

    .progress {
      height: 100%;
      padding: 0 8px;
      background-color: rgba(0, 0, 0, 0.3);
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    
    .time-num {
      font-size: 10px;
      color: #fff;
    }
    
    .percentage {
      margin-left: 2px;
      margin-right: 2px;
      width: 55%;
      // flex: 1;
    }
    
    .icon-play {
      width: 15px;
      height: 15px;
      font-size: 15px;
    }
    
    .fullscreen {
      width: 15px;
      height: 15px;
      margin-left: 5px;
    }
    
    @{deep} .van-slider__button {
      width: 10px;
      height: 10px;
    }
    
    @{deep} .van-slider--disabled {
      opacity: 1 !important;
    }
  }

  .tab-bar {
    flex: 1;
    z-index: 1;
    overflow: hidden;
    display: flex;
    flex-direction: column;
  }
}

二、ios全屏时,设置手势滑动屏幕以跳转进度(模拟滑块Slider进度条)

苹果ios下手势滑动屏幕更改视频的播放进度,本人项目在安卓端使用vant UI里面的Slider进度条。
安卓端横屏全屏后手势依然可以使用,所以直接滑动Slider控制播放进度。
由于ios端方向&手势问题,全屏后设置slider不可滑动(disabled),利用一下手势代码控制视频的currentTime,以达到进度控制,并显示在视频进度箱中。

mounted的时候调用以下代码

// mounted的时候调用以下代码
 const that = this
	
	//mounted判断是否时ios 才进行以下监听滑动的代码
     if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
      console.log('苹果设备');
      this.isIphone = true
	 
	 //视频大盒子
      let fullBox = document.querySelector('.video-box')

      //监听手指接触视频大盒子屏幕事件touchstart touchmove touchend
      
      fullBox.addEventListener("touchstart", function (e) {
        startx = e.touches[0].pageX;
        starty = e.touches[0].pageY;
        curLength = that.initVideo.currentTime
      }, false);
      
       fullBox.addEventListener("touchmove", function (e) {
        var movex, movey
        movex = e.touches[0].pageX;
        movey = e.touches[0].pageY;
        // 全屏时
        if (that.isFullscreen) {
        // isMove 滑动中显示视频进度箱
          that.isMove = true
          if (that.$refs.video) {
          // 滑动时暂停
            that.$refs.video.pause()
          }
          //isTouchMove 作为滑动结束时是否播放视频的标志
          isTouchMove = true
        }
        var direction = getDirection(startx, starty, movex, movey);
        switch (direction) {
          case 0:
            console.log("未滑动!");
            break;
          case 1:
            if (that.isFullscreen) {
              console.log("向上!")
              let changelength = movey - starty
              console.log(changelength, 'changelength');
              that.initVideo.currentTime = curLength + parseInt(changelength)
              if (that.initVideo.currentTime <= 0) {
                that.initVideo.currentTime = 0
              }
              if (that.initVideo.currentTime > that.initVideo.videoLength) {
                that.initVideo.currentTime = that.initVideo.videoLength - 5
              }
            }
            break;
          case 2:
            if (that.isFullscreen) {
              console.log("向下!")
              let changelength = movey - starty
              console.log(changelength, 'changelength');
              that.initVideo.currentTime = curLength + parseInt(changelength)
              if (that.initVideo.currentTime <= 0) {
                that.initVideo.currentTime = 0
              }
              if (that.initVideo.currentTime > that.initVideo.videoLength) {
                that.initVideo.currentTime = that.initVideo.videoLength - 5
              }
            }
            break;
          case 3:
            console.log("向左!")
            break;
          case 4:
            console.log("向右!")
            break;
          default:
        }

      }, false);
     

     //手指离开屏幕
      fullBox.addEventListener("touchend", function (e) {
        var endx, endy;
        endx = e.changedTouches[0].pageX;
        endy = e.changedTouches[0].pageY;
        var direction = getDirection(startx, starty, endx, endy);
        if (that.isFullscreen) {
          that.isMove = false
          if (isTouchMove) {
            if (that.$refs.video) {
              that.$refs.video.play()
            }
            isTouchMove = false
          }

        }
        switch (direction) {
          case 0:
            console.log("未滑动!");
            break;
          case 1:
            if (that.isFullscreen) {
              console.log("向上!")
              if (that.$refs.video) {
                that.$refs.video.currentTime = that.initVideo.currentTime;
              }
            }
            break;
          case 2:
            if (that.isFullscreen) {
              console.log("向下!")
              if (that.$refs.video) {
                that.$refs.video.currentTime = that.initVideo.currentTime;
              }
            }

            break;
          case 3:
            console.log("向左!")
            break;
          case 4:
            console.log("向右!")
            break;
          default:
        }
      }, false);

    
    }

三、使用赋值css实现全屏横屏功能

点击全屏按钮,调用full()方法

 full() {
	
	// 当不是全屏的时候
      if (!this.isFullscreen) {
        if (!this.isPlay) {
          if (this.$refs.video) {
            //这里每次全屏之后将视频播放
            this.$refs.video.play()
          }
        }
        setTimeout(() => {
        //设置进入全屏的各种方法,一般安卓app会进入全屏但不会横屏(横屏方法在下面,要使用cordova插件),ios会进入else
          var ele = document.querySelector('.video-box')
          if (ele.requestFullscreen) {
            ele.requestFullscreen()
          } else if (ele.mozRequestFullScreen) {
            ele.mozRequestFullScreen()
          } else if (ele.webkitRequestFullScreen) {
            //安卓微信
            ele.webkitRequestFullScreen()
          } else {
            console.log('想要进入全屏但进入了else');
            if (this.isIphone) {
              console.log('苹果设备')
              //ios调用horizontalScreen方法, 强制全屏横屏
              horizontalScreen('.video-box')
            }
          }

		 // ios 强制全屏横屏函数horizontalScreen
          function horizontalScreen(className) {
            // transform 强制横屏
            var conW = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
            var conH = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;

            document.querySelector(className).style.transform = "rotate(90deg) translate(" + ((conH - conW) / 2) + "px," + ((conH - conW) / 2) + "px)"
            document.querySelector(className).style.width = conH + "px"
            document.querySelector(className).style.height = conW + "px"
            document.querySelector(className).style.transformOrigin = "center center"
            document.querySelector(className).style.webkitTransformOrigin = "center center"
            document.querySelector(className).style.zIndex = '999999999999'
            document.querySelector(className).style.position = 'absolute'
            
            if (document.querySelector(".video")) {
              document.querySelector(".video").style.zIndex = ''
              document.querySelector(".video").style.position = 'relatived !important'
            }

            var max = conW > conH ? conW : conH;
            var min = conW > conH ? conH : conW;
			
			//重新设置.video-box宽高
            document.querySelector(className).style.width = max + "px";
            document.querySelector(className).style.height = min + "px";
          }
		  
		  //安卓app横屏
          if (!this.isIphone) {
           //cordova-plugin-screen-orientation插件的方法,用于在安卓app全屏时设置变成横屏
            screen.orientation.lock('landscape')
          }

          this.isFullscreen = true
          
          setTimeout(() => {
            if (this.$refs.video) {
              //这里每次全屏横屏之后将视频播放
              this.$refs.video.play()
            }
          }, 0);
        }, 100);

      } else {
        //设置退出全屏的各种方法,一般安卓会退出全屏但不会恢复竖屏,ios会进入else
        if (document.cancelFullScreen) {
          console.log('document.cancelFullScreen()');
          document.cancelFullScreen();
        } else if (document.mozCancelFullScreen) {
          console.log('document.mozCancelFullScreen()');
          document.mozCancelFullScreen();
        } else if (document.webkitCancelFullScreen) {
          console.log('document.webkitCancelFullScreen()');
          //安卓微信
          document.webkitCancelFullScreen();
        } else if (document.webkitExitFullScreen) {
          console.log('document.webkitExitFullScreen()');
          document.webkitExitFullScreen()
        } else {
          console.log('想要退出全屏但进入了else');
          if (this.isIphone) {
            console.log('苹果设备');
            // 设置css恢复为原本的css
            var docHtml = document.documentElement;
            var docBody = document.body;
            var videobox = document.querySelector('.video-box');
            docHtml.style.cssText = "";
            docBody.style.cssText = "";
            videobox.style.cssText = "";
          }
        }

		// 安卓app退出横屏
        if (!this.isIphone) {
         //cordova-plugin-screen-orientation插件的方法,用于在安卓app退出全屏时设置不锁定为横屏,即变为竖屏
          screen.orientation.unlock()
        }

        this.isFullscreen = false

        setTimeout(() => {
          if (this.$refs.video) {
            //这里每次退出全屏之后将视频播放
            this.$refs.video.play()
          }
        }, 500);
      }
    },

你可能感兴趣的:(javascript,vue.js,css3,webview)