h5使用video标签简单实现播放视频

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,内容安全 1000 次 1年
对象存储OSS,敏感数据保护2.0 200GB 1年
简介: h5使用video标签简单实现播放视频

h5使用video标签简单实现播放视频


近期做了一个需求,h5 页面,展示视频列表,点击视频,全屏播放。开始还以为全屏是不是需要读写 app 的标题栏,事实证明多想了,video 标签本身全屏的时候,可以让视频之外的地方全黑,ios 和 android 都一样。

但 ios 播放视频的时候,会自动将 video 放在最高层,然后全屏播放。

为了统一效果,播放视频的时候,统一用一个黑色遮罩层。

video,其实本身还是挺多事的,本文只是简单的播放,以后再涉及 video 的时候,心里有个数。video 的所有属性和事件可以参考MDN 的介绍

如果是 PC 端的,推荐DPlayer。移动端,效果可能没那么好。

播放效果和简单逻辑

网络异常,图片无法展示
|

可能全屏效果比很多插件要好些,不然样式问题,也是闹心。

实现其实没啥大逻辑。

播放组件关键逻辑就是,播放的时候,先检测是否能播放,若不能则显示加载中。

播放组件的逻辑:

  • 判断是否能播放,不能播放显示加载中
  • 播放中出错的话,提示
  • 播放结束,通知父组件

网络异常,图片无法展示
|

列表的逻辑:

  • 展示列表
  • 传递必要属性
  • 接受子组件的事件

网络异常,图片无法展示
|

列表项组件项组件的逻辑:

  • 属于展示组件,额外增加一个图片失败的监测

网络异常,图片无法展示
|

附注代码

videoPlayer 的代码

<template>
  <div class="video-box" v-if="isShowPlayer">
    <video
      controls
      :src="info.url"
      :poster="info.cover"
      ref="video"
      @canplay="canPlay"
      @error="error"
      @ended="ended"
      controlslist="nodownload"
      disablePictureInPicture
    ></video>
    <div class="close-box" @click="clickClose">
      <img
        src="https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/close.png"
        alt="关闭"
      />
    </div>
    <div class="loadingBox" v-if="isLoading">
      <img
        class="loading-icon"
        src="https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/loading.gif"
        alt="正在加载..."
      />
    </div>
  </div>
</template>
<script>
export default {
  props: {
    info: { required: true, type: Object }, // url cover属性
    isShowPlayer: { default: false }
  },
  data() {
    return {
      isCanPlay: false,
      isLoading: false
    };
  },
  mounted() {
    if (this.isCanPlay) {
      this.$refs.video.play();
      return;
    }
    this.isLoading = true;
  },
  methods: {
    canPlay() {
      this.isCanPlay = true;
      this.isLoading = false;
      this.$refs.video.play();
    },
    error() {
      alert("视频出错了,请联系客服人员");
      this.showLoading = false;
    },
    ended() {
      this.$emit("ended");
    },
    clickClose() {
      this.$emit("update:isShowPlayer", false);
    }
  }
};
</script>
<style scoped>
.video-box {
  position: fixed;
  z-index: 9999;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #000;
}
video {
  width: 100%;
}
.close-box {
  position: fixed;
  z-index: 10000;
  top: 0px;
  right: 0;
  padding: 20px;
}
.close-box img {
  width: 24px;
}
.loadingBox {
  position: fixed;
  z-index: 9999;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: transparent;
}
.loading-icon {
  width: 50px;
  padding: 10px;
  border-radius: 10px;
  background-color: rgba(0, 0, 0);
}
</style>

列表 的代码

<template>
  <div class="paper">
    <div class="list">
      <play-item
        :info="item"
        class="item"
        @clickItem="clickItem(index)"
        v-for="(item, index) in playList"
        :key="index"
      ></play-item>
    </div>
    <videoPlayer
      v-if="isShowPlayer"
      :isShowPlayer.sync="isShowPlayer"
      :info="curVideo"
      @ended="ended"
    ></videoPlayer>
    <div class="loadingBox" v-if="isLoading">
      <img
        class="loading-icon"
        src="https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/loading.gif"
        alt="正在加载..."
      />
    </div>
  </div>
</template>
<script>
import PlayItem from "./components/PlayItem";
import VideoPlayer from "./components/VideoPlayer";
export default {
  components: { PlayItem, VideoPlayer },
  data() {
    return {
      // 播放列表
      playList: [],
      // 是否正在加载
      isLoading: true,
      // 是否显示播放器
      isShowPlayer: false,
      // 当前播放视频的索引
      curVideoIndex: null
    };
  },
  computed: {
    curVideo() {
      return this.playList[this.curVideoIndex];
    }
  },
  mounted() {
    this.getPlayList();
  },
  methods: {
    getPlayList() {
      setTimeout(() => {
        this.isLoading = false;
        const list = [
          {
            title: "黑色毛衣",
            url:
              "https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/video.mp4"
          },
          {
            title: "发如雪",
            url:
              "https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/video2.mp4"
          },
          {
            title: "千里之外",
            url:
              "https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/video.mp4"
          }
        ];
        list.forEach(
          item =>
            (item.cover =
              "https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/cover.png")
        );
        this.playList = list;
      }, 20);
    },
    clickItem(index) {
      this.curVideoIndex = index;
      this.isShowPlayer = true;
    },
    ended() {
      const isLast = this.curVideoIndex === this.playList.length - 1;
      if (isLast) {
        alert("这是最后一个视频了~~");
        return;
      }
      this.curVideoIndex++;
    }
  }
};
</script>
<style scoped>
.list {
  padding: 16px;
}
.item {
  margin-bottom: 16px;
}
.loadingBox {
  position: fixed;
  z-index: 9999;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: transparent;
}
.loading-icon {
  width: 50px;
  padding: 10px;
  border-radius: 10px;
  background-color: rgba(0, 0, 0);
}
</style>

列表项组件 的代码

<template>
  <div class="play-item-box" @click="clickItem">
    <div class="left">
      <img class="resource" :src="info.cover" @error="errorImg" alt="" />
      <img
        class="play-icon"
        src="https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/play_icon.png"
        alt=""
      />
    </div>
    <div class="right">
      <div class="title">
        {{ info.title }}
      </div>
      <div class="time">发送时间:2021-10-21 10:10:10</div>
    </div>
  </div>
</template>
<script>
export default {
  name: "playItem",
  props: { info: Object },
  data() {
    return {
      defaultCover:
        "https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/cover.png"
    };
  },
  methods: {
    clickItem() {
      this.$emit("clickItem");
    },
    errorImg() {
      this.info.coverImage = this.defaultCover;
    }
  }
};
</script>
<style scoped>
.play-item-box {
  display: flex;
  border-radius: 6px;
}
.left {
  width: 33%;
  position: relative;
  height: 70px;
}
.left .resource {
  display: block;
  width: 100%;
  border-radius: 6px;
  height: 100%;
}
.play-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1;
  width: 33px;
}
.right {
  flex: 1;
  margin-left: 10px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 2px 0;
}
.title {
  font-size: 16px;
  font-weight: bold;
  color: #333;
  line-height: 1.3;
}
.time {
  font-size: 12px;
  color: #999;
}
.video {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  opacity: 0;
  left: 0;
  bottom: 0;
  right: 0;
  z-index: 11;
}
</style>


相关实践学习
通义万相文本绘图与人像美化
本解决方案展示了如何利用自研的通义万相AIGC技术在Web服务中实现先进的图像生成。
目录
相关文章
|
前端开发
修改Ant Design 按钮的样式
修改Ant Design 按钮的样式
1010 0
|
移动开发 Android开发 HTML5
uniapp视频播放功能
uniapp视频播放功能
1441 0
|
机器学习/深度学习 人工智能 搜索推荐
人工智能推荐系统
人工智能推荐系统
953 2
|
定位技术 数据格式
Echarts实战案例代码(59):geomap实现飞线、散点、引导线以及重叠label的解决
Echarts实战案例代码(59):geomap实现飞线、散点、引导线以及重叠label的解决
1256 0
|
JavaScript
element-plus 按需引入将英文组件修改为中文
element-plus 按需引入将英文组件修改为中文
element-plus 按需引入将英文组件修改为中文
|
12月前
|
移动开发 编解码 前端开发
HTML5 <video>视频详解
HTML5引入了内置的`&lt;video&gt;`标签,简化了网页中视频的嵌入与播放。本文详细介绍了HTML5视频的基本语法、常用属性(如controls、autoplay等)、示例代码及使用注意事项,包括浏览器兼容性、跨域请求处理和响应式设计。通过JavaScript还可实现对视频播放的动态控制。掌握这些技巧,有助于提升网站的多媒体体验。
|
11月前
Threejs用官方提供的编辑器做一个简单的模型
这篇文章介绍了如何使用Three.js内置的编辑器来创建和编辑简单的3D模型,并提供了相应的操作指南。
1184 1
|
移动开发 JavaScript HTML5
Vue3视频播放(Video)
这篇文章介绍了如何在Vue 3框架中创建一个视频播放组件(Video),支持自定义视频源、封面、自动播放等多种播放选项和样式设置。
2179 1
Vue3视频播放(Video)
|
网络协议 大数据 云栖大会
2024云栖大会 预告:IPv6与DNS基础资源专场
2024云栖大会 预告:IPv6与DNS基础资源专场
2024云栖大会 预告:IPv6与DNS基础资源专场
|
11月前
|
JavaScript
vue中使用echarts绘制双Y轴图表时,刻度没有对齐的两种解决方法
vue中使用echarts绘制双Y轴图表时,刻度没有对齐的两种解决方法
2578 0