微信小程序图片(头像)裁剪工具we-cropper含2d版-完整版

本教程基于 we-cropper v1.4.0,非本人同意禁止任何形的转载

we-cropper官方下载链接

效果图

android 仿照微信裁剪头像_上传

一、非2d版

1.组件文件we-cropper.wxml
<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>
  <canvas
    class="cropper"
    disable-scroll="true"
    style="position: fixed; top: -{{width * pixelRatio}}px; left: -{{height * pixelRatio}}px; width:{{width * pixelRatio}}px;height:{{height * pixelRatio}}px;"
    canvas-id="{{targetId}}">
  </canvas>
</template>
2.调用页面imgcropper.wxml(2b版一样)
<import src="/components/we-cropper/we-cropper.wxml" />

<view class="page imgDisposeBlock">
  <view class='titleBar'>
    <text>头像裁剪工具</text>
    <text class='backBtn' decode='true' bindtap="cancleCropper"> < 返回 </text>
  </view>
  <view class='imgDisposeArea'>
    <template is="we-cropper" data="{{...cropperOpt}}" />
  </view> 
  <view class='imgDisposeControlLine'>
    <button class='editBtn reelectBtn' bindtap="chooseMedia">选择图片</button>
    <!-- <button class='editBtn reelectBtn' open-type="chooseAvatar" bindchooseavatar="onChooseAvatar">选择图片</button> -->
    <view class='editBtn uploadBtn editPerfectBtn' bindtap="getCropperImage">上传</view>
  </view>
</view>
3.css文件imgcropper.wxss(2d版一样)
/* pages/user/edit/imgcropper.wxss */
page {
  width: 100%;
  height: 100%;
}

.titleBar {
  width: 100%;
  height: 128rpx;
  /* position: fixed; */
  padding-left: 15rpx;
  text-align: left;
  line-height: 128rpx;
  font-size: 36rpx;
  color: #333;
  background: linear-gradient(to right, #EEE 0%, #FFF 100%);
}

.imgDisposeBlock {
  position: absolute;
  z-index: 99;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  /* background: rgb(0, 0, 0); */
  background: rgb(255, 255, 255);
  overflow: hidden;
}

.imgDisposeArea {
  width: 100%;
  overflow: hidden;
  background: #FFF;
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAAAflBMVEXDw8PFxcX6+vrIyMi/v7/09PTNzM3ExMTCwsLv7+/ExMT+/v76+vq7u7v+/v76+frDw8PFxcW+vr7BwcH4+Pj8/PzHx8fKysrx8fHv7+/Ly8vAwMC9vb36+vr29va7u7vOzs7z8/PQ0NC4uLi5ubnr6+vq6upKc1GSAAAAEnRSTlPg4ODg4ODg4PDq6uDM8PDgzMzoyyPwAAABkklEQVQ4y82Va5OCIBSG8VZ7aW8eDoIFKJq1//8PLiAkW81un5qeST+888xxpPeM5POZzLxmWZ7n9laVBQkUZWUTy8uGvNWBHQCl1N6w7WLWCKTgeSLkNjG/t9jRW8XrE6mgDpSJqKgnFbeIzGPaOsLBzJnKSNF1HbfUW4YMwD+64U3TNfbqBQsTS1JS4Whh6APNVoLwgByt7+hXpEKPmkQd6XBCpRCZYTxmxE6cQXkKR4ZzxmgTs4Lk4EnFniH4g1IPIpbXxcuXydjF/9GjYeBA6GJGFlEuImB4ipvIHXbiahv4bkXbSosYxpjtBiFn1kspej1pdEywlEJNqLX9mSppD4QCKLk0vHU1A0v5S4TQHn62Cow9jKjAo5N19TtzJo4KmXIVPgheR6hxC2OvdLm0VujFZLmYO0eNqCuy3s8M++MucNwPM3I4Za/k42C0BSdaRxrjMmP0hDwtha9U+q4jZcvOcMtcs9BHk4jxoGJx/xXx7mJ/VXwKoW7rvya+k81L5qnWJLLKqvAhyYsQPX/9AIo9ekfsnrQOAAAAAElFTkSuQmCC');
}

.imgDisposeControlLine {
  width: 100%;
  height: 240rpx;
  position: relative;
  background: #fff;
}

.editBtn {
  position: absolute;
  top: 70rpx;
  transform: translateY(-50%);
  width: 260rpx;
  height: 80rpx;
  background: #EEE;
  font-size: 26rpx;
  text-align: center;
  line-height: 80rpx;
  color: #333;
  border-radius: 6rpx;
}

.uploadBtn {
  background: #79958b;
  color: #FFF;
}

.backBtn {
  position: absolute;
  transform: translateY(-50%);
  /* width:260rpx; */
  padding: 0 25rpx 0 25rpx;
  height: 60rpx;
  background: #DDD;
  font-size: 26rpx;
  text-align: center;
  line-height: 60rpx;
  color: #333;
  border-radius: 6rpx;
  top: 62rpx;
  right: 25rpx;
}

.editBtn.reelectBtn {
  left: 10%;
}


.editBtn.editPerfectBtn {
  right: 10%;
}

.finalCanvasClass {
  position: absolute;
  top: -600%;
  left: 0;
  z-index: 15;
  transform-origin: left top;
  transform: scale(0.25);
}

.letterCanvasClass {
  position: absolute;
  top: -9999rpx;
  left: 0;
  transform-origin: left top;
  transform: scale(0.25);
  z-index: -1;
}

.letterSrcClass {
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;

}


/* 截图canvas放大 真机上不行*/
/* .cropper{
  transform-origin: left top;
  transform: scale(0.25);
} */

.cropper {
  /* background-color: red; */
  /* background-color: rgba(0, 0, 0, 0.2); */
}

.shadowBlock {
  width: 100%;
  height: 100%;
  background: #f7f7f7;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 99;
}
4.js文件imgcropper.js
// pages/user/edit/imgcropper.js
import WeCropper from '../../../components/we-cropper/we-cropper.min.js';
const device = wx.getSystemInfoSync(); // 获取设备信息
const width = device.windowWidth; // 示例为一个与屏幕等宽的正方形裁剪框
const devicePixelRatio = device.pixelRatio;
const height = device.windowHeight - Math.round(368 / devicePixelRatio) - 70; //70是手机底部圆角区
const fs = width / 750 * 2;
const imgPix = 250;
Page({

  /**
   * 页面的初始数据
   */
  data: {
    showCropper: !1,
    imgSrc: '',//确定裁剪后的图片
    currentImage: '',
    cropperOpt: {
      id: 'cropper',
      targetId: 'targetCropper',
      pixelRatio: devicePixelRatio,
      width: width, // 画布宽度
      height: height, // 画布高度
      scale: 2.5, // 最大缩放倍数
      zoom: 8, // 缩放系数
      cut: {
        x: (width - imgPix) / 2, // 裁剪框x轴起点(width * fs * 0.128) / 2
        y: (height * 0.5 - imgPix * 0.5), // 裁剪框y轴期起点
        width: imgPix, // 裁剪框宽度
        height: imgPix// 裁剪框高度
      }
    },
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    const that = this;
    const avatarUrl = options.avatarUrl;
    const nickName = options.nickName;
    this.setData({
      avatarUrl: avatarUrl,
      nickName: nickName
    });
    //剪裁组件初始化
    const { cropperOpt } = this.data;
     this.myCropper = new WeCropper(cropperOpt)
       .on('ready', (ctx) => {
         console.log('wecropper is ready for work!');
       })
       .on('beforeImageLoad', (ctx) => {
         wx.showToast({
           title: '加载中',
           icon: 'loading',
           duration: 20000
         })
       })
       .on('imageLoad', (ctx) => {
         wx.hideToast();
       });
    //刷新画面
     this.myCropper.updateCanvas();
    //剪裁组件初始化end
    // 加载原有的图片
    if (avatarUrl) {
      wx.getImageInfo({
        src: avatarUrl,
        success(res) {
          // console.log(res);
          that.myCropper.pushOrign(res.path);
        }
      });
    }
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {

  },
  onChooseAvatar(e) {
    let that = this;
    const { avatarUrl } = e.detail
    // console.log(avatarUrl);
    that.setData({
      currentImage: avatarUrl
    });
    that.myCropper.pushOrign(avatarUrl);
  },
  chooseMedia(e) {
    let that = this;
    wx.chooseMedia({
      count: 1,
      mediaType: ['image'],
      sourceType: ['album', 'camera'],
      maxDuration: 30,
      camera: 'back',
      success(res) {
        // console.log(res.tempFiles[0],e);
        let { tempFilePath, fileType } = res.tempFiles[0];
        that.setData({
          currentImage: tempFilePath
        });
        if (fileType == 'image') {
          that.myCropper.pushOrign(tempFilePath);
        }
        else {
          getApp().core.showModal({
            title: '提示',
            content: "只能选择图片",
            showCancel: false
          });
        }
      },
      fail(res) {
        // console.log(res);
        let { errMsg } = res;
        if (errMsg != "chooseMedia:fail cancel") {
          getApp().core.showModal({
            title: '选择图片时出错',
            content: res.errMsg,
            showCancel: false
          });
        }
      }
    });
  },
  touchStart(e) {
    this.myCropper.touchStart(e)
  },
  touchMove(e) {
    this.myCropper.touchMove(e)
  },
  touchEnd(e) {
    this.myCropper.touchEnd(e)
  },

  getCropperImage() {
    let that = this;
    if (!that.data.currentImage) {
      getApp().core.showModal({
        title: '提示',
        content: '请先选择图片',
        showCancel: false,
        success: res => { }
      });
      return false;
    }
    wx.showToast({
      title: '上传中',
      icon: 'loading',
      duration: 20000
    });
    // 如果有需要两层画布处理模糊,实际画的是放大的那个画布
    this.myCropper.getCropperImage((src) => {
      if (src) {
        that.setData({
          imgSrc: src
        });
        console.log(src);
        // wx.previewImage({
        //   current: '', // 当前显示图片的http链接
        //   urls: [src] // 需要预览的图片http链接列表
        // })
        wx.uploadFile({
          filePath: src,
          name: 'image',
          url: getApp().api.default.upload_avatar,
          success(res) {
            if (res.statusCode == 200) {
              const avatar = JSON.parse(res.data);
              if (avatar.code == 0) {
                const img = avatar.data.url;
                console.log(img);
                that.setData({
                  avatarUrl: img,
                  // currentImage: '',
                });
                getApp().core.redirectTo({
                  url: "/pages/user/edit/edit?avatarUrl=" + that.data.avatarUrl + "&nickName=" + that.data.nickName,
                });
              } else {
                console.log("上传头像失败:", avatar.msg);
                getApp().core.showToast({
                  title: avatar.msg
                });
              }
            } else {
              console.log("上传头像失败:", res.errMsg);
              getApp().core.showToast({
                title: res.errMsg
              });
            }
          },
          fail(err) {
            console.log("上传头像失败:", err);
            getApp().core.showToast({
              title: '上传头像失败'
            });
          },
          complete(res) {
            wx.hideToast()
          }
        });
        that.setData({
          showCropper: !that.data.showCropper
        });
      } else {
        console.log('获取图片地址失败,请稍后重试')
      }
    })
  },

  /**
   * 回退
   */
  cancleCropper() {
    let that = this;
    getApp().core.redirectTo({
      url: "/pages/user/edit/edit?avatarUrl=" + that.data.avatarUrl + "&nickName=" + that.data.nickName,
    });
  },
})

二、2d版

1.组件文件we-cropper.wxml
<template name="we-cropper">
  <canvas
    class="cropper"
    type="2d"
    disable-scroll="true"
    bindtouchstart="touchStart"
    bindtouchmove="touchMove"
    bindtouchend="touchEnd"
    style="width:{{width}}px;height:{{height}}px;"
    id="{{id}}">
  </canvas>
  <canvas
    class="cropper"
    type="2d"
    disable-scroll="true"
    style="position: fixed; top: -{{width * pixelRatio}}px; left: -{{height * pixelRatio}}px; width:{{width * pixelRatio}}px;height:{{height * pixelRatio}}px;"
    id="{{targetId}}">
  </canvas>
</template>
2.调用页面imgcropper.wxml(同上)
3.css文件imgcropper.wxss(同上)
4.js文件imgcropper.js
// pages/user/edit/imgcropper.js
import WeCropper from '../../../components/we-cropper/we-cropper.min.js';
const device = wx.getSystemInfoSync(); // 获取设备信息
const width = device.windowWidth; // 示例为一个与屏幕等宽的正方形裁剪框
const devicePixelRatio = device.pixelRatio;
const height = device.windowHeight - Math.round(368 / devicePixelRatio) - 70; //70是手机底部圆角区
const fs = width / 750 * 2;
const imgPix = 250;
Page({

  /**
   * 页面的初始数据
   */
  data: {
    showCropper: !1,
    imgSrc: '',//确定裁剪后的图片
    currentImage: '',
    cropperOpt: {
      id: 'cropper',
      targetId: 'targetCropper',
      pixelRatio: devicePixelRatio,
      width: width, // 画布宽度
      height: height, // 画布高度
      scale: 2.5, // 最大缩放倍数
      zoom: 8, // 缩放系数
      cut: {
        x: (width - imgPix) / 2, // 裁剪框x轴起点(width * fs * 0.128) / 2
        y: (height * 0.5 - imgPix * 0.5), // 裁剪框y轴期起点
        width: imgPix, // 裁剪框宽度
        height: imgPix// 裁剪框高度
      }
    },
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    const that = this;
    const avatarUrl = options.avatarUrl;
    const nickName = options.nickName;
    this.setData({
      avatarUrl: avatarUrl,
      nickName: nickName
    });
    //剪裁组件初始化
    const { cropperOpt } = this.data;
    this.createSelectorQuery().select('#' + cropperOpt.id).fields({ node: true, size: true }).exec((res) => {
      // console.log(res);
      const canvas = res[0].node;
      const ctx = canvas.getContext('2d');
      const dpr = wx.getSystemInfoSync().pixelRatio;
      canvas.width = res[0].width * dpr;
      canvas.height = res[0].height * dpr;
      ctx.scale(dpr, dpr);
      cropperOpt.canvas = canvas;
      cropperOpt.ctx = ctx;

      this.myCropper = new WeCropper(cropperOpt)
        .on('ready', (ctx) => {
          console.log('wecropper is ready for work!');
        })
        .on('beforeImageLoad', (ctx) => {
          wx.showToast({
            title: '加载中',
            icon: 'loading',
            duration: 20000
          })
        })
        .on('imageLoad', (ctx) => {
          wx.hideToast();
        })
      //刷新画面
      this.myCropper.updateCanvas();
      // 加载原有的图片
    if (avatarUrl) {
      wx.getImageInfo({
        src: avatarUrl,
        success(res) {
          // console.log(res);
          that.myCropper.pushOrign(res.path);
        }
      });
    }
    })

    //剪裁组件初始化end
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {

  },
  onChooseAvatar(e) {
    let that = this;
    const { avatarUrl } = e.detail
    // console.log(avatarUrl);
    that.setData({
      currentImage: avatarUrl
    });
    that.myCropper.pushOrign(avatarUrl);
  },
  chooseMedia(e) {
    let that = this;
    wx.chooseMedia({
      count: 1,
      mediaType: ['image'],
      sourceType: ['album', 'camera'],
      maxDuration: 30,
      camera: 'back',
      success(res) {
        // console.log(res.tempFiles[0],e);
        let { tempFilePath, fileType } = res.tempFiles[0];
        that.setData({
          currentImage: tempFilePath
        });
        if (fileType == 'image') {
          that.myCropper.pushOrign(tempFilePath);
        }
        else {
          getApp().core.showModal({
            title: '提示',
            content: "只能选择图片",
            showCancel: false
          });
        }
      },
      fail(res) {
        // console.log(res);
        let { errMsg } = res;
        if (errMsg != "chooseMedia:fail cancel") {
          getApp().core.showModal({
            title: '选择图片时出错',
            content: res.errMsg,
            showCancel: false
          });
        }
      }
    });
  },
  touchStart(e) {
    this.myCropper.touchStart(e)
  },
  touchMove(e) {
    this.myCropper.touchMove(e)
  },
  touchEnd(e) {
    this.myCropper.touchEnd(e)
  },

  getCropperImage() {
    let that = this;
    if (!that.data.currentImage) {
      getApp().core.showModal({
        title: '提示',
        content: '请先选择图片',
        showCancel: false,
        success: res => { }
      });
      return false;
    }
    wx.showToast({
      title: '上传中',
      icon: 'loading',
      duration: 20000
    });
    // 如果有需要两层画布处理模糊,实际画的是放大的那个画布
    this.myCropper.getCropperImage((src) => {
      if (src) {
        that.setData({
          imgSrc: src
        });
        console.log(src);
        // wx.previewImage({
        //   current: '', // 当前显示图片的http链接
        //   urls: [src] // 需要预览的图片http链接列表
        // })
        wx.uploadFile({
          filePath: src,
          name: 'image',
          url: getApp().api.default.upload_avatar,
          success(res) {
            if (res.statusCode == 200) {
              const avatar = JSON.parse(res.data);
              if (avatar.code == 0) {
                const img = avatar.data.url;
                console.log(img);
                that.setData({
                  avatarUrl: img,
                  // currentImage: '',
                });
                getApp().core.redirectTo({
                  url: "/pages/user/edit/edit?avatarUrl=" + that.data.avatarUrl + "&nickName=" + that.data.nickName,
                });
              } else {
                console.log("上传头像失败:", avatar.msg);
                getApp().core.showToast({
                  title: avatar.msg
                });
              }
            } else {
              console.log("上传头像失败:", res.errMsg);
              getApp().core.showToast({
                title: res.errMsg
              });
            }
          },
          fail(err) {
            console.log("上传头像失败:", err);
            getApp().core.showToast({
              title: '上传头像失败'
            });
          },
          complete(res) {
            wx.hideToast()
          }
        });
        that.setData({
          showCropper: !that.data.showCropper
        });
      } else {
        console.log('获取图片地址失败,请稍后重试')
      }
    })
  },

  /**
   * 回退
   */
  cancleCropper() {
    let that = this;
    getApp().core.redirectTo({
      url: "/pages/user/edit/edit?avatarUrl=" + that.data.avatarUrl + "&nickName=" + that.data.nickName,
    });
  },
})