微信小程序 canvas 圖片裁剪 旋轉 插件we-cropper

微信小程序 canvas 圖片裁剪 旋轉 插件we-cropper

比較推薦插件: https://github.com/wx-plugin/image-cropper

另一個插件
git:https://github.com/we-plugin/we-cropper
文檔: https://we-plugin.github.io/we-cropper/#/

此插件只提供裁剪等功能,所以我在此插件上修改了一部分代碼妻味,添加了圖片旋轉功能碎连,和圖片放大功能

圖片裁剪

旋轉后的圖片

下載解壓:
文件目錄


文件目錄

如果在所需要裁剪的頁面通過彈出窗口來使用這個功能肌括,ios真機上面遇到一個問題:
第一次進入頁面,頁面無法上拉,原因查不出來我磁,所以只好將裁剪的功能寫在一個新頁面中(而且本小程序需要裁剪的頁面很多孽文,所以將裁剪寫成一個新的頁面比較好)

pages里面新建目錄cutFace
在index.wxml中代碼如下:

// 引入裁剪組件
<import src="/commpents/we-cropper/we-cropper.wxml"/>  



<view class='cut-img-page'>
  <view class='cropper-wrapper-bg'>
    <view style='height: 1rpx;'></view>
    <view class="cropper-wrapper" style='margin-top: {{marTop}}px'>
      <template is="we-cropper" data="{{...cropperOpt}}"/>
      <view class="getCropperImage">
        <view bindtap='cancleCropper'>取消</view><view bindtap='rotateImg'><image src='/images/rotate.png' class='rotate'></image></view><view  bindtap="getCropperImage">確定</view>
      </view>
    </view>
  </view>
</view>

在index.wxss中代碼如下:

page {
  min-height: auto;
  height: auto;
}

.cropper-wrapper {
  background: #fff;
}

.getCropperImage {
  text-align: center;
  font-size: 32rpx;
  display: flex;
  justify-content: space-around;
  position: absolute;
  bottom: 0;
  width: 750rpx;
  padding: 40 0rpx;
  background: #000;
  z-index: 9999;
  color: #fff;
  align-items: center;
}

.getCropperImage > view {
  padding: 0 40rpx;
  height: 60px;
  line-height: 60px;
}

.cropper-wrapper-bg {
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  background: rgb(0, 0, 0);
  z-index: 22;
}

.rotate {
  width: 44rpx;
  height: 44rpx;
}

在index.js中代碼如下:

// pages/cutFace/index.js

// pages/cutFace/index.js

const WeCropper = require('../../we-cropper/we-cropper.js')

const device = wx.getSystemInfoSync() // 獲取設備信息
const width = device.windowWidth // 示例為一個與屏幕等寬的正方形裁剪框
const system = device.system;
let height = device.windowHeight - 100




Page({

  /**
   * 頁面的初始數(shù)據(jù)
   */
  data: {
    rotateI: 0, //旋轉默認角度
    cropperOpt: {
      id: 'cropper',
      rotateI: 0,//旋轉默認角度
      tranlateX: width / 2,    //定義canvas 的原點
      tranlateY: height / 2,  //定義canvas 的原點
      width,  // 畫布寬度
      height, // 畫布高度
      scale: 2.5, // 最大縮放倍數(shù)
      zoom: 8, // 縮放系數(shù),
      cut: {
        x: -width / 2,  // 裁剪框的坐標
        y: -(height - (width / 1.4)) / 2, // 裁剪框的坐標
        width: width, //裁剪框的大小
        height: width / 1.4
      }
    },
    chooseImg: false,
    imgSrc: '',
    marTop: 40
  },


  onLoad: function (options) {
    const self = this;
    //兼容可不寫
    const system = device.system;
    if (system.indexOf('iOS') != -1) {
      this.setData({
        ios: true
      })
    };
    if (system.indexOf('iOS') != -1) {
      this.setData({
        marTop: 45
      })
    } else {
      this.setData({
        marTop: 45
      })
    };
    if (device.model.indexOf("iPhone X") != -1) {
      this.setData({
        height: wx.getStorageSync('height') * 2 + 50,
        marTop: 80
      })
    };
    // 判斷來自哪個圖片的裁剪  身份證、榮譽證書夺艰、營業(yè)證書等
    this.setData({
      cuttype: options.cuttype
    })
    //裁剪 插件配置
    const { cropperOpt } = this.data;
    new WeCropper(cropperOpt)
      .on('ready', (ctx) => {
        self.wecropper.updateCanvas(this.data.rotateI)
      })
      .on('beforeImageLoad', (ctx) => {
        wx.showToast({
          title: '上傳中',
          icon: 'loading',
          duration: 20000
        })
      })
      .on('imageLoad', (ctx) => {
        wx.hideToast()
      })
    this.chooseImg()

  },

  chooseImg() {
    const self = this;
    wx.chooseImage({
      count: 1, // 默認9
      sizeType: ['original', 'compressed'], // 可以指定是原圖還是壓縮圖芋哭,默認二者都有
      sourceType: ['album', 'camera'], // 可以指定來源是相冊還是相機,默認二者都有
      success(res) {
        const src = res.tempFilePaths[0]
        if (src) {
        // 將圖片參數(shù)傳遞給插件
          self.wecropper.pushOrign(src)
          self.setData({
            chooseImg: true,
            imgSrc: src,
            rotateI: 0
          })
        };
        wx.hideToast()
      },
      fail(res) {
        wx.hideToast();
        wx.navigateBack()
      }
    })
  },

  touchStart(e) {
    this.wecropper.touchStart(e)
  },
  touchMove(e) {
    this.wecropper.touchMove(e)
  },
  touchEnd(e) {
    this.wecropper.touchEnd(e)
  },
  // 獲取裁剪后的圖片
  getCropperImage() {
    let that = this;
    if (this.data.chooseImg) {
      this.wecropper.getCropperImage((src) => {
        //獲取上個頁面的參數(shù)
        let pages = getCurrentPages();
        //prevPage 相當于上個頁面的this郁副,可以通過setData修改上個頁面參數(shù)執(zhí)行上個頁面的方法等
        let prevPage = pages[pages.length - 2]
        if (src) {
          // 身份證
          if (this.data.cuttype == 1) {
            prevPage.setData({
              IDCard: src,
              cuttype: this.data.cuttype
            })
          //榮譽證書
          } else if (this.data.cuttype == 2) {
            prevPage.setData({
              hornerCard: src,
              cuttype: this.data.cuttype
            })
          } else if (this.data.cuttype == 3) {
            prevPage.setData({
              licenceCard: src,
              cuttype: this.data.cuttype
            })
          } else if (this.data.cuttype == 4) {
            prevPage.setData({
              certificationCard: src,
              cuttype: this.data.cuttype
            })
          };
          wx.navigateBack()
        } else {
          wx.hideToast()
          wx.showToast({
            title: '獲取圖片地址失敗减牺,請稍后再試!',
          })
        }
      })
    } else {
      wx.showToast({
        title: '您還沒選擇圖片存谎!',
        icon: 'none'
      })
    }
  },
  cancleCropper() {
    wx.hideToast()
    wx.navigateBack()
  },

  // 圖片旋轉
  rotateImg() {
    const self = this;
    let rotateI = this.data.rotateI + 1;
    this.setData({
      rotateI: rotateI
    })
    // 將旋轉的角度傳遞給插件
    self.wecropper.updateCanvas(rotateI)
  }
})

上個頁面(進入裁剪的那個頁面)處理


image.png

在上個頁面的數(shù)據(jù)處理
在data里面聲明:

data: {
    IDCard: '', //身份證
    cuttype: null, // 圖片類型
    licenceCard: '',// 機構證書
    certificationCard: ''//資質證書
}

點擊跳轉到裁剪頁面時所傳遞的參數(shù)

//證書
getLicence() {
    wx.navigateTo({
      url: '/pages/cutFace/index?cuttype=3',
    })
  },
  //證書
  getCertification() {
    wx.navigateTo({
      url: '/pages/cutFace/index?cuttype=4',
    })
  },
  // 身份證
  IDCardFont() {
    wx.navigateTo({
      url: '/pages/cutFace/index?cuttype=1',
    })
  },

從裁剪頁面退回時的判斷

onShow() {
    if (this.data.cuttype == 1) {
      this.upLoadImg(this.data.IDCard)
    };
    if (this.data.cuttype == 3) {
      this.upLoadImg(this.data.licenceCard)
    };
    if (this.data.cuttype == 4) {
      this.upLoadImg(this.data.certificationCard)
    };
  },
//圖片上傳
upLoadImg(path) {
    let that = this;
    wx.uploadFile({
      url: wx.$.host + 'api/uploadImage',
      filePath: path,
      name: 'files',
      success: function (res) {
        if (res.data) {
          const lecturerInfo = that.data.lecturerInfo
          let data = JSON.parse(res.data);
          if (that.data.cuttype == 1) {
            lecturerInfo.hand_card_one = data.path
          };
          if (that.data.cuttype == 3) {
            lecturerInfo.licence_pic = data.path
          };
          if (that.data.cuttype == 4) {
            lecturerInfo.certification_pic = data.path
          };
          app.data.lecturerInfo.data.lecturer = lecturerInfo
          that.setData({
            lecturerInfo: lecturerInfo,
            cuttype: null//上傳成功后設置為空
          })
        }
      }
    })
  },

下面是插件源碼的修改(能力有限拔疚,修改之后勉強能用吧,反正自己是不怎么滿意)
在插件的method函數(shù)中修改

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 (rotateI) {
      self.rotateI = rotateI ? rotateI : self.rotateI
      if (self.croperTarget) {
        //  畫布繪制圖片
        self.ctx.save()
        self.ctx.translate(self.tranlateX, self.tranlateY)
        self.ctx.rotate(self.rotateI * 90 * Math.PI / 180)
        self.ctx.drawImage(self.croperTarget, self.imgLeft, self.imgTop, self.scaleWidth, self.scaleHeight);
      }
      isFunction(self.onBeforeDraw) && self.onBeforeDraw(self.ctx, self);
      self.ctx.restore()
      self.setBoundStyle(); //  設置邊界樣式
      self.ctx.draw();
      return self
    };

    self.pushOrign = function (src) {
      self.rotateI = 0
      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.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
    };
  }

對照圖


image.png

image.png

update 函數(shù)


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);
      if (self.rotateI % 4 == 0) {
        var imgLeft = Math.round(self.rectX + xMove);
        var imgTop = Math.round(self.rectY + yMove);
      } else if (self.rotateI % 4 == 1) {
        var imgLeft = Math.round(self.rectX + yMove);
        var imgTop = Math.round(self.rectY - xMove);
      } else if (self.rotateI % 4 == 2) {
        var imgLeft = Math.round(self.rectX - xMove);
        var imgTop = Math.round(self.rectY - yMove);
      } else if (self.rotateI % 4 == 3) {
        var imgLeft = Math.round(self.rectX - yMove);
        var imgTop = Math.round(self.rectY + xMove);
      }
      self.outsideBound(imgLeft, imgTop);
      self.updateCanvas();
    };

對照圖


image.png

outsideBound函數(shù)

self.outsideBound = function (imgLeft, imgTop) {

      self.imgLeft = imgLeft
      self.imgTop = 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;
      // console.log(imgLeft)
    };

對照圖


image.png

__twoTouchMove函數(shù),原插件只提供了圖片放大,沒有圖片縮小功能固以,這里小改了一下

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 <= 0.2 && (self.newScale = oldScale - 0.001 * zoom * (newDistance - oldDistance));
      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();
    };

對照圖


image.png

選擇圖片不居中時需要微調墩虹,微調地方


image.png

最后附上demo的git地址: https://github.com/UprightCedar/we-cropper-demo-cut-img

有更好的方法請告訴我

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市憨琳,隨后出現(xiàn)的幾起案子诫钓,更是在濱河造成了極大的恐慌,老刑警劉巖篙螟,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菌湃,死亡現(xiàn)場離奇詭異,居然都是意外死亡遍略,警方通過查閱死者的電腦和手機惧所,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绪杏,“玉大人下愈,你說我怎么就攤上這事±倬茫” “怎么了势似?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長僧著。 經(jīng)常有香客問我履因,道長,這世上最難降的妖魔是什么盹愚? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任栅迄,我火速辦了婚禮,結果婚禮上皆怕,老公的妹妹穿的比我還像新娘毅舆。我一直安慰自己西篓,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布朗兵。 她就那樣靜靜地躺著污淋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪余掖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天礁鲁,我揣著相機與錄音盐欺,去河邊找鬼。 笑死仅醇,一個胖子當著我的面吹牛冗美,可吹牛的內容都是我干的。 我是一名探鬼主播析二,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼粉洼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了叶摄?” 一聲冷哼從身側響起属韧,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛤吓,沒想到半個月后宵喂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡会傲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年锅棕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淌山。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡裸燎,死狀恐怖,靈堂內的尸體忽然破棺而出泼疑,到底是詐尸還是另有隱情德绿,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布王浴,位于F島的核電站脆炎,受9級特大地震影響,放射性物質發(fā)生泄漏氓辣。R本人自食惡果不足惜秒裕,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钞啸。 院中可真熱鬧几蜻,春花似錦喇潘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至弧烤,卻和暖如春忱屑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背暇昂。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工莺戒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人急波。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓从铲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親澄暮。 傳聞我的和親對象是個殘疾皇子名段,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內容