vue中实现使用相框点击拍照,canvas进行前端图片合并下载
拍照和相框合成,下载图片dome
一、canvas介绍
Canvas是一个HTML5元素,它提供了一个用于在网页上绘制图形、图像和动画的2D渲染上下文。Canvas可以用于创建各种图形,如线条、矩形、圆形、文本等,并且可以通过JavaScript进行编程操作。
Canvas元素本身是一个矩形框,可以通过CSS样式进行样式设置。在Canvas上绘制图形时,需要先获取Canvas的2D渲染上下文,然后通过上下文的方法来进行绘制。
二、navigator.mediaDevices.getUserMedia介绍
navigator.mediaDevices.getUserMedia是一个Web API,它允许网页访问用户的媒体设备,如摄像头和麦克风。这个API返回一个Promise对象,成功后会resolve回调一个MediaStream对象。
使用navigator.mediaDevices.getUserMedia调用系统原生摄像头功能时,需要调用其getUserMedia方法并传入一个包含媒体类型约束的约束对象。这个约束对象可以包含音频、视频或两者都包含。?
navigator.mediaDevices.getUserMedia({ audio: true, video: true }) ?
? .then(function(stream) { ?
? ? // 在这里使用媒体流 ?
? }) ?
? .catch(function(err) { ?
? ? // 处理错误 ?
? });如果用户同意,getUserMedia方法会返回一个包含音频和视频轨道的MediaStream对象。我们可以在then回调函数中使用这个媒体流。如果用户拒绝访问权限,或者需要的媒体源不可用,promise会reject回调一个PermissionDeniedError或者NotFoundError。
三、拍照下载图片功能
1:拍照画布
<!-- 拍照canvas -->
<canvas style="display: none;" ref="canvasCamera" class="canvas"></canvas>
2:显示调用摄像头效果
<video ref="photoVideo" autoplay class="video"></video>
3:拍照后显示的图片
<img :src="downloadImgLink" alt="" ref="photosDownload" class="photos-download">
3:点击拍照的按钮
<button class="operate-button" @click="btnTakePhotoClicked">
<div class="round"></div>
</button>
四、方法
1:点击拍照
async btnTakePhotoClicked(){
this._context2d=this.canvasCamera.getContext("2d");
//如果已经拍照了就不能在点击拍照
if(!this.photoEnabled) return
// 将canvas画布设置和视频元素的大小一样
this.canvasCamera.width=this.photoVideo.offsetWidth
this.canvasCamera.height=this.photoVideo.offsetHeight
// 截取和视频一样大小的图片保证图片没有变形
this._context2d.drawImage(this.photoVideo,0,0,this.photoVideo.offsetWidth,this.photoVideo.offsetHeight )
this.downloadImgLink =this.canvasCamera.toDataURL("image/png"); // 截取视频最后一帧
this.photoEnabled=false
},
2:下载拍摄照片
//下载拍摄的照片
async downloadImg(){
//如果没有拍照点击下载无效
if(this.downloadImgLink==='') return
let downloadBase64= await this.composeImgs(this.photoImg, this.photosDownload);
//下载base64格式图片需要使用a标签来创建
let a = document.createElement("a");
a.style.display = "none";
a.download = 'christmas';
a.href = downloadBase64;
document.body.appendChild(a);
a.click();
// 下载完成可以点击拍照
this.photoEnabled=true
//下载完成清空上次拍照地址
this.downloadImgLink=''
},
3:将拍好的照片和相框合成一张图片,返回一个base64的图片地址
composeImgs(img1,img2){
return new Promise((resolve, reject) => {
//2,创建画笔
const context = this.downloadCanvas.getContext('2d');
//3,设置背景的宽高
this.downloadCanvas.width = this.photoVideo.offsetWidth;
this.downloadCanvas.height = this.photoVideo.offsetHeight;
// 第一张图片传的是个链接 需要new Image 创建一个html元素
// 如果本身就是一个html元素 那么可以直接调用 drawImage 方法进行绘制图片
const myImage1 = new Image();
myImage1.src = img1;
myImage1.crossOrigin = 'Anonymous';// 跨域设置
myImage1.onload = () => {
context.drawImage(img2, 0, 0,this.photoVideo.offsetWidth,this.photoVideo.offsetHeight); // 开始画第一张,拍的人物照片
context.drawImage(myImage1, 0, 0, this.photoVideo.offsetWidth,this.photoVideo.offsetHeight); // 开始画第二张,相框放到上面
const base64 = this.downloadCanvas.toDataURL('image/png'); //获取base64的图片流
resolve(base64); // 成功之后返回出去
};
})
},
下面是完整代码
HTML部分
<template>
<div style="padding: 12rem 0;">
<div class="photograph">
<div class="frame-box" @mouseenter="stopScrolling" @mouseleave="startScrolling">
<div class="frame" ref="frameWrap">
<div class="item" v-for="(item, index) in frameList" :key="index" @click="frameBtn(index)">
<img :src="item" alt="" >
</div>
</div>
</div>
</div>
<div class="photo-frame" v-show="frameBox">
<div class="photograph-frame">
<img src="../cyberxmas-close.png" alt="" class="close" @click="close">
<div class="photo">
<img :src="photoImg" alt="" class="photo-img">
<img :src="downloadImgLink" alt="" ref="photosDownload" class="photos-download">
<video ref="photoVideo" autoplay class="video"></video>
<!-- 拍照canvas -->
<canvas style="display: none;" ref="canvasCamera" class="canvas"></canvas>
</div>
<div class="operate">
<div class="download text" @click="downloadImg">Download</div>
<button class="operate-button" @click="btnTakePhotoClicked">
<div class="round"></div>
</button>
<div class="download text" @click="retake">Retake</div>
</div>
</div>
<!-- 合成图片canvas -->
<canvas ref="downloadCanvas" style="display: none;"></canvas>
</div>
</div>
</template>
JS部分。注意图片按照自己的项目结构引入图片,navigator.mediaDevices.getUserMedia({})中video参数根据自己需求进行设置、调整
<script>
export default {
data() {
return {
mobile:false,//是否是移动端
frameBox:false, //拍照弹框
frameList: [
require('/img/frame-1.png'),
require('/img/frame-2.png'),
require('img/frame-4.png'),
require('img/frame-5.png'),
require('img/frame-6.png'),
require('/img/frame-7.png'),
require('/img/frame-8.png'),
require('/img/frame-9.png'),
require('/img/frame-10.png'),
require('/img/frame-11.png'),
require('/img/frame-12.png'),
require('/img/frame-13.png'),
require('/img/frame-14.png'),
require('/img/frame-15.png'),
require('/img/frame-16.png'),
],
timer: null,
direction: -1,
photoImg:'/./img/frame-11.0.png?c0cda5dab250893f74a2783d10767d0',
downloadImgLink:'',
photoEnabled:true,//拍照按钮是否启用
}
},
computed:{
// 无缝滚动
frameWrap(){
return this.$refs.frameWrap
},
//拍照视频框
photoVideo(){
return this.$refs.photoVideo
},
//拍照canvas
canvasCamera(){
return this.$refs.canvasCamera
},
//合成图片cancas
downloadCanvas(){
return this.$refs.downloadCanvas
},
// 拍照后的图片
photosDownload(){
return this.$refs.photosDownload
},
},
mounted() {
import("public/js/flexible").then(() => {
this.timer = setInterval(this.frameSlide, 20);
this.mobile=$(window).width() <= 800
})
},
beforeDestroy() {
clearInterval(this.timer)
},
methods: {
//点击拍照
frameBtn(index){
this.photoImg=this.frameList[index]
this.camera()
},
//调用摄像头,绘制画布
async camera(){
if (!("mediaDevices" in navigator) || !("getUserMedia" in navigator.mediaDevices)) {
alert("Media Capture API is not supported");
return;
}
//将视频流放到video标签中显示拍摄画面
await navigator.mediaDevices.getUserMedia({ video:{
//设置图片宽高
width:590,
height:590,
},audio:false }).then((stream)=>{
this.photoVideo.srcObject=stream
//显示弹框
this.frameBox=true
}).catch(err=>{
alert(err)
})
},
async btnTakePhotoClicked(){
this._context2d=this.canvasCamera.getContext("2d");
//如果已经拍照了就不能在点击拍照
if(!this.photoEnabled) return
// 将canvas画布设置和视频元素的大小一样
this.canvasCamera.width=this.photoVideo.offsetWidth
this.canvasCamera.height=this.photoVideo.offsetHeight
// 截取和视频一样大小的图片保证图片没有变形
this._context2d.drawImage(this.photoVideo,0,0,this.photoVideo.offsetWidth,this.photoVideo.offsetHeight )
this.downloadImgLink =this.canvasCamera.toDataURL("image/png"); // 截取视频最后一帧
this.photoEnabled=false
},
//清空画布重新拍照
retake(){
this.downloadImgLink=''
// 清空画布还能点击拍照
this.photoEnabled=true
},
//下载拍摄的照片
async downloadImg(){
//如果没有拍照点击下载无效
if(this.downloadImgLink==='') return
let downloadBase64= await this.composeImgs(this.photoImg, this.photosDownload);
//下载base64格式图片需要使用a标签来创建
let a = document.createElement("a");
a.style.display = "none";
a.download = 'christmas';
a.href = downloadBase64;
document.body.appendChild(a);
a.click();
// 下载完成可以点击拍照
this.photoEnabled=true
//下载完成清空上次拍照地址
this.downloadImgLink=''
},
// 合成图片
composeImgs(img1,img2){
return new Promise((resolve, reject) => {
//2,创建画笔
const context = this.downloadCanvas.getContext('2d');
//3,设置背景的宽高
this.downloadCanvas.width = this.photoVideo.offsetWidth;
this.downloadCanvas.height = this.photoVideo.offsetHeight;
// 第一张图片传的是个链接 需要new Image 创建一个html元素
// 如果本身就是一个html元素 那么可以直接调用 drawImage 方法进行绘制图片
const myImage1 = new Image();
myImage1.src = img1;
myImage1.crossOrigin = 'Anonymous';// 跨域设置
myImage1.onload = () => {
context.drawImage(img2, 0, 0,this.photoVideo.offsetWidth,this.photoVideo.offsetHeight); // 开始画第一张,拍的人物照片
context.drawImage(myImage1, 0, 0, this.photoVideo.offsetWidth,this.photoVideo.offsetHeight); // 开始画第二张,相框放到上面
const base64 = this.downloadCanvas.toDataURL('image/png'); //获取base64的图片流
resolve(base64); // 成功之后返回出去
};
})
},
// 关闭拍照弹框
close(){
this.frameBox=false
// 清空截图
this.downloadImgLink=''
// 启用拍照按钮
this.photoEnabled=true
},
//相框无缝滚动
frameSlide() {
//让frameWrap的marginLeft不断变小 direction:滚动方向 -1是从右到左,1是从左到右 list数组 speed:滚动速度
this.frameWrap.style.marginLeft = this.frameWrap.offsetLeft + this.direction * 1 + 'px';
//判断当frameWrap滚动到一半时,从新拉回去,重0开始
if (this.frameWrap.offsetLeft <= -this.frameWrap.offsetWidth / 2) {
this.frameWrap.style.marginLeft = 0;
}
if (this.frameWrap.offsetLeft > 0) {
this.frameWrap.style.marginLeft = -this.frameWrap.offsetWidth / 2 + 'px';
}
},
// 鼠标移入停止滚动
stopScrolling() {
clearInterval(this.timer)
},
//鼠标移出开始滚动
startScrolling() {
this.timer = setInterval(this.frameSlide, 20);
}
}
}
</script>
CSS部分
<style scoped lang="scss">
.photograph {
.title {
display: block;
margin: auto;
height: 0.45rem;
width: auto;
margin-top: 0.87rem;
}
.frame-box {
width: 13.39rem;
margin: auto;
margin-top: 0.53rem;
height: 2.5rem;
overflow: hidden;
position: relative;
.frame {
display: flex;
position: absolute;
top: 0.15rem;
z-index: 100;
.item {
width: 2rem;
height: 2rem;
background: #E2E2E2;
border: 0.06rem solid #FFFFFF;
opacity: 0.7;
border-radius: 0.3rem;
display: flex;
align-items: center;
justify-content: center;
margin-right: 0.24rem;
cursor: pointer;
&:hover {
transform: scale(1.1);
}
img {
width: 1.9rem;
height: 1.9rem;
display: block;
border-radius: 0.3rem;
}
}
}
}
}
.photo-frame{
position: fixed;
width: 100vw;
height: 100vh;
top: 0;
background: rgba(0,0,0,0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
.photograph-frame{
width: 8.73rem;
height: 8.66rem;
background: rgba(108, 84, 127, 0.92);
border-radius: 0.02rem;
position: relative;
padding-top: 0.61rem;
box-sizing: border-box;
.close{
position: absolute;
right: 0.33rem;
top: 0.19rem;
width: 0.28rem;
height: 0.28rem;
cursor: pointer;
}
.photo{
width: 5.9rem;
height: 5.9rem;
// background: #010308;
border: 0.02rem solid rgba(255,255,255,0.36);
border-radius: 0.02rem;
margin: auto;
position: relative;
.photo-img,.photos-download{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
}
.photos-download{
width: 100%;
z-index: 1;
}
.video{
width: 100%;
height: auto;
position: absolute;
}
.canvas{
position: absolute;
left: -100%;
z-index: 1;
}
}
.operate{
display: flex;
align-items: center;
justify-content: center;
margin-top: 0.23rem;
.text{
width: 0.86rem;
font-size: 0.19rem;
color: #F4CCFB;
text-shadow: 0px 0px 0.09rem #F25DFF;
cursor: pointer;
}
.operate-button{
width: 0.67rem;
height: 0.67rem;
border: 0.04rem solid #FFFFFF;
border-radius: 50%;
background-color: transparent;
margin: 0 0.32rem;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.round{
width: 0.4rem;
height: 0.4rem;
background-color: #FFFFFF;
border-radius: 50%;
}
}
}
}
}
</style>
最终下载下来的图片效果
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!