vue前端video视频截图与录影功能的简单实现
目录
截图:截取图片后可以进行涂鸦编辑,并能清除,撤销。
录影:点击按钮后录制10s左右的视频文件。
文件上传服务器示例
最近项目开发中,需要实现在video 视频上截图,录影后将文件上传到阿里云服务器上。截图功能相对来说比较容易实现,使用canvas 的 drawImage 方法将video 控件的区域绘制下来即可。录影相对来说比较麻烦,目前用webRTC 简单实现。
功能简单介绍:使用videojs来播放海康NVR 的Dash视频流,需要针对实时画面进行截取,或者录制10s的视频片段然后上传至阿里云上。
截图:截取图片后可以进行涂鸦编辑,并能清除,撤销。
//截取当前帧的图片
cutPicture(){let self=this;self.showCancelContent=false; // 在点击video 上的截图按钮隐藏取消编辑区域。if(self.sourceList.length>=5){self.notify('最多上传5个资源!','warning',3000);return false;}if(self.fullScreen){ //视频如果处于全屏状态,退出全屏self.exitFullscreen();self.fullScreen=false;}self.showCutDialog=true;self.$nextTick(()=>{self.canvasEl=document.getElementById('icanvas');var ctx = self.canvasEl.getContext('2d'); ctx.drawImage(self.videoEl,0,0,767*self.percentHeight,431*self.percentHeight);var oGrayImg=self.canvasEl.toDataURL('image/jpeg'); // 这里通过toDataURL 获取图片的URL连接链接self.imageCanvas.src=oGrayImg; let imgObj=new Image();imgObj.src=oGrayImg; //新建一个image 对象把初始的canvas.src 添加到数组中。self.imageCanvasList.push(imgObj);})
},
涂鸦编辑:通过监听canvas上的鼠标按下,移动,抬起事件来进行绘制。
//给canvas注册mousedown mousemove 事件
mouseDownAction(e){let self=this;self.isMouseDown=true;self.X=e.offsetX;self.Y=e.offsetY;self.showPenBtn=false;
},
mouseMoveAction(e){let self=this;if(self.isMouseDown){self.X1=e.offsetX;self.Y1=e.offsetY;self.showPenBtn=false; //鼠标移动的时候隐藏画笔区域self.drawLine(self.X,self.Y,self.X1,self.Y1);self.flag++;}
},
mouseUpAction(e){let self=this;self.isMouseDown=false;self.showPenBtn=true;self.showCancelContent=true; //每次鼠标弹起后显示撤销区域,画笔区域if(self.flag!=0&&self.canvasEl!=''){let imgObj=new Image();imgObj.src=self.canvasEl.toDataURL("image/jpeg");self.imageCanvasList.push(imgObj); //每编辑一次就存入数组中}self.flag=0;
},
drawLine(x,y,x1,y1){let self=this;var ctx=self.canvasEl.getContext('2d');if(self.flag){ctx.beginPath();}ctx.moveTo(x,y);ctx.lineWidth=4;ctx.strokeStyle=self.penChecked;ctx.lineTo(x1,y1);ctx.stroke();if(self.flag!=0){self.X=self.X1;self.Y=self.Y1;}
},
涂鸦撤销清除功能:在清空canvas的同时,把数组中存放的image 再绘制到canvas上。
// 清空canvas 上所有的涂鸦信息
removeEditCanvas(){let self=this;self.showCancelContent=false;self.canvasEl=document.getElementById('icanvas');var ctx = self.canvasEl.getContext('2d');ctx.clearRect(0,0,767*self.percentHeight,431*self.percentHeight); ctx.drawImage(self.imageCanvasList[0],0,0,767*self.percentHeight,431*self.percentHeight);self.imageCanvasList=[];
},
// 撤销一次涂鸦
cancleEditCanvas(){let self=this;self.showCancelContent=false;self.imageCanvasList.pop();self.canvasEl=document.getElementById('icanvas');var ctx = self.canvasEl.getContext('2d');ctx.clearRect(0,0,767*self.percentHeight,431*self.percentHeight);if(self.imageCanvasList.length==0){ ctx.drawImage(self.imageCanvas,0,0,767*self.percentHeight,431*self.percentHeight);}else{ctx.drawImage(self.imageCanvasList[self.imageCanvasList.length-1],0,0,767*self.percentHeight,431*self.percentHeight); }
},
最终点击确认添加到下方的sourceList 中。
录影:点击按钮后录制10s左右的视频文件。
import RecordRTC from '../../../static/RecordRTC.js'
getVideo(){let self=this;self.showGetVideo=true; //显示录制按钮self.videoSpeed=0;self.$nextTick(()=>{self.startTimeCutVideo=new Date().getTime();self.computeFrame();self.looper();setTimeout(()=>{var btn_canvas = document.getElementById("btn-graph-canvas");self.drawBtn(btn_canvas, 100, "#f31d65", "#f31d65"); //在开始录制的同时显示录制进度的按钮。},1000)})
},
looper(){let self=this;if(!self.isRecordingStarted){self.timeVideo=setTimeout(self.looper, 0);}else{self.endTImeCutVideo=new Date().getTime();if((self.endTImeCutVideo-self.startTimeCutVideo)/1000>11){clearTimeout(self.timeVideo);self.showGetVideo=false;self.isRecordingStarted=false;self.isREC=false;setTimeout(()=>{self.addVideoToList();},100)}else{self.isREC=true;html2canvas(self.videoEl).then(function(canvas){var ctx = self.canvasEl.getContext('2d');let width=self.varyWindowWidth*0.418;let height=self.varyWindowWidth*0.288;ctx.clearRect(0, 0, width, height);ctx.drawImage(self.videoEl,0,0,width,height);if(self.isStoppedRecording) {return;}requestAnimationFrame(self.looper);})}}},computeFrame(){let self=this;self.canvasEl=document.getElementById('vcanvas');var ctx = self.canvasEl.getContext('2d');self.recorder = RecordRTC(self.canvasEl, {type: 'canvas'});self.isStoppedRecording =false;self.isRecordingStarted = true;self.recorder.startRecording(); //开始录制
},
绘制录制按钮:参考网上大神绘制环形进度条的代码做的。使用计时器每秒绘制10%的长度。
drawMain(drawing_elem, percent, forecolor, bgcolor) {/*@drawing_elem: 绘制对象@percent:绘制圆环百分比, 范围[0, 100]@forecolor: 绘制圆环的前景色,颜色代码@bgcolor: 绘制圆环的背景色,颜色代码*/let self=this;var context = drawing_elem.getContext("2d");var center_x = drawing_elem.width / 2;var center_y = drawing_elem.height / 2;var rad = Math.PI*2/100; // 绘制背景圆圈function backgroundCircle(){context.beginPath();context.lineWidth = 14; //设置线宽var radius = center_x - context.lineWidth;context.arc(center_x, center_y, radius, 0, Math.PI*2, false);context.fillStyle=bgcolor;context.globalAlpha = 0.5;context.fill();}//绘制运动圆环function foregroundCircle(n){context.save();context.strokeStyle = forecolor;context.globalAlpha = 1;context.lineWidth = 6;context.lineCap = "round";var radius = center_x - context.lineWidth;context.beginPath();context.arc(center_x, center_y, radius , -Math.PI/2, -Math.PI/2 +n*rad, false); //用于绘制圆弧context.arc(x坐标,y坐标,半径,起始角度,终止角度,顺时针/逆时针)context.stroke();context.closePath();context.restore();}//绘制文字function text(n){context.save();context.fillStyle='white';context.globalAlpha = 1;var font_size=self.btnFontSize;context.font='bold '+font_size+'px Helvetica';var textStr='';if(n==100){textStr='录制成功';}else{textStr='正在录制';}var text_width = context.measureText(textStr).width;context.fillText(textStr,center_x-text_width/2,center_y+font_size/2);context.restore();}//执行动画function drawFrame(speed){context.clearRect(0, 0, drawing_elem.width, drawing_elem.height);backgroundCircle();text(speed);foregroundCircle(speed);if(speed>=percent){clearInterval(self.videoSpeedId);}}self.videoSpeedId=setInterval(() => {if(self.videoSpeed >= percent){return;}else{self.videoSpeed += 2;drawFrame(self.videoSpeed);}}, 100);},
在录影过程中,可以设置面板pointer-events 样式来防止用户在页面上进行操作,从而确保10s的视频录制完成。
pointer-events: none;
功能最终实现结果如下所示。
视频截图涂鸦的demo 如下地址
https://github.com/dickbinge/vue-node-admin/blob/jingbin_dev/vue-admin/src/views/video-demo/cutPicture.vue
视频录影,如果视频质量较高录影卡顿就会非常明显,这个暂时没有办法解决。
文件上传服务器示例
这里首先申请阿里云OSS存储服务,具体参考网上教程。
使用canvas 的 toDataURL 方法可以将canvas对象转换为一个 data-URL地址。示例如下
1,首先画一个示例的canvas画板,然后获取对应的data-URL ,并给到一个image对象。
let iCanvas = document.getElementById('canvas');
var getCanvas = function() {if (!iCanvas) {iCanvas = document.createElement('canvas');iCanvas.width = 500;iCanvas.height = 300;iCanvas.style.position = 'absolute';iCanvas.style.left = 0;iCanvas.style.top = 0;var ctx = iCanvas.getContext('2d');ctx.fillStyle = 'blue';ctx.fillRect(10, 10, 300, 300);document.body.append(iCanvas);}
};
getCanvas();var getImageSrc = function() {var src = iCanvas.toDataURL(); // toDataURL 默认参数是image/jpeg, base64位的编码var imgObj = new Image();imgObj.src = src; // 可以得到一个image对象iCanvas.toBlob(blob => {imgObj.file = blob; // blob 文件可以通过formData格式传参直接上传服务器var url = URL.createObjectURL(blob); // 用于URL的File对象,Blob对象。});
}
通过canvas的toBlob 方法创造Blob对象,type指定图片格式,默认格式为image/png
canvas.toBlob(callback, type, encoderOptions);
2,上传到oss存储
上传文件前需要把我们上传的文件转换成 Blob,File 格式。
其中File 继承自Blob类,是特殊的Blob,可以用在任意的Blob类型Context 中,即Blob适用的场景File也可以。
// 阿里云OSS存储
var uploadImg = (fileObj) => {const OSS = require('ali-oss'); // 引入阿里云的包const client = new OSS({region,accessKeyId,accessKeySecret,bucket,});client.multipartUpload(fileObj.fileName, fileObj.file).then((result) => {const fileUrl = result.name; // 最终可得到一个上传成功后的文件云端路径,我们可以把这个地址存储在数据库中,当需要查看上传的图片时再进行解析即可。});
};
3,使用formData通过请求后端接口上传
var uploadItem = (fileObj) => {var formData = new FormData(); // 获取一个formData 对象formData.append('file', fileObj.file);axios.post('http://....', formData).then(res => { console.log(res);}); //请求接口进行上传
}
可以看到formData 格式中的file有了时间戳等信息。
4,File 格式上传服务器
通常我们可以使用input type="file" 进行上传,我们可以通过inputfile action 直接上传,也可以通过FormData 格式调用接口上传。
<input type="file"multiple="multiple"id="avatar" name="avatar"accept="image/png, image/jpeg">
<script>var inputDom = document.getElementId('avatar');var fileList = Array.from(inputDom.files);// 由于fileList 是一个FileList类型 因此我们可以将其转为Array类型进行遍历。fileList.forEach(file => {uploadItem(file); // 这里即可以把这个file转为formData 格式然后手动请求后端接口});
</script>
vue前端video视频截图与录影功能的简单实现相关推荐
- vue实现mp4视频截图
vue实现mp4视频截图 需求:按帧数(时间)截取图片 /*** @description : 视频截图* @param {String} url : 视频地址* @param {Number} cu ...
- html5前端Video视频标签和audio音频标签的使用
html5前端Video视频标签和audio音频标签的使用 h5新特性中关于Video视频标签和audio音频标签的使用和浅谈 一.Video视频标签 video标签是h5新特性中用来播放视频的控件, ...
- JS,VUE检测Video视频是否全屏播放
检测video视频是否正在全屏播放,如果正在全屏播放将会返回 var isRouterAlive = ref(true) function reload() {//刷新页面isRouterAlive. ...
- 基于Vue+canvas实现视频截图功能
开发过程中遇到一个实际问题,上传的视频需要提供视频封面(视频封面必填).封面可以自己制作并上传,但是这样需要脱离网站,用其他方式制作封面,使用体验并不友好,因此第一个想到的方案是:上传视频时,若人员未 ...
- 使用vue控制video视频和弹幕功能
2020-02-19 前两天想说练一下vue,就按照bilibili写了一个demo(我第一次放这种模仿的页面,如果有哪里不合适的请告诉我哈),就写了比较核心的首页和视频播放页,包括控制视频和弹幕渲染 ...
- 前端 显示视频截图; 在线获取视频的截图
当你需要显示视频封面:但没有封面图地址的时候 可以用一下这个方法 <video :src="item" :poster="item+'?x-oss-process= ...
- video 视频截图 云储存 跨域引用
<video poster="" src="" crossOrigin="anonymous" width="100%&qu ...
- js截屏 video_用原生JS和html5进行视频截图并保存到本地
Video视频截图 body, h1, h2, p { margin:0; padding:0; } html { font-family:"微软雅黑"; background-c ...
- 20190227最近比较纠结的问题vue的video中视频的播放和nginx-rtmp的推流以及什么时候推流的分析
1.vue中的video的使用(支持MP4) Vue中引入Video.js视频播放器 参考:https://www.jianshu.com/p/8b8023c7ed37 Video.js是一个有着HT ...
最新文章
- 信息网络基础设施普遍薄弱,提防信息安全风险--央行副行长
- matlab里点云的读入显示和保存
- USB device如何进入suspend模式
- Hello Blazor:(14)CSS隔离
- python通过什么连接数据库_如何使用python连接数据库?
- NODE安装N管理出错
- Java集合框架讲解【泛型、Collection接口、Map接口、以及子接口和实现类、集合的遍历形式等】
- Matlab遗传算法工具箱求函数最小值
- 【雷达与对抗】【2014.06】荷兰人工育滩工程Sand Motor的X波段雷达深度反演模型研制
- 企业微信检测僵尸粉小工具,企业微信如何检测僵尸粉
- 魏武帝 太祖知不可匡正,遂不复献言
- Java字符串查找第一个不重复字符_java如何实现获取字符串中第一个出现不重复的字符...
- i510200h和i78750h哪个好?有什么区别
- 苹果App Store审核指南中文翻译
- 【Pytorch神经网络理论篇】 34 样本均衡+分类模型常见损失函数
- 微信小程序/校园社区论坛/微信云开发/云函数
- Quartus软件及器件库下载及安装
- JAVA程序员常用访问网址
- 不借助编辑器自带的代码高亮工具(Syntaxhighlighte),生成完美格式的语法高亮代码
- Cognitive Complexity of methods should not be too high Refactor this method to reduce its Cognitive