微信小程序上传头像先裁剪图片后上传

我是使用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-character
    var 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/1020396
    var 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-btoa
    var 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 browser
        root.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 file
    bfSize & 0xff, bfSize >> 8 & 0xff, bfSize >> 16 & 0xff, bfSize >> 24 & 0xff,
    // WORD bfReserved1 -- Reserved; must be zero
    0, 0,
    // WORD bfReserved2 -- Reserved; must be zero
    0, 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 structure
    40, 0, 0, 0,
    // LONG biWidth -- The width of the bitmap, in pixels
    biWidth & 0xff, biWidth >> 8 & 0xff, biWidth >> 16 & 0xff, biWidth >> 24 & 0xff,
    // LONG biHeight -- The height of the bitmap, in pixels
    biHeight & 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 1
    1, 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) -- uncompressed
    0, 0, 0, 0,
    // DWORD biSizeImage -- The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps
    biSizeImage & 0xff, biSizeImage >> 8 & 0xff, biSizeImage >> 16 & 0xff, biSizeImage >> 24 & 0xff,
    // LONG biXPelsPerMeter, unused
    0, 0, 0, 0,
    // LONG biYPelsPerMeter, unused
    0, 0, 0, 0,
    // DWORD biClrUsed, the number of color indexes of palette, unused
    0, 0, 0, 0,
    // DWORD biClrImportant, unused
    0, 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);
          }
        }); break
      case '[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.data
    this.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,//上传文件url
      header: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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值