遇到一个需求:当前播放的视频上面放置一个按钮,点击按钮能够截取当前视频的那一帧,三秒过后可以重新截取,视频暂停隐藏按钮。下面是代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>前端截取当前播放的视频帧为图片</title>
<!--引入 element-ui 的样式,-->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>
<!-- 引入element 的组件库-->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
#app {
margin: 30px;
}
.video-wrap {
width: 500px;
height: 500px;
position: relative;
background-color: #000;
display: inline-block;
margin-right: 100px;
}
video {
width: 100%;
height: 100%;
}
.cover-btn {
position: absolute;
top: 0;
right: 0;
padding: 8px 12px;
background: rgba(255, 255, 255, 0.8);
z-index: 1;
cursor: pointer;
border-radius: 0px 6px;
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: #2c66ff;
transition: all 0.3s;
.icon {
width: 14px;
height: 14px;
}
&:hover {
color: rgba(44, 102, 255, 0.72);
}
&.success {
color: #121314;
cursor: text;
}
}
</style>
</head>
<body>
<div id="app">
<div class="video-wrap">
<!-- 视频播放才展示该按钮 -->
<div v-if="isPlay" class="cover-btn" :class="{ success: updateImgTip }" @click="captureFrame">
{{ updateImgTip ? "截图成功" : "将当前画面设为截图" }}
<svg-icon v-if="updateImgTip" class="icon" icon-class="icon-duigou" />
</div>
<!--
crossorigin 属性用于定义是否支持 CORS 请求。从另一个域或第三方服务器获取资源时。它包括音频等资源。视频、图片。链接和脚本。它用于处理 CORS 请求,检查是否允许共享来自其他域的资源是安全的。
用法:用于<audio>、<video>、<link>、<img>、<script>等很多元素。
也就是说如果用了 crossOrigin="anonymous" 表示是想跨域获取这张图片,但是跨域获取时需要服务器端支持,也就是请求返回头部要有:Access-Control-Allow-Origin:*。
-->
<video ref="video" controls crossOrigin="Anonymous" @play="updatePlayingState(true)"
@pause="updatePlayingState(false)" loop>
<source
src="https://files.porsche.cn/filestore/video/multimedia/none/modelseries-718-models-overview-design-loop/video-mp4/92fb8d17-1205-11eb-80ce-005056bbdc38/porsche-video.mp4"
type="video/mp4" preload="auto" />
您的浏览器不支持 video 标签。
</video>
</div>
<img v-if="base64String" style="width: 200px" :src="base64String" alt="">
</div>
<script>
new Vue({
el: '#app',
data() {
return {
// 是否播放中
isPlay: false,
// 是否更新截图文案
updateImgTip: false,
// 防止按钮重复点击
btnDisabled: false,
// 定时器
interval: null,
// 倒计时
captchaTime: 3,
// 固定时间
fixedSecond2: 3,
base64String: null
}
},
created() {
},
methods: {
// 获取视频播放、暂停状态
updatePlayingState(State) {
this.isPlay = !this.isPlay
},
// 将当前画面设为素材封面
async captureFrame() {
// 防止重复点击
if (this.btnDisabled) return;
this.btnDisabled = true;
// 创建一个canvas元素
let canvas = document.createElement("canvas");
const ratio = this.$refs.video.videoWidth / this.$refs.video.videoHeight;
canvas.width = 300;
canvas.height = canvas.width / ratio;
// 将当前的视频帧画到canvas上
let ctx = canvas.getContext("2d");
ctx.drawImage(this.$refs.video, 0, 0, canvas.width, canvas.height);
/*
canvas.toDataURL(type, encoderOptions);
1、type:图片格式,默认为 image/png,可以是其他image/jpeg等
2、encoderOptions:0到1之间的取值,主要用来选定图片的质量,默认值是0.92,超出范围也会选择默认值。
*/
// 将canvas上的图像转换为base64字符串
this.base64String = canvas.toDataURL("image/png", 0.8);
// 销毁canvas
canvas = null;
console.log(this.base64String);
// 模拟ajax,成功之后
setTimeout(() => {
this.updateImgTip = true;
this.countdownFun3();
}, 300);
},
// 3秒倒计时
countdownFun3() {
this.interval = setInterval(() => {
this.captchaTime = this.captchaTime - 1;
if (this.captchaTime === 0) {
clearInterval(this.interval);
this.btnDisabled = false;
this.captchaTime = this.fixedSecond2;
this.updateImgTip = false;
}
}, 1000);
},
}
})
</script>
</body>
</html>
上面是需求的代码。在开发过程中,遇到几个问题:
- video标签必须设置属性crossOrigin="Anonymous",如果不设置,会报这个错:
Uncaught (in promise) DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.。 - 如果视频资源是接口获取的,需要配置资源跨域功能,比如视频是存储在阿里云的,就需要找后端或者运维在阿里云配置跨域,如果不配置跨域,但又加上了crossOrigin="Anonymous"属性,会导致视频无法播放。
上面的两个问题改了很久才解决,参考跨源相关机制综述(三):crossorigin属性
在网上浏览遇到另一段话:
crossOrigin="anonymous"是什么意思:
crossorigin 属性用于定义是否支持 CORS 请求。从另一个域或第三方服务器获取资源时。它包括音频等资源。视频、图片。链接和脚本。它用于处理 CORS 请求,检查是否允许共享来自其他域的资源是安全的。
用法:用于<audio>、<video>、<link>、<img>、<script>等很多元素。
也就是说如果用了 crossOrigin="anonymous" 表示是想跨域获取这张图片,但是跨域获取时需要服务器端支持,也就是请求返回头部要有:Access-Control-Allow-Origin:*。
跨域图片或视频能正常裁剪(图片未转化成base64),应该满足三个条件:
1、img或video元素中设置crossorigin属性
2、图片或视频资源允许跨域,设置响应头Access-Control-Allow-Origin
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。