微信小程序canvas 证件照制作( 二 )

const fsm = wx.getFileSystemManager();const FILE_BASE_NAME = 'tmp_base64src';const base64src = function(base64data, pathName) {return new Promise((resolve, reject) => {const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || [];if (!format) {reject(new Error('ERROR_BASE64SRC_PARSE'));}const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME+pathName}.${format}`;const buffer = wx.base64ToArrayBuffer(bodyData);fsm.writeFile({filePath,data: buffer,encoding: 'binary',success() {resolve(filePath);},fail() {reject(new Error('ERROR_BASE64SRC_WRITE'));},});});};export default base64src;

  1. 将图片按照要一定的比列绘制在canvas 中
let IMG_RATIOlet ratio = 295/413let initWidth = 295let initHeight = 413let scrollTop = 250let IMG_REAL_W,IMG_REAL_HIMG_REAL_H = initHeightIMG_REAL_W = IMG_REAL_H*IMG_RATIOlet canH = imgW * IMG_REAL_H / IMG_REAL_Wconst ctx = uni.createCanvasContext("myselfCanvas", _this);if (color) {ctx.setFillStyle(color)ctx.fillRect(0,0,IMG_REAL_W,IMG_REAL_H)}// 绘制的时候将选中的背景颜色填充到画布中ctx.drawImage(res.path, 0, 0, IMG_REAL_W, IMG_REAL_H);
  1. 根据原图中头像位置坐标 。换算出需要在原图上裁剪出来的区域
let x = location.right_shoulder.x //右肩位置的坐标 xlet y = location.top_head.y - scrollTop // 头部坐标位置 减去一定比列的坐标 ylet x1 = location.left_shoulder.x // 左肩位置坐标 xvar canvasW = ((x1 - x) / imgW) * IMG_REAL_W;// 左肩坐标 减去右肩坐标 和原图的宽度比列 计算出 在上一步绘制的图中裁剪的宽度var canvasH = canvasW/ratio // 根据证件照的比列 计算出 裁剪的高度var canvasL = (x / imgW) * IMG_REAL_W;var canvasT = (y / imgH) * IMG_REAL_H;// 计算裁剪的起始坐标位置
  1. 在canvas 绘制图后导出证件照需要的尺寸
ctx.draw(false,(ret)=>{uni.showToast({icon:'success',mask:true,title: '绘制完成',});uni.canvasToTempFilePath({ // 保存canvas为图片x: canvasL,y: canvasT,width: canvasW, //canvasH,height: canvasH, //canvasH,destWidth: initWidth,destHeight: initHeight,canvasId: 'myselfCanvas',quality: 1,fileType: color? 'jpg': 'png',complete: function(res) {resolve(res.tempFilePath)} ,})});导出证件照之前 , 还需要修改图片的dpi
  1. 修改图片dpi 是将图片转为base64 格式后修改 。本项目使用changedpi 插件
  2. npm install changedpi
import {changeDpiDataUrl} from 'changedpi'export const changeDpi = (url, dpi) => {return new Promise((resolve) => {if (dpi) {uni.getFileSystemManager().readFile({filePath: url, //选择图片返回的相对路径encoding: 'base64', //编码格式success: res => {// 成功的回调// 'data:image/jpeg;base64,'let base64 = res.data;let str = changeDpiDataUrl('data:image/jpeg;base64,' + base64, dpi)base64src(str).then(filePath => {resolve(filePath)})}});}else {resolve(url)}})}
  1. 在小程序中使用需要注意 插件中直接使用了btoa atob 两个函数 。但是在小程序是不支持直接调用的 。需要重写这两个方法
  2. 重写的方法
(function(f) {'use strict';/* istanbul ignore else */if (typeof exports === 'object' && exports != null &&typeof exports.nodeType !== 'number') {module.exports = f ();} else if (typeof define === 'function' && define.amd != null) {define ([], f);} else {var base64 = f ();var global = typeof self !== 'undefined' ? self : $.global;if (typeof global.btoa !== 'function') global.btoa = base64.btoa;if (typeof global.atob !== 'function') global.atob = base64.atob;}} (function() {'use strict';var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';function InvalidCharacterError(message) {this.message = message;}InvalidCharacterError.prototype = new Error ();InvalidCharacterError.prototype.name = 'InvalidCharacterError';// encoder// [https://gist.github.com/999166] by [https://github.com/nignag]function btoa(input) {var str = String (input);for (// initialize result and countervar block, charCode, idx = 0, map = chars, output = '';// if the next str index does not exist://change the mapping table to "="//check if d has no fractional digitsstr.charAt (idx | 0) || (map = '=', idx % 1);// "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8output += map.charAt (63 & block >> 8 - idx % 1 * 8)) {charCode = str.charCodeAt (idx += 3 / 4);if (charCode > 0xFF) {throw new InvalidCharacterError ("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");}block = block << 8 | charCode;}return output;}// decoder// [https://gist.github.com/1020396] by [https://github.com/atk]function atob(input) {var str = (String (input)).replace (/[=]+$/, ''); // #31: ExtendScript bad parse of /=// if (str.length % 4 === 1) {//throw new InvalidCharacterError ("'atob' failed: The string to be decoded is not correctly encoded.");// }for (// initialize result and countersvar bc = 0, bs, buffer, idx = 0, output = '';// get next characterbuffer = str.charAt (idx++); // eslint-disable-line no-cond-assign// character found in table? initialize bit storage and add its ascii value;~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,// and if not first of each 4 characters,// convert the first 8 bits to one ascii characterbc++ % 4) ? output += String.fromCharCode (255 & bs >> (-2 * bc & 6)) : 0) {// try to find character in table (0-63, not found => -1)buffer = chars.indexOf (buffer);}return output;}return {btoa: btoa, atob: atob};}));

推荐阅读