我是使用we-cropper.js插件实现的,下面是实现过程:

文件结构

插件we-cropper.js

/*** we-cropper v1.2.0* (c) 2018 dlhandsome* @license MIT*/
(function (global, factory) {typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :typeof define === 'function' && define.amd ? define(factory) :(global.WeCropper = factory());
}(this, (function () { 'use strict';var device = void 0;
var TOUCH_STATE = ['touchstarted', 'touchmoved', 'touchended'];function isFunction (obj) {return typeof obj === 'function'
}function firstLetterUpper (str) {return str.charAt(0).toUpperCase() + str.slice(1)
}function setTouchState (instance) {var arg = [], len = arguments.length - 1;while ( len-- > 0 ) arg[ len ] = arguments[ len + 1 ];TOUCH_STATE.forEach(function (key, i) {if (arg[i] !== undefined) {instance[key] = arg[i];}});
}function validator (instance, o) {Object.defineProperties(instance, o);
}function    getDevice () {if (!device) {device = wx.getSystemInfoSync();}return device
}var tmp = {};var DEFAULT = {id: {default: 'cropper',get: function get () {return tmp.id},set: function set (value) {if (typeof (value) !== 'string') {console.error(("id:" + value + " is invalid"));}tmp.id = value;}},width: {default: 750,get: function get () {return tmp.width},set: function set (value) {if (typeof (value) !== 'number') {console.error(("width:" + value + " is invalid"));}tmp.width = value;}},height: {default: 750,get: function get () {return tmp.height},set: function set (value) {if (typeof (value) !== 'number') {console.error(("height:" + value + " is invalid"));}tmp.height = value;}},scale: {default: 2.5,get: function get () {return tmp.scale},set: function set (value) {if (typeof (value) !== 'number') {console.error(("scale:" + value + " is invalid"));}tmp.scale = value;}},zoom: {default: 5,get: function get () {return tmp.zoom},set: function set (value) {if (typeof (value) !== 'number') {console.error(("zoom:" + value + " is invalid"));} else if (value < 0 || value > 10) {console.error("zoom should be ranged in 0 ~ 10");}tmp.zoom = value;}},src: {default: 'cropper',get: function get () {return tmp.src},set: function set (value) {if (typeof (value) !== 'string') {console.error(("id:" + value + " is invalid"));}tmp.src = value;}},cut: {default: {},get: function get () {return tmp.cut},set: function set (value) {if (typeof (value) !== 'object') {console.error(("id:" + value + " is invalid"));}tmp.cut = value;}},onReady: {default: null,get: function get () {return tmp.ready},set: function set (value) {tmp.ready = value;}},onBeforeImageLoad: {default: null,get: function get () {return tmp.beforeImageLoad},set: function set (value) {tmp.beforeImageLoad = value;}},onImageLoad: {default: null,get: function get () {return tmp.imageLoad},set: function set (value) {tmp.imageLoad = value;}},onBeforeDraw: {default: null,get: function get () {return tmp.beforeDraw},set: function set (value) {tmp.beforeDraw = value;}}
};function prepare () {var self = this;var ref = getDevice();var windowWidth = ref.windowWidth;self.attachPage = function () {var pages = getCurrentPages();//  获取到当前page上下文var pageContext = pages[pages.length - 1];//  把this依附在Page上下文的wecropper属性上,便于在page钩子函数中访问pageContext.wecropper = self;};self.createCtx = function () {var id = self.id;if (id) {self.ctx = wx.createCanvasContext(id);} else {console.error("constructor: create canvas context failed, 'id' must be valuable");}};self.deviceRadio = windowWidth / 750;
}function observer () {var self = this;var EVENT_TYPE = ['ready', 'beforeImageLoad', 'beforeDraw', 'imageLoad'];self.on = function (event, fn) {if (EVENT_TYPE.indexOf(event) > -1) {if (typeof (fn) === 'function') {event === 'ready'? fn(self): self[("on" + (firstLetterUpper(event)))] = fn;}} else {console.error(("event: " + event + " is invalid"));}return self};
}var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};function createCommonjsModule(fn, module) {return module = { exports: {} }, fn(module, module.exports), module.exports;
}var base64 = createCommonjsModule(function (module, exports) {
/*! http://mths.be/base64 v0.1.0 by @mathias | MIT license */
(function(root) {// Detect free variables `exports`.var freeExports = 'object' == 'object' && exports;// Detect free variable `module`.var freeModule = 'object' == 'object' && module &&module.exports == freeExports && module;// Detect free variable `global`, from Node.js or Browserified code, and use// it as `root`.var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal;if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {root = freeGlobal;}/*--------------------------------------------------------------------------*/var InvalidCharacterError = function(message) {this.message = message;};InvalidCharacterError.prototype = new Error;InvalidCharacterError.prototype.name = 'InvalidCharacterError';var error = function(message) {// Note: the error messages used throughout this file match those used by// the native `atob`/`btoa` implementation in Chromium.throw new InvalidCharacterError(message);};var TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';// http://whatwg.org/html/common-microsyntaxes.html#space-charactervar REGEX_SPACE_CHARACTERS = /[\t\n\f\r ]/g;// `decode` is designed to be fully compatible with `atob` as described in the// HTML Standard. http://whatwg.org/html/webappapis.html#dom-windowbase64-atob// The optimized base64-decoding algorithm used is based on @atk’s excellent// implementation. https://gist.github.com/atk/1020396var decode = function(input) {input = String(input).replace(REGEX_SPACE_CHARACTERS, '');var length = input.length;if (length % 4 == 0) {input = input.replace(/==?$/, '');length = input.length;}if (length % 4 == 1 ||// http://whatwg.org/C#alphanumeric-ascii-characters/[^+a-zA-Z0-9/]/.test(input)) {error('Invalid character: the string to be decoded is not correctly encoded.');}var bitCounter = 0;var bitStorage;var buffer;var output = '';var position = -1;while (++position < length) {buffer = TABLE.indexOf(input.charAt(position));bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer;// Unless this is the first of a group of 4 characters…if (bitCounter++ % 4) {// …convert the first 8 bits to a single ASCII character.output += String.fromCharCode(0xFF & bitStorage >> (-2 * bitCounter & 6));}}return output;};// `encode` is designed to be fully compatible with `btoa` as described in the// HTML Standard: http://whatwg.org/html/webappapis.html#dom-windowbase64-btoavar encode = function(input) {input = String(input);if (/[^\0-\xFF]/.test(input)) {// Note: no need to special-case astral symbols here, as surrogates are// matched, and the input is supposed to only contain ASCII anyway.error('The string to be encoded contains characters outside of the ' +'Latin1 range.');}var padding = input.length % 3;var output = '';var position = -1;var a;var b;var c;var buffer;// Make sure any padding is handled outside of the loop.var length = input.length - padding;while (++position < length) {// Read three bytes, i.e. 24 bits.a = input.charCodeAt(position) << 16;b = input.charCodeAt(++position) << 8;c = input.charCodeAt(++position);buffer = a + b + c;// Turn the 24 bits into four chunks of 6 bits each, and append the// matching character for each of them to the output.output += (TABLE.charAt(buffer >> 18 & 0x3F) +TABLE.charAt(buffer >> 12 & 0x3F) +TABLE.charAt(buffer >> 6 & 0x3F) +TABLE.charAt(buffer & 0x3F));}if (padding == 2) {a = input.charCodeAt(position) << 8;b = input.charCodeAt(++position);buffer = a + b;output += (TABLE.charAt(buffer >> 10) +TABLE.charAt((buffer >> 4) & 0x3F) +TABLE.charAt((buffer << 2) & 0x3F) +'=');} else if (padding == 1) {buffer = input.charCodeAt(position);output += (TABLE.charAt(buffer >> 2) +TABLE.charAt((buffer << 4) & 0x3F) +'==');}return output;};var base64 = {'encode': encode,'decode': decode,'version': '0.1.0'};// Some AMD build optimizers, like r.js, check for specific condition patterns// like the following:if (typeof undefined == 'function' &&typeof undefined.amd == 'object' &&undefined.amd) {undefined(function() {return base64;});}    else if (freeExports && !freeExports.nodeType) {if (freeModule) { // in Node.js or RingoJS v0.8.0+freeModule.exports = base64;} else { // in Narwhal or RingoJS v0.7.0-for (var key in base64) {base64.hasOwnProperty(key) && (freeExports[key] = base64[key]);}}} else { // in Rhino or a web browserroot.base64 = base64;}}(commonjsGlobal));
});function makeURI (strData, type) {return 'data:' + type + ';base64,' + strData
}function fixType (type) {type = type.toLowerCase().replace(/jpg/i, 'jpeg');var r = type.match(/png|jpeg|bmp|gif/)[0];return 'image/' + r
}function encodeData (data) {var str = '';if (typeof data === 'string') {str = data;} else {for (var i = 0; i < data.length; i++) {str += String.fromCharCode(data[i]);}}return base64.encode(str)
}/*** 获取图像区域隐含的像素数据* @param canvasId canvas标识* @param x 将要被提取的图像数据矩形区域的左上角 x 坐标* @param y 将要被提取的图像数据矩形区域的左上角 y 坐标* @param width 将要被提取的图像数据矩形区域的宽度* @param height 将要被提取的图像数据矩形区域的高度* @param done 完成回调*/
function getImageData (canvasId, x, y, width, height, done) {wx.canvasGetImageData({canvasId: canvasId,x: x,y: y,width: width,height: height,success: function success (res) {done(res);},fail: function fail (res) {done(null);console.error('canvasGetImageData error: ' + res);}});
}/*** 生成bmp格式图片* 按照规则生成图片响应头和响应体* @param oData 用来描述 canvas 区域隐含的像素数据 { data, width, height } = oData* @returns {*} base64字符串*/
function genBitmapImage (oData) {//// BITMAPFILEHEADER: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183374(v=vs.85).aspx// BITMAPINFOHEADER: http://msdn.microsoft.com/en-us/library/dd183376.aspx//var biWidth = oData.width;var biHeight    = oData.height;var biSizeImage = biWidth * biHeight * 3;var bfSize = biSizeImage + 54; // total header size = 54 bytes////  typedef struct tagBITMAPFILEHEADER {//      WORD bfType;//      DWORD bfSize;//      WORD bfReserved1;//      WORD bfReserved2;//      DWORD bfOffBits;//  } BITMAPFILEHEADER;//var BITMAPFILEHEADER = [// WORD bfType -- The file type signature; must be "BM"0x42, 0x4D,// DWORD bfSize -- The size, in bytes, of the bitmap filebfSize & 0xff, bfSize >> 8 & 0xff, bfSize >> 16 & 0xff, bfSize >> 24 & 0xff,// WORD bfReserved1 -- Reserved; must be zero0, 0,// WORD bfReserved2 -- Reserved; must be zero0, 0,// DWORD bfOffBits -- The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits.54, 0, 0, 0];////  typedef struct tagBITMAPINFOHEADER {//      DWORD biSize;//      LONG  biWidth;//      LONG  biHeight;//      WORD  biPlanes;//      WORD  biBitCount;//      DWORD biCompression;//      DWORD biSizeImage;//      LONG  biXPelsPerMeter;//      LONG  biYPelsPerMeter;//      DWORD biClrUsed;//      DWORD biClrImportant;//  } BITMAPINFOHEADER, *PBITMAPINFOHEADER;//var BITMAPINFOHEADER = [// DWORD biSize -- The number of bytes required by the structure40, 0, 0, 0,// LONG biWidth -- The width of the bitmap, in pixelsbiWidth & 0xff, biWidth >> 8 & 0xff, biWidth >> 16 & 0xff, biWidth >> 24 & 0xff,// LONG biHeight -- The height of the bitmap, in pixelsbiHeight & 0xff, biHeight >> 8 & 0xff, biHeight >> 16 & 0xff, biHeight >> 24 & 0xff,// WORD biPlanes -- The number of planes for the target device. This value must be set to 11, 0,// WORD biBitCount -- The number of bits-per-pixel, 24 bits-per-pixel -- the bitmap// has a maximum of 2^24 colors (16777216, Truecolor)24, 0,// DWORD biCompression -- The type of compression, BI_RGB (code 0) -- uncompressed0, 0, 0, 0,// DWORD biSizeImage -- The size, in bytes, of the image. This may be set to zero for BI_RGB bitmapsbiSizeImage & 0xff, biSizeImage >> 8 & 0xff, biSizeImage >> 16 & 0xff, biSizeImage >> 24 & 0xff,// LONG biXPelsPerMeter, unused0, 0, 0, 0,// LONG biYPelsPerMeter, unused0, 0, 0, 0,// DWORD biClrUsed, the number of color indexes of palette, unused0, 0, 0, 0,// DWORD biClrImportant, unused0, 0, 0, 0];var iPadding = (4 - ((biWidth * 3) % 4)) % 4;var aImgData = oData.data;var strPixelData = '';var biWidth4 = biWidth << 2;var y = biHeight;var fromCharCode = String.fromCharCode;do {var iOffsetY = biWidth4 * (y - 1);var strPixelRow = '';for (var x = 0; x < biWidth; x++) {var iOffsetX = x << 2;strPixelRow += fromCharCode(aImgData[iOffsetY + iOffsetX + 2]) +fromCharCode(aImgData[iOffsetY + iOffsetX + 1]) +fromCharCode(aImgData[iOffsetY + iOffsetX]);}for (var c = 0; c < iPadding; c++) {strPixelRow += String.fromCharCode(0);}strPixelData += strPixelRow;} while (--y)var strEncoded = encodeData(BITMAPFILEHEADER.concat(BITMAPINFOHEADER)) + encodeData(strPixelData);return strEncoded
}/*** 转换为图片base64* @param canvasId canvas标识* @param x 将要被提取的图像数据矩形区域的左上角 x 坐标* @param y 将要被提取的图像数据矩形区域的左上角 y 坐标* @param width 将要被提取的图像数据矩形区域的宽度* @param height 将要被提取的图像数据矩形区域的高度* @param type 转换图片类型* @param done 完成回调*/
function convertToImage (canvasId, x, y, width, height, type, done) {if ( done === void 0 ) done = function () {};if (type === undefined) { type = 'png'; }type = fixType(type);if (/bmp/.test(type)) {getImageData(canvasId, x, y, width, height, function (data) {var strData = genBitmapImage(data);isFunction(done) && done(makeURI(strData, 'image/' + type));});} else {console.error('暂不支持生成\'' + type + '\'类型的base64图片');}
}var CanvasToBase64 = {convertToImage: convertToImage,// convertToPNG: function (width, height, done) {//   return convertToImage(width, height, 'png', done)// },// convertToJPEG: function (width, height, done) {//   return convertToImage(width, height, 'jpeg', done)// },// convertToGIF: function (width, height, done) {//   return convertToImage(width, height, 'gif', done)// },convertToBMP: function (ref, done) {if ( ref === void 0 ) ref = {};var canvasId = ref.canvasId;var x = ref.x;var y = ref.y;var width = ref.width;var height = ref.height;if ( done === void 0 ) done = function () {};return convertToImage(canvasId, x, y, width, height, 'bmp', done)}
};function methods () {var self = this;var id = self.id;var deviceRadio = self.deviceRadio;var boundWidth = self.width; // 裁剪框默认宽度,即整个画布宽度var boundHeight = self.height; // 裁剪框默认高度,即整个画布高度var ref = self.cut;var x = ref.x; if ( x === void 0 ) x = 0;var y = ref.y; if ( y === void 0 ) y = 0;var width = ref.width; if ( width === void 0 ) width = boundWidth;var height = ref.height; if ( height === void 0 ) height = boundHeight;self.updateCanvas = function () {if (self.croperTarget) {//  画布绘制图片self.ctx.drawImage(self.croperTarget, self.imgLeft, self.imgTop, self.scaleWidth, self.scaleHeight);}isFunction(self.onBeforeDraw) && self.onBeforeDraw(self.ctx, self);self.setBoundStyle(); //    设置边界样式self.ctx.draw();return self};self.pushOrign = function (src) {self.src = src;isFunction(self.onBeforeImageLoad) && self.onBeforeImageLoad(self.ctx, self);wx.getImageInfo({src: src,success: function success (res) {var innerAspectRadio = res.width / res.height;self.croperTarget = res.path;if (innerAspectRadio < width / height) {self.rectX = x;self.baseWidth = width;self.baseHeight = width / innerAspectRadio;self.rectY = y - Math.abs((height - self.baseHeight) / 2);} else {self.rectY = y;self.baseWidth = height * innerAspectRadio;self.baseHeight = height;self.rectX = x - Math.abs((width - self.baseWidth) / 2);}self.imgLeft = self.rectX;self.imgTop = self.rectY;self.scaleWidth = self.baseWidth;self.scaleHeight = self.baseHeight;self.updateCanvas();isFunction(self.onImageLoad) && self.onImageLoad(self.ctx, self);}});self.update();return self};self.getCropperBase64 = function (done) {if ( done === void 0 ) done = function () {};CanvasToBase64.convertToBMP({canvasId: id,x: x,y: y,width: width,height: height}, done);};self.getCropperImage = function () {var args = [], len = arguments.length;while ( len-- ) args[ len ] = arguments[ len ];var ARG_TYPE = toString.call(args[0]);var fn = args[args.length - 1];switch (ARG_TYPE) {case '[object Object]':var ref = args[0];var quality = ref.quality; if ( quality === void 0 ) quality = 10;if (typeof (quality) !== 'number') {console.error(("quality:" + quality + " is invalid"));} else if (quality < 0 || quality > 10) {console.error("quality should be ranged in 0 ~ 10");}wx.canvasToTempFilePath({canvasId: id,x: x,y: y,width: width,height: height,destWidth: width * quality / (deviceRadio * 10),destHeight: height * quality / (deviceRadio * 10),success: function success (res) {isFunction(fn) && fn.call(self, res.tempFilePath);},fail: function fail (res) {isFunction(fn) && fn.call(self, null);}}); breakcase '[object Function]':wx.canvasToTempFilePath({canvasId: id,x: x,y: y,width: width,height: height,destWidth: width / deviceRadio,destHeight: height / deviceRadio,success: function success (res) {isFunction(fn) && fn.call(self, res.tempFilePath);},fail: function fail (res) {isFunction(fn) && fn.call(self, null);}}); break}return self};
}/*** 获取最新缩放值* @param oldScale 上一次触摸结束后的缩放值* @param oldDistance 上一次触摸结束后的双指距离* @param zoom 缩放系数* @param touch0 第一指touch对象* @param touch1 第二指touch对象* @returns {*}*/
var getNewScale = function (oldScale, oldDistance, zoom, touch0, touch1) {var xMove, yMove, newDistance;// 计算二指最新距离xMove = Math.round(touch1.x - touch0.x);yMove = Math.round(touch1.y - touch0.y);newDistance = Math.round(Math.sqrt(xMove * xMove + yMove * yMove));return oldScale + 0.001 * zoom * (newDistance - oldDistance)
};function update () {var self = this;if (!self.src) { return }self.__oneTouchStart = function (touch) {self.touchX0 = Math.round(touch.x);self.touchY0 = Math.round(touch.y);};self.__oneTouchMove = function (touch) {var xMove, yMove;// 计算单指移动的距离if (self.touchended) {return self.updateCanvas()}xMove = Math.round(touch.x - self.touchX0);yMove = Math.round(touch.y - self.touchY0);var imgLeft = Math.round(self.rectX + xMove);var imgTop = Math.round(self.rectY + yMove);self.outsideBound(imgLeft, imgTop);self.updateCanvas();};self.__twoTouchStart = function (touch0, touch1) {var xMove, yMove, oldDistance;self.touchX1 = Math.round(self.rectX + self.scaleWidth / 2);self.touchY1 = Math.round(self.rectY + self.scaleHeight / 2);// 计算两指距离xMove = Math.round(touch1.x - touch0.x);yMove = Math.round(touch1.y - touch0.y);oldDistance = Math.round(Math.sqrt(xMove * xMove + yMove * yMove));self.oldDistance = oldDistance;};self.__twoTouchMove = function (touch0, touch1) {var oldScale = self.oldScale;var oldDistance = self.oldDistance;var scale = self.scale;var zoom = self.zoom;self.newScale = getNewScale(oldScale, oldDistance, zoom, touch0, touch1);//  设定缩放范围self.newScale <= 1 && (self.newScale = 1);self.newScale >= scale && (self.newScale = scale);self.scaleWidth = Math.round(self.newScale * self.baseWidth);self.scaleHeight = Math.round(self.newScale * self.baseHeight);var imgLeft = Math.round(self.touchX1 - self.scaleWidth / 2);var imgTop = Math.round(self.touchY1 - self.scaleHeight / 2);self.outsideBound(imgLeft, imgTop);self.updateCanvas();};self.__xtouchEnd = function () {self.oldScale = self.newScale;self.rectX = self.imgLeft;self.rectY = self.imgTop;};
}var handle = {//  图片手势初始监测touchStart: function touchStart (e) {var self = this;var ref = e.touches;var touch0 = ref[0];var touch1 = ref[1];setTouchState(self, true, null, null);// 计算第一个触摸点的位置,并参照改点进行缩放self.__oneTouchStart(touch0);// 两指手势触发if (e.touches.length >= 2) {self.__twoTouchStart(touch0, touch1);}},//  图片手势动态缩放touchMove: function touchMove (e) {var self = this;var ref = e.touches;var touch0 = ref[0];var touch1 = ref[1];setTouchState(self, null, true);// 单指手势时触发if (e.touches.length === 1) {self.__oneTouchMove(touch0);}// 两指手势触发if (e.touches.length >= 2) {self.__twoTouchMove(touch0, touch1);}},touchEnd: function touchEnd (e) {var self = this;setTouchState(self, false, false, true);self.__xtouchEnd();}
};function cut () {var self = this;var boundWidth = self.width; // 裁剪框默认宽度,即整个画布宽度var boundHeight = self.height;// 裁剪框默认高度,即整个画布高度var ref = self.cut;var x = ref.x; if ( x === void 0 ) x = 0;var y = ref.y; if ( y === void 0 ) y = 0;var width = ref.width; if ( width === void 0 ) width = boundWidth;var height = ref.height; if ( height === void 0 ) height = boundHeight;/*** 设置边界* @param imgLeft 图片左上角横坐标值* @param imgTop 图片左上角纵坐标值*/self.outsideBound = function (imgLeft, imgTop) {self.imgLeft = imgLeft >= x? x: self.scaleWidth + imgLeft - x <= width? x + width - self.scaleWidth:    imgLeft;self.imgTop = imgTop >= y? y: self.scaleHeight + imgTop - y <= height? y + height - self.scaleHeight: imgTop;};/*** 设置边界样式* @param color    边界颜色*/self.setBoundStyle = function (ref) {if ( ref === void 0 ) ref = {};var color = ref.color; if ( color === void 0 ) color = '#04b00f';var mask = ref.mask; if ( mask === void 0 ) mask = 'rgba(0, 0, 0, 0.3)';var lineWidth = ref.lineWidth; if ( lineWidth === void 0 ) lineWidth = 1;var boundOption = [{start: { x: x - lineWidth, y: y + 10 - lineWidth },step1: { x: x - lineWidth, y: y - lineWidth },step2: { x: x + 10 - lineWidth, y: y - lineWidth }},{start: { x: x - lineWidth, y: y + height - 10 + lineWidth },step1: { x: x - lineWidth, y: y + height + lineWidth },step2: { x: x + 10 - lineWidth, y: y + height + lineWidth }},{start: { x: x + width - 10 + lineWidth, y: y - lineWidth },step1: { x: x + width + lineWidth, y: y - lineWidth },step2: { x: x + width + lineWidth, y: y + 10 - lineWidth }},{start: { x: x + width + lineWidth, y: y + height - 10 + lineWidth },step1: { x: x + width + lineWidth, y: y + height + lineWidth },step2: { x: x + width - 10 + lineWidth, y: y + height + lineWidth }}];// 绘制半透明层self.ctx.beginPath();self.ctx.setFillStyle(mask);self.ctx.fillRect(0, 0, x, boundHeight);self.ctx.fillRect(x, 0, width, y);self.ctx.fillRect(x, y + height, width, boundHeight - y - height);self.ctx.fillRect(x + width, 0, boundWidth - x - width, boundHeight);self.ctx.fill();boundOption.forEach(function (op) {self.ctx.beginPath();self.ctx.setStrokeStyle(color);self.ctx.setLineWidth(lineWidth);self.ctx.moveTo(op.start.x, op.start.y);self.ctx.lineTo(op.step1.x, op.step1.y);self.ctx.lineTo(op.step2.x, op.step2.y);self.ctx.stroke();});};
}var version = "1.2.0";var WeCropper = function WeCropper (params) {var self = this;var _default = {};validator(self, DEFAULT);Object.keys(DEFAULT).forEach(function (key) {_default[key] = DEFAULT[key].default;});Object.assign(self, _default, params);self.prepare();self.attachPage();self.createCtx();self.observer();self.cutt();self.methods();self.init();self.update();return self
};WeCropper.prototype.init = function init () {var self = this;var src = self.src;self.version = version;typeof self.onReady === 'function' && self.onReady(self.ctx, self);if (src) {self.pushOrign(src);}setTouchState(self, false, false, false);self.oldScale = 1;self.newScale = 1;return self
};Object.assign(WeCropper.prototype, handle);WeCropper.prototype.prepare = prepare;
WeCropper.prototype.observer = observer;
WeCropper.prototype.methods = methods;
WeCropper.prototype.cutt = cut;
WeCropper.prototype.update = update;return WeCropper;})));

avater.js


const WeCropper = require('../../../utils/we-cropper.js')const device = wx.getSystemInfoSync()
const width = device.windowWidth
const height = device.windowHeight - 100;const app = getApp();
Page({/*** 页面的初始数据*/data: {request: request,userInfo:app.globalData.userInfo,weChatConfig:app.globalData.weChatConfig,avator: app.globalData.weChatConfig.filedServiceIp+app.globalData.userInfo.avatar,noIpAvater:app.globalData.userInfo.avatar,modalName: null,cropperOpt: {  //基础设置 id: 'cropper',width,height,scale: 2.5,zoom: 8,cut: {x: (width - 300) / 2,y: (height - 300) / 2,width: 300,height: 300}},},//点击插入图片  初始化插件addImage: function (event) {const {cropperOpt} = this.datathis.wecropper = new WeCropper(cropperOpt).on('ready', (ctx) => { }).on('beforeImageLoad', (ctx) => {console.log(`before picture loaded, i can do something`)console.log(`current canvas context:`, ctx)}).on('imageLoad', (ctx) => {console.log(`picture loaded`)console.log(`current canvas context:`, ctx)wx.hideToast()}).on('beforeDraw', (ctx, instance) => {console.log(`before canvas draw,i can do something`)console.log(`current canvas context:`, ctx)}).updateCanvas();//调用微信的选择图片接口this.chooseimg()},touchStart(e) {this.wecropper.touchStart(e)},touchMove(e) {this.wecropper.touchMove(e)},touchEnd(e) {this.wecropper.touchEnd(e)},
//调用微信的选择图片接口chooseimg(){let _this = this;wx.chooseImage({count: 1,sizeType: ['compressed'],sourceType: ['album', 'camera'],success: function (res1) {//显示剪裁区域_this.setData({cutImage: true,modalName: 'cropperModal'});_this.wecropper.pushOrign(res1.tempFilePaths[0]);}});},// 获取裁剪后的图片getCropperImage() {var that = this;that.wecropper.getCropperImage((src) => {console.log(src)if (src) {//隐藏剪裁区域that.setData({cutImage: false,modalName: null})//遮罩层wx.showLoading({title: '头像上传中',})that._uploadImage(src); }})},//上传图片到后台服务器_uploadImage: function (tempFilePath) {let _this = this;let headers = {'Content-Type': 'application/json;charset=utf-8','X-Access-weChatId': app.globalData.weChatId,'X-Access-token': app.globalData.token,}wx.uploadFile({filePath: tempFilePath,name: 'file',url: app.url.upload,//上传文件urlheader:headers,success: function (res) {res = JSON.parse(res.data);wx.hideLoading({success: () => {if (res.code === 200) {//上传成功后处理逻辑  显示头像_this.setData({noIpAvater:res.result[0].url,avator:app.globalData.weChatConfig.filedServiceIp+res.result[0].url})} else {message.alert("服务器错误,稍后重试!")}},});}});},})

avater.wxml

<!-- 裁剪对话框 begin-->
<view class="cu-modal bottom-modal {{modalName=='cropperModal'?'show':''}}"><view class="cu-dialog" wx:if="{{cutImage}}"><!-- 裁剪区域   重要 --><template name="we-cropper"><canvas class="cropper" disable-scroll="true" bindtouchstart="touchStart" bindtouchmove="touchMove" bindtouchend="touchEnd" style="width:{{width}}px;height:{{height}}px;" canvas-id="{{id}}"></canvas></template><!-- 按钮,无所谓的东西 --><view class="cropper-wrapper {{cutImage}}"><template is="we-cropper" data="{{...cropperOpt}}" /><view class='caijananniu'><view class='cxuan1 bg-blue' bindtap="chooseimg">重新选择</view><view class='cxuan2 bg-blue' bindtap="getCropperImage">确定选择</view></view></view><!--end 用户自动截取正方形照片  --></view>
</view>
<!-- 裁剪对话框 end-->
<!-- 下面的不重要   页面布局-->
<form><view class="cu-bar bg-white margin-top-sm"><view class="action">头像</view></view><view class="cu-form-group"><view class="grid col-4 grid-square flex-sub"><view class="bg-img"  bindtap="ViewImage" wx:if="{{avator!=''}}"><image src='{{avator}}' mode='aspectFill'></image><view class="cu-tag bg-red" catchtap="DelImg" ><text class="cuIcon-close"></text></view></view><view class="solids" bindtap="addImage" ><text class="cuIcon-cameraadd"></text></view></view></view><view class="cu-bar btn-group"><button bindtap="submit"  class="cu-btn bg-blue shadow-blue round lg">保存</button></view>
</form>

avater.wxss

/* 裁剪头像 */
.caijananniu{margin-top: 40rpx;width: 80%;margin-left: 10%;margin-bottom: 10%;height: 50rpx;/* background-color: #EEEEEE; *//* border-radius: 50rpx; */
}
.cxuan1{width: 200rpx;height: 60rpx;float: left;color:#fff;border-radius: 50rpx;font-size: 24rpx;text-align: center;line-height: 60rpx;
}
.cxuan2{width: 200rpx;height: 60rpx;float: right;color: #fff;border-radius: 50rpx;font-size: 24rpx;text-align: center;line-height: 60rpx;
}

微信小程序上传头像先裁剪图片后上传相关推荐

  1. 微信小程序选择手机相册里的图片并上传到页面

    微信小程序获取手机相册里的图片并传到页面上 index.wxml代码 // 相册中的照片 <button bindtap='handleChooseAlbum'>选中图片</butt ...

  2. 前端插件库之vue3使用vue3-cropper上传头像(裁剪图片、上传)

    一.vue3-cropper官网 vue3 github的readme 非vue3可以参考这份demo 二.使用 1.命令行安装 npm install vue3-cropper -S 2.组件内使用 ...

  3. 微信提交表单到服务器,微信小程序页面表单如何跟图片一起上传服务器

    拆开写. 表单提交是 wx.request 上传图片是 wx.uploadFile 你需要写一个通用图片上传接口,上传图片后台返回图片的url.这个通用接口在任何需要提交图片的表单都可以用到. 添加图 ...

  4. 微信小程序怎么绑定服务器,微信小程序页面表单如何跟图片一起上传服务器

    拆开写. 表单提交是 wx.request 上传图片是 wx.uploadFile 你需要写一个通用图片上传接口,上传图片后台返回图片的url.这个通用接口在任何需要提交图片的表单都可以用到. 添加图 ...

  5. 微信小程序 实现点击按钮选择图片后显示图片并且可以预览该图片

    wxml: js: data里 要写图片这个变量. 点击选择图片 预览图片 给 image加事件,利用e.target.dataset.src 获取当前图片地址,赋值给wx.previewImage的 ...

  6. 微信小程序开发之从相册获取图片 使用相机拍照 本地图片上传

    今天遇到微信小程序的用户头像设置功能,做笔记. 先上gif: 再上代码: 小demo,代码很简单. 1.index.wxml <!--index.wxml--> <button st ...

  7. 微信小程序最新获取头像和昵称的方法 直接用!

    调整背景 微信小程序获取用户头像和昵称一个开放接口是wx.getUserInfo,2021年4月5日被废弃,原因是很多开发者在打开小程序时就通过组件方式唤起getUserInfo弹窗,如果用户点击拒绝 ...

  8. 微信小程序后台获取签名,裁剪并上传图片至阿里云oss

    微信小程序后台获取签名,裁剪并上传图片至阿里云oss 首先,设置微信开发者工具,选择校验合法域名,防止本地可应使用,线上报错问题: 其次,在微信公众平台–小程序开发,设置合法域名 Oss设置–> ...

  9. 微信小程序:纯头像微信小程序源码下载,多分类头像自动采集无需服务器和域名

    这是一款纯头像的微信小程序 除了头像没有其它功能 头像有多种分类,功能简洁实用 支持流量主模式收益 另外该小程序无需服务器和域名 该小程序安装方法如下: 解压域名以后然后使用微信开发者工具打开该小程序 ...

最新文章

  1. mysql.info文件是什么_info 数据库
  2. [转载]jquery 消息插件--仿QQ消息弹出提醒
  3. cython linux so,更改Cython的.so文件命名规则
  4. mysql重置密码以及授予权限
  5. 人的幸福感取决于什么
  6. 动态规划法实现最优二叉搜索树
  7. List集合之Vector
  8. CSS 命名 BEM 。线上样式不对。已经更新。El-input__suffix偏上。字体样式 苹方-简 PingFangSC-Regular。echarts字体。
  9. Word转成PDF后目录出现未定义书签是怎么回事
  10. redis cluster master failover问题
  11. 中科大计算机博士毕业条件,惊呆!中科大博士毕业6年后再考医学专科,他这样回应……...
  12. Ribbon负载均衡及Feign消费者调用服务
  13. c语言课程设计--贪吃蛇
  14. 小程序·云开发实战:SCRM社交化客户管理小程序
  15. SSM整合之CRUD增删改查案例(非ajax版)
  16. 数据库第四次实验报告
  17. Google Play In-app Billing
  18. C语言之关系运算符与逻辑运算符小结
  19. 网站漏洞修复对DiscuzX3.4论坛总是被篡改页面
  20. 第十四章 使用SQL Shell界面(一)

热门文章

  1. db2离线备份_DB2离线全备份与在线备份.pdf
  2. Java实现视频分类 【内链】
  3. ubuntu中用python获取GPU温度。push给falcon-agent。
  4. shell comand
  5. STM32CubeMX 下载直链
  6. 易语言多线程大漠《剑侠情缘》实现一键批量开启游戏
  7. 室内污染,美博士帮你搞定“甲醛君”
  8. 遇到不认识的字怎么办?
  9. ESFP型人格的性格缺陷和心理问题分析
  10. JProfiler之java剖析工具