需求描述:
- 最近有这么一个需求,移动端(企微H5)需要手机拍摄图片,同时需要把图片压缩到指定大小,虽然可以调用官方的js-sdk的接口 window.wx.chooseImage 中设置 sizeType 属性来这是拍摄的图片为缩略图还是原图,但是考虑到后端服务器,传输的问题,还是将拍摄的图片进行压缩到指定大小,再使用 canvas 进行添加水印,再进行上传
- 为了实现功能,使用插件 image-conversion 来进行压缩图片 (官方有例子)
下载image-conversion
npm install image-conversion --save
页面引入image-conversion
import * as imageConversion from 'image-conversion'
页面html
<div class="item_box">
<!-- table.ZX_DJHD1702 是放图片的数组,拍摄的图片可能有一张也可能多张 -->
<template v-if="table.ZX_DJHD1702.length !== 0">
<div v-for="(item,index) in table.ZX_DJHD1702" :key="item.id" class="item">
<!-- 加一个判断是为了显示从后端拿到的图片 -->
<template v-if="item.content.includes('data:image/')">
<img :src="item.content" alt="error">
</template>
<template v-else>
<img :src="'data:image/png;base64,' + item.content" alt="error">
</template>
<!-- 删除图片 -->
<i @click="deleteImg('location', item, index)"></i>
</div>
</template>
<!-- 拍摄图片图片 -->
<div class="before_item" @click="takePictures()">
<span>拍照</span>
</div>
<!-- <div class="before_item">
<input type="file" @change="changeFile($event, 'location')">
<span>拍照</span>
</div> -->
</div>
js事件
首先需要拿到用户拍摄的图片数据
// 拍摄图片
takePictures (str) {
const $this = this
// console.log('str: ', str)
window.wx.chooseImage({
count: 1, // 默认9
sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['camera'], // 可以指定来源是相册还是相机,默认二者都有
defaultCameraMode: 'normal', // 表示进入拍照界面的默认模式,目前有normal与batch两种选择,normal表示普通单拍模式,batch表示连拍模式,不传该参数则为normal模式。从3.0.26版本开始支持front和batch_front两种值,其中front表示默认为前置摄像头单拍模式,batch_front表示默认为前置摄像头连拍模式。(注:用户进入拍照界面仍然可自由切换两种模式)
isSaveToAlbum: 1, // 整型值,0表示拍照时不保存到系统相册,1表示自动保存,默认值是1
success: function (res) {
console.log('调用企微接口:', res)
var localIds = res.localIds // 返回选定照片的本地ID列表,
// andriod中localId可以作为img标签的src属性显示图片;
// iOS应当使用 getLocalImgData 获取图片base64数据,从而用于img标签的显示(在img标签内使用 wx.chooseImage 的 localid 显示可能会不成功)
// 苹果手机获取拍过的图片
}
)}
}
注: (这里得到的localIds是你拍摄的图片base64,安卓和苹果需要区分)使用 ua = navigator.userAgent 来区分用户的手机型号是安卓系统还是苹果系统 拿到base64数据以后就可以对图片进行一系列的操作了
生成一张图片
dataUrl 是拍摄图片的base64编码 返回一个primise
// 生成图片
createImage (dataUrl) {
return new Promise((resolve) => {
const img = new Image()
img.src = dataUrl
img.onload = () => {
resolve(img)
}
})
},
添加水印
img 形参是上边方法返回的图片
// 写水印
createWatermark (img) {
const str = ' 经度' + this.lat + ' 纬度' + this.lng
// 创建canvas
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
this.imgWidth = img.width
this.imgHeight = img.width
// 绘制图片
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
// 添加水印
ctx.font = '50px Microsoft YaHei'
ctx.fillStyle = '#fff'// 设置水印字体颜色
ctx.textAlign = 'left'
ctx.textBaseline = 'Middle'
ctx.rotate(-0.2)
ctx.fillText(str, canvas.width * 0.1, canvas.height * 0.2)
// 返回带有水印的图片
return canvas.toDataURL()
},
格式转换
添加水印后 返回的canvas是base64格式 但是在图片压缩的插件image-conversion中接收的文件格式是 file 因此需要把格式转换一下
// 图片 base64 转换 file 格式
baseToFile (base64) {
if (base64.includes('data:image/')) {
// console.log('base64: ', base64)
const fileArray = base64.split(',')
// 过滤出文件类型
const fileType = fileArray[0].match(/:(.*?);/)[1]
// atob 是对经过 base-64 编码的字符串进行解码
const bstr = atob(fileArray[1])
let n = bstr.length
// Uint8Array 数组类型表示一个 8 位无符号整型数组
const u8arr = new Uint8Array(n)
while (n--) {
// 返回字符串n个字符的 Unicode 编码
u8arr[n] = bstr.charCodeAt(n)
}
const second = new Date()
return new File([u8arr], second.getTime(), { type: fileType })
},
压缩图片
返回的文件类型是blob格式的数据
// 压缩图片
compressImg (file) {
return new Promise((resolve) => {
if (file.size / 1024 > 200) {
imageConversion.compressAccurately(file, { width: 360, height: 640, size: 90 }).then(res => {
resolve(res)
})
} else {
resolve(file)
}
})
},
注:( 图片压缩的写法有很多,想了解更多的请自行查看 image-conversion官网 )
压缩完后需要把数据格式转换成后端需要的数据格式
我这里是把blob类型的数据转换成了file格式
// 类型转换 blob 转换 file
transToFile (data) {
return new Promise((resolve, reject) => {
console.log(data)
// 转换bolb类型
const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
// 这么写是因为文件转换是异步任务
const transToFile = async (blob, fileName, fileType) => {
return new window.File([blob], fileName, { type: fileType })
}
const textContain = transToFile(blob, 'put.png', 'image/png')
// 转换完成后可以将file对象传给接口
textContain.then((res) => {
// console.log('res', res)
resolve(res)
})
})
}
完整代码
<template>
<div class="item_box">
<template v-if="table.ZX_DJHD1702.length !== 0">
<div v-for="(item,index) in table.ZX_DJHD1702" :key="item.id" class="item">
<template v-if="item.content.includes('data:image/')">
<img :src="item.content" alt="error">
</template>
<template v-else>
<img :src="'data:image/png;base64,' + item.content" alt="error">
</template>
<i @click="deleteImg('location', item, index)"></i>
</div>
</template>
<div class="before_item" @click="takePictures('location')">
<span>拍照</span>
</div>
<!-- <div class="before_item">
<input type="file" @change="changeFile($event, 'location')">
<span>拍照</span>
</div> -->
</div>
</template>
<script>
import storage from '@/modules/utils/storage'
import { isIOS } from '@/modules/utils/index.js'
import waterMark from '@/modules/utils/waterMark.js'
import * as imageConversion from 'image-conversion'
export default {
data () {
return {
// 用于页面上显示
table: {
ZX_DJHD1702: [],
},
// 用于传参
query: {
ZX_DJHD1702: [],
},
lat: '',
lng: '',
imgWidth: '',
imgHeight: ''
}
},
methods: {
// 拍摄图片
takePictures (str) {
const $this = this
// console.log('str: ', str)
window.wx.chooseImage({
count: 1, // 默认9
sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['camera'], // 可以指定来源是相册还是相机,默认二者都有
defaultCameraMode: 'normal', // 表示进入拍照界面的默认模式,目前有normal与batch两种选择,normal表示普通单拍模式,batch表示连拍模式,不传该参数则为normal模式。从3.0.26版本开始支持front和batch_front两种值,其中front表示默认为前置摄像头单拍模式,batch_front表示默认为前置摄像头连拍模式。(注:用户进入拍照界面仍然可自由切换两种模式)
isSaveToAlbum: 1, // 整型值,0表示拍照时不保存到系统相册,1表示自动保存,默认值是1
success: function (res) {
console.log('调用企微接口:', res)
var localIds = res.localIds // 返回选定照片的本地ID列表,
// andriod中localId可以作为img标签的src属性显示图片;
// iOS应当使用 getLocalImgData 获取图片base64数据,从而用于img标签的显示(在img标签内使用 wx.chooseImage 的 localid 显示可能会不成功)
// 苹果手机获取拍过的图片
if (isIOS) {
window.wx.getLocalImgData({
localId: res.localIds[0], // 图片的localID
success: function (res) {
// console.log('IOS--getLocalImgData: ', res)
var localData = res.localData // localData是图片的base64数据,可以用img标签显示
$this.createImage(localData).then(res2 => {
if (res2) {
// console.log('createImage', res2)
// 添加水印 base64 格式
const baseUrl = $this.createWatermark(res2)
// base64转换为file格式
const file = $this.baseToFile(baseUrl)
// console.log('压缩前的图片大小:', file.size)
if (file.size / 1024 > 100) {
// 压缩 file 图片
$this.compressImg(file).then(res3 => {
// console.log('压缩完成了: ', res3, '这是blob类型')
if (res3) {
// 本地base64显示图片
$this.transToBase64(res3).then(res4 => {
$this.table.ZX_DJHD1702.push({ content: res4 })
})
// 将压缩好的图片类型转为file格式 用于上传接口保存参数 blob-->file
$this.transToFile(res3).then(res5 => {
const compressedFiled = res5
$this.query.ZX_DJHD1702.push(compressedFiled)
})
}
})
}
}
})
}
})
}
}
})
},
// 写水印
createWatermark (img) {
const str = 'ZRC ' + this.jobNum + ' 经度' + this.lat + ' 纬度' + this.lng
// 创建canvas
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
this.imgWidth = img.width
this.imgHeight = img.width
// console.log('img', img.width, img.height)
// 绘制图片
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
// 添加水印
ctx.font = '50px Microsoft YaHei'
ctx.fillStyle = '#fff'// 设置水印字体颜色
ctx.textAlign = 'left'
ctx.textBaseline = 'Middle'
ctx.rotate(-0.2)
ctx.fillText(str, canvas.width * 0.1, canvas.height * 0.2)
// 返回带有水印的图片
return canvas.toDataURL()
},
// 压缩图片
compressImg (file) {
return new Promise((resolve) => {
if (file.size / 1024 > 200) {
imageConversion.compressAccurately(file, { width: 360, height: 640, size: 90 }).then(res => {
resolve(res)
})
} else {
resolve(file)
}
})
},
// 类型转换 blob 转换 file
transToFile (data) {
return new Promise((resolve, reject) => {
console.log(data)
// 转换bolb类型
const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
// 这么写是因为文件转换是异步任务
const transToFile = async (blob, fileName, fileType) => {
return new window.File([blob], fileName, { type: fileType })
}
const textContain = transToFile(blob, 'put.png', 'image/png')
// 转换完成后可以将file对象传给接口
textContain.then((res) => {
// console.log('res', res)
resolve(res)
})
})
},
// 图片 base64 转换 file 格式
baseToFile (base64) {
if (base64.includes('data:image/')) {
// console.log('base64: ', base64)
const fileArray = base64.split(',')
// 过滤出文件类型
const fileType = fileArray[0].match(/:(.*?);/)[1]
// atob 是对经过 base-64 编码的字符串进行解码
const bstr = atob(fileArray[1])
let n = bstr.length
// Uint8Array 数组类型表示一个 8 位无符号整型数组
const u8arr = new Uint8Array(n)
while (n--) {
// 返回字符串n个字符的 Unicode 编码
u8arr[n] = bstr.charCodeAt(n)
}
const second = new Date()
return new File([u8arr], second.getTime(), { type: fileType })
} else {
const bstr = atob(base64)
let n = bstr.length
// Uint8Array 数组类型表示一个 8 位无符号整型数组
const u8arr = new Uint8Array(n)
while (n--) {
// 返回字符串n个字符的 Unicode 编码
u8arr[n] = bstr.charCodeAt(n)
}
const second = new Date()
return new File([u8arr], second.getTime(), { type: 'image/png' })
}
},
// 删除图片
deleteImg (str, item, index) {
this.query.ZX_DJHD1702.splice(index, 1)
this.table.ZX_DJHD1702.splice(index, 1)
},
}
}
</script>
效果可以看我视频:
拍照添加水印并压缩
注意:
如果你是pc端上传图片的话,就不需要以上js-sdk的代码了
// 上传图片
changeFile (e, str) {
console.log('changeFile', e, str)
const $this = this
if (e) {
console.log('changeFile', e.target.files[0], str)
const file = e.target.files[0]
// 读取文件
$this.readFile(file).then(res => {
// base64格式
if (res) {
console.log('readFile: ')
// 创建图片
$this.createImage(res).then(res2 => {
if (res2) {
console.log('createImage', res2)
// 添加水印
// this.imageUrl = $this.createWatermark(res2)
const url = $this.createWatermark(res2)
console.log('添加过水印的图片:', url)
const file = this.baseToFile(url)
this.compressImg(file).then(res3 => {
this.transToFile(res3).then(res => {
this.query.ZX_DJHD1702.push(res)
})
})
console.log('image File:', file)
}
})
}
})
}
},
// 读取图片文件
readFile (file) {
return new Promise((resolve) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
resolve(reader.result)
}
})
},
pc代码上只不过是通过input标签上传图片可以直接进行水印操作和压缩操作
以上就是全部内容 文章整理若有不足 还望各路大神批评指正