小程序图片裁剪插件image-cropper实现个人头像上传裁剪功能
参考文档:小程序图片裁剪插件 image-cropper
整体效果流程图

一、第一步引入image-cropper,放在dist文件夹下

image-cropper.wxml

  <view class='image-cropper' catchtouchmove='_preventTouchMove'><view class='main' bindtouchend="_cutTouchEnd" bindtouchstart="_cutTouchStart" bindtouchmove="_cutTouchMove" bindtap="_click"><view class='content'><view class='content_top bg_gray {{_flag_bright?"":"bg_black"}}' style="height:{{cut_top}}px;transition-property:{{_cut_animation?'':'background'}}"></view><view class='content_middle' style="height:{{height}}px;"><view class='content_middle_left bg_gray {{_flag_bright?"":"bg_black"}}' style="width:{{cut_left}}px;transition-property:{{_cut_animation?'':'background'}}"></view><view class='content_middle_middle' style="width:{{width}}px;height:{{height}}px;transition-duration: .3s;transition-property:{{_cut_animation?'':'background'}};"><view class="border border-top-left"></view><view class="border border-top-right"></view><view class="border border-right-top"></view><view class="border border-right-bottom"></view><view class="border border-bottom-right"></view><view class="border border-bottom-left"></view><view class="border border-left-bottom"></view><view class="border border-left-top"></view></view><view class='content_middle_right bg_gray {{_flag_bright?"":"bg_black"}}' style="transition-property:{{_cut_animation?'':'background'}}"></view></view><view class='content_bottom bg_gray {{_flag_bright?"":"bg_black"}}' style="transition-property:{{_cut_animation?'':'background'}}"></view></view><image bindload="imageLoad" bindtouchstart="_start" bindtouchmove="_move" bindtouchend="_end" style="width:{{img_width ? img_width + 'px' : 'auto'}};height:{{img_height ? img_height + 'px' : 'auto'}};transform:translate3d({{_img_left-img_width/2}}px,{{_img_top-img_height/2}}px,0) scale({{scale}}) rotate({{angle}}deg);transition-duration:{{_cut_animation?.4:0}}s;" class='img' src='{{imgSrc}}'></image></view><canvas canvas-id='image-cropper' disable-scroll="true" style="width:{{_canvas_width * export_scale}}px;height:{{_canvas_height * export_scale}}px;left:{{canvas_left}}px;top:{{canvas_top}}px" class='image-cropper-canvas'></canvas></view>

image-cropper.wxss

.image-cropper{background:rgba(14, 13, 13,.8);position: fixed;top:0;left:0;width:100vw;height:100vh;z-index: 1;
}
.main{position: absolute;width:100vw;height:100vh;overflow: hidden;
}
.content{z-index: 9;position: absolute;width:100vw;height:100vh;display: flex;flex-direction:column;pointer-events:none;
}
.bg_black{background: rgba(0, 0, 0, 0.8)!important;
}
.bg_gray{background: rgba(0, 0, 0, 0.45);transition-duration: .35s;
}
.content>.content_top{pointer-events:none;
}
.content>.content_middle{display: flex;height: 200px;width:100%;
}
.content_middle_middle{width:200px;box-sizing:border-box;position: relative;transition-duration: .3s;
}
.content_middle_right{flex: auto;
}
.content>.content_bottom{flex: auto;
}
.image-cropper .img{z-index: 2;top:0;left:0;position: absolute;border:none;width:100%;backface-visibility: hidden;transform-origin:center;
}
.image-cropper-canvas{position: fixed;background: white;width:150px;height:150px;z-index: 10;top:-200%;pointer-events:none;
}
.border{background: white;pointer-events:auto;position:absolute;
}
.border-top-left{left:-2.5px;top:-2.5px;height:2.5px;width:33rpx;
}
.border-top-right{right:-2.5px;top:-2.5px;height:2.5px;width:33rpx;
}
.border-right-top{top:-1px;width:2.5px;height:30rpx;right:-2.5px;
}
.border-right-bottom{width:2.5px;height:30rpx;right:-2.5px;bottom:-1px;
}
.border-bottom-left{height:2.5px;width:33rpx;bottom:-2.5px;left:-2.5px;
}
.border-bottom-right{height:2.5px;width:33rpx;bottom:-2.5px;right:-2.5px;
}
.border-left-top{top:-1px;width:2.5px;height:30rpx;left:-2.5px;
}
.border-left-bottom{width:2.5px;height:30rpx;left:-2.5px;bottom:-1px;
}

image-cropper.json

{"component": true
}

image-cropper.js

Component({properties: {/**     * 图片路径*/'imgSrc': {type: String},/*** 裁剪框高度*/'height': {type: Number,value: 200},/*** 裁剪框宽度*/'width': {type: Number,value: 200},/*** 裁剪框最小尺寸*/'min_width': {type: Number,value: 100},'min_height': {type: Number,value: 100},/*** 裁剪框最大尺寸*/'max_width': {type: Number,value: 300},'max_height': {type: Number,value: 300},/*** 裁剪框禁止拖动*/'disable_width': {type: Boolean,value: false},'disable_height': {type: Boolean,value: false},/*** 锁定裁剪框比例*/'disable_ratio': {type: Boolean,value: false},/*** 生成的图片尺寸相对剪裁框的比例*/'export_scale': {type: Number,value: 3},/*** 生成的图片质量0-1*/'quality': {type: Number,value: 1},'cut_top': {type: Number,value: null},'cut_left': {type: Number,value: null},/*** canvas上边距(不设置默认不显示)*/'canvas_top': {type: Number,value: null},/*** canvas左边距(不设置默认不显示)*/'canvas_left': {type: Number,value: null},/*** 图片宽度*/'img_width': {type: null,value: null},/*** 图片高度*/'img_height': {type: null,value: null},/*** 图片缩放比*/'scale': {type: Number,value: 1},/*** 图片旋转角度*/'angle': {type: Number,value: 0},/*** 最小缩放比*/'min_scale': {type: Number,value: 0.5},/*** 最大缩放比*/'max_scale': {type: Number,value: 2},/*** 是否禁用旋转*/'disable_rotate': {type: Boolean,value: false},/*** 是否限制移动范围(剪裁框只能在图片内)*/'limit_move': {type: Boolean,value: false}},data: {el: 'image-cropper', //暂时无用info: wx.getSystemInfoSync(),MOVE_THROTTLE: null,//触摸移动节流settimeoutMOVE_THROTTLE_FLAG: true,//节流标识INIT_IMGWIDTH: 0, //图片设置尺寸,此值不变(记录最初设定的尺寸)INIT_IMGHEIGHT: 0, //图片设置尺寸,此值不变(记录最初设定的尺寸)TIME_BG: null,//背景变暗延时函数TIME_CUT_CENTER: null,_touch_img_relative: [{x: 0,y: 0}], //鼠标和图片中心的相对位置_flag_cut_touch: false,//是否是拖动裁剪框_hypotenuse_length: 0, //双指触摸时斜边长度_flag_img_endtouch: false, //是否结束触摸_flag_bright: true, //背景是否亮_canvas_overflow: true,//canvas缩略图是否在屏幕外面_canvas_width: 200,_canvas_height: 200,origin_x: 0.5, //图片旋转中心origin_y: 0.5, //图片旋转中心_cut_animation: false,//是否开启图片和裁剪框过渡_img_top: wx.getSystemInfoSync().windowHeight / 2, //图片上边距_img_left: wx.getSystemInfoSync().windowWidth / 2, //图片左边距watch: {//监听截取框宽高变化width(value, that) {if (value < that.data.min_width) {that.setData({width: that.data.min_width});}that._computeCutSize();},height(value, that) {if (value < that.data.min_height) {that.setData({height: that.data.min_height});}that._computeCutSize();},angle(value, that) {//停止居中裁剪框,继续修改图片位置that._moveStop();if (that.data.limit_move) {if (that.data.angle % 90) {that.setData({angle: Math.round(that.data.angle / 90) * 90});return;}}},_cut_animation(value, that) {//开启过渡300毫秒之后自动关闭clearTimeout(that.data._cut_animation_time);if (value) {that.data._cut_animation_time = setTimeout(() => {that.setData({_cut_animation: false});}, 300)}},limit_move(value, that) {if (value) {if (that.data.angle % 90) {that.setData({angle: Math.round(that.data.angle / 90) * 90});}that._imgMarginDetectionScale();!that.data._canvas_overflow && that._draw();}},canvas_top(value, that) {that._canvasDetectionPosition();},canvas_left(value, that) {that._canvasDetectionPosition();},imgSrc(value, that) {that.pushImg();},cut_top(value, that) {that._cutDetectionPosition();if (that.data.limit_move) {!that.data._canvas_overflow && that._draw();}},cut_left(value, that) {that._cutDetectionPosition();if (that.data.limit_move) {!that.data._canvas_overflow && that._draw();}}}},attached() {this.data.info = wx.getSystemInfoSync();//启用数据监听this._watcher();this.data.INIT_IMGWIDTH = this.data.img_width;this.data.INIT_IMGHEIGHT = this.data.img_height;this.setData({_canvas_height: this.data.height,_canvas_width: this.data.width,});this._initCanvas();this.data.imgSrc && (this.data.imgSrc = this.data.imgSrc);//根据开发者设置的图片目标尺寸计算实际尺寸this._initImageSize();//设置裁剪框大小>设置图片尺寸>绘制canvasthis._computeCutSize();//检查裁剪框是否在范围内this._cutDetectionPosition();//检查canvas是否在范围内this._canvasDetectionPosition();//初始化完成this.triggerEvent('load', {cropper: this});},methods: {/*** 上传图片*/upload() {let that = this;wx.chooseImage({count: 1,sizeType: ['original', 'compressed'],sourceType: ['album', 'camera'],success(res) {const tempFilePaths = res.tempFilePaths[0];that.pushImg(tempFilePaths);wx.showLoading({title: '加载中...'})}})},/*** 返回图片信息*/getImg(getCallback) {this._draw(() => {wx.canvasToTempFilePath({width: this.data.width * this.data.export_scale,height: Math.round(this.data.height * this.data.export_scale),destWidth: this.data.width * this.data.export_scale,destHeight: Math.round(this.data.height) * this.data.export_scale,fileType: 'png',quality: this.data.quality,canvasId: this.data.el,success: (res) => {getCallback({url: res.tempFilePath,width: this.data.width * this.data.export_scale,height: this.data.height * this.data.export_scale});}}, this)});},/*** 设置图片动画* {*    x:10,//图片在原有基础上向下移动10px*    y:10,//图片在原有基础上向右移动10px*    angle:10,//图片在原有基础上旋转10deg*    scale:0.5,//图片在原有基础上增加0.5倍* }*/setTransform(transform) {if (!transform) return;if (!this.data.disable_rotate) {this.setData({angle: transform.angle ? this.data.angle + transform.angle : this.data.angle});}var scale = this.data.scale;if (transform.scale) {scale = this.data.scale + transform.scale;scale = scale <= this.data.min_scale ? this.data.min_scale : scale;scale = scale >= this.data.max_scale ? this.data.max_scale : scale;}this.data.scale = scale;let cutX = this.data.cut_left;let cutY = this.data.cut_top;if (transform.cutX) {this.setData({cut_left: cutX + transform.cutX});this.data.watch.cut_left(null, this);}if (transform.cutY) {this.setData({cut_top: cutY + transform.cutY});this.data.watch.cut_top(null, this);}this.data._img_top = transform.y ? this.data._img_top + transform.y : this.data._img_top;this.data._img_left = transform.x ? this.data._img_left + transform.x : this.data._img_left;//图像边缘检测,防止截取到空白this._imgMarginDetectionScale();//停止居中裁剪框,继续修改图片位置this._moveDuring();this.setData({scale: this.data.scale,_img_top: this.data._img_top,_img_left: this.data._img_left});!this.data._canvas_overflow && this._draw();//可以居中裁剪框了this._moveStop();//结束操作},/*** 设置剪裁框位置*/setCutXY(x, y) {this.setData({cut_top: y,cut_left: x});},/*** 设置剪裁框尺寸*/setCutSize(w, h) {this.setData({width: w,height: h});this._computeCutSize();},/*** 设置剪裁框和图片居中*/setCutCenter() {let cut_top = (this.data.info.windowHeight - this.data.height) * 0.5;let cut_left = (this.data.info.windowWidth - this.data.width) * 0.5;//顺序不能变this.setData({_img_top: this.data._img_top - this.data.cut_top + cut_top,cut_top: cut_top, //截取的框上边距_img_left: this.data._img_left - this.data.cut_left + cut_left,cut_left: cut_left, //截取的框左边距});},_setCutCenter() {let cut_top = (this.data.info.windowHeight - this.data.height) * 0.5;let cut_left = (this.data.info.windowWidth - this.data.width) * 0.5;this.setData({cut_top: cut_top, //截取的框上边距cut_left: cut_left, //截取的框左边距});},/*** 设置剪裁框宽度-即将废弃*/setWidth(width) {this.setData({width: width});this._computeCutSize();},/*** 设置剪裁框高度-即将废弃*/setHeight(height) {this.setData({height: height});this._computeCutSize();},/*** 是否锁定旋转*/setDisableRotate(value) {this.data.disable_rotate = value;},/*** 是否限制移动*/setLimitMove(value) {this.setData({_cut_animation: true,limit_move: !!value});},/*** 初始化图片,包括位置、大小、旋转角度*/imgReset() {this.setData({scale: 1,angle: 0,_img_top: wx.getSystemInfoSync().windowHeight / 2,_img_left: wx.getSystemInfoSync().windowWidth / 2,})},/*** 加载(更换)图片*/pushImg(src) {if (src) {this.setData({imgSrc: src});//发现是手动赋值直接返回,交给watch处理return;}// getImageInfo接口传入 src: '' 会导致内存泄漏if (!this.data.imgSrc) return;wx.getImageInfo({src: this.data.imgSrc,success: (res) => {this.data.imageObject = res;//图片非本地路径需要换成本地路径if (this.data.imgSrc.search(/tmp/) == -1) {this.setData({imgSrc: res.path});}//计算最后图片尺寸this._imgComputeSize();if (this.data.limit_move) {//限制移动,不留空白处理this._imgMarginDetectionScale();}this._draw();},fail: (err) => {this.setData({imgSrc: ''});}});},imageLoad(e) {setTimeout(() => {this.triggerEvent('imageload', this.data.imageObject);}, 1000)},/*** 设置图片放大缩小*/setScale(scale) {if (!scale) return;this.setData({scale: scale});!this.data._canvas_overflow && this._draw();},/*** 设置图片旋转角度*/setAngle(angle) {if (!angle) return;this.setData({_cut_animation: true,angle: angle});this._imgMarginDetectionScale();!this.data._canvas_overflow && this._draw();},_initCanvas() {//初始化canvasif (!this.data.ctx) {this.data.ctx = wx.createCanvasContext("image-cropper", this);}},/*** 根据开发者设置的图片目标尺寸计算实际尺寸*/_initImageSize() {//处理宽高特殊单位 %>pxif (this.data.INIT_IMGWIDTH && typeof this.data.INIT_IMGWIDTH == "string" && this.data.INIT_IMGWIDTH.indexOf("%") != -1) {let width = this.data.INIT_IMGWIDTH.replace("%", "");this.data.INIT_IMGWIDTH = this.data.img_width = this.data.info.windowWidth / 100 * width;}if (this.data.INIT_IMGHEIGHT && typeof this.data.INIT_IMGHEIGHT == "string" && this.data.INIT_IMGHEIGHT.indexOf("%") != -1) {let height = this.data.img_height.replace("%", "");this.data.INIT_IMGHEIGHT = this.data.img_height = this.data.info.windowHeight / 100 * height;}},/*** 检测剪裁框位置是否在允许的范围内(屏幕内)*/_cutDetectionPosition() {let _cutDetectionPositionTop = () => {//检测上边距是否在范围内if (this.data.cut_top < 0) {this.setData({cut_top: 0});}if (this.data.cut_top > this.data.info.windowHeight - this.data.height) {this.setData({cut_top: this.data.info.windowHeight - this.data.height});}}, _cutDetectionPositionLeft = () => {//检测左边距是否在范围内if (this.data.cut_left < 0) {this.setData({cut_left: 0});}if (this.data.cut_left > this.data.info.windowWidth - this.data.width) {this.setData({cut_left: this.data.info.windowWidth - this.data.width});}};//裁剪框坐标处理(如果只写一个参数则另一个默认为0,都不写默认居中)if (this.data.cut_top == null && this.data.cut_left == null) {this._setCutCenter();} else if (this.data.cut_top != null && this.data.cut_left != null) {_cutDetectionPositionTop();_cutDetectionPositionLeft();} else if (this.data.cut_top != null && this.data.cut_left == null) {_cutDetectionPositionTop();this.setData({cut_left: (this.data.info.windowWidth - this.data.width) / 2});} else if (this.data.cut_top == null && this.data.cut_left != null) {_cutDetectionPositionLeft();this.setData({cut_top: (this.data.info.windowHeight - this.data.height) / 2});}},/*** 检测canvas位置是否在允许的范围内(屏幕内)如果在屏幕外则不开启实时渲染* 如果只写一个参数则另一个默认为0,都不写默认超出屏幕外*/_canvasDetectionPosition() {if (this.data.canvas_top == null && this.data.canvas_left == null) {this.data._canvas_overflow = false;this.setData({canvas_top: -5000,canvas_left: -5000});} else if (this.data.canvas_top != null && this.data.canvas_left != null) {if (this.data.canvas_top < - this.data.height || this.data.canvas_top > this.data.info.windowHeight) {this.data._canvas_overflow = true;} else {this.data._canvas_overflow = false;}} else if (this.data.canvas_top != null && this.data.canvas_left == null) {this.setData({canvas_left: 0});} else if (this.data.canvas_top == null && this.data.canvas_left != null) {this.setData({canvas_top: 0});if (this.data.canvas_left < -this.data.width || this.data.canvas_left > this.data.info.windowWidth) {this.data._canvas_overflow = true;} else {this.data._canvas_overflow = false;}}},/*** 图片边缘检测-位置*/_imgMarginDetectionPosition(scale) {if (!this.data.limit_move) return;let left = this.data._img_left;let top = this.data._img_top;var scale = scale || this.data.scale;let img_width = this.data.img_width;let img_height = this.data.img_height;if (this.data.angle / 90 % 2) {img_width = this.data.img_height;img_height = this.data.img_width;}left = this.data.cut_left + img_width * scale / 2 >= left ? left : this.data.cut_left + img_width * scale / 2;left = this.data.cut_left + this.data.width - img_width * scale / 2 <= left ? left : this.data.cut_left + this.data.width - img_width * scale / 2;top = this.data.cut_top + img_height * scale / 2 >= top ? top : this.data.cut_top + img_height * scale / 2;top = this.data.cut_top + this.data.height - img_height * scale / 2 <= top ? top : this.data.cut_top + this.data.height - img_height * scale / 2;this.setData({_img_left: left,_img_top: top,scale: scale})},/*** 图片边缘检测-缩放*/_imgMarginDetectionScale() {if (!this.data.limit_move) return;let scale = this.data.scale;let img_width = this.data.img_width;let img_height = this.data.img_height;if (this.data.angle / 90 % 2) {img_width = this.data.img_height;img_height = this.data.img_width;}if (img_width * scale < this.data.width) {scale = this.data.width / img_width;}if (img_height * scale < this.data.height) {scale = Math.max(scale, this.data.height / img_height);}this._imgMarginDetectionPosition(scale);},_setData(obj) {let data = {};for (var key in obj) {if (this.data[key] != obj[key]) {data[key] = obj[key];}}this.setData(data);return data;},/*** 计算图片尺寸*/_imgComputeSize() {let img_width = this.data.img_width,img_height = this.data.img_height;if (!this.data.INIT_IMGHEIGHT && !this.data.INIT_IMGWIDTH) {//默认按图片最小边 = 对应裁剪框尺寸img_width = this.data.imageObject.width;img_height = this.data.imageObject.height;if (img_width / img_height > this.data.width / this.data.height) {img_height = this.data.height;img_width = this.data.imageObject.width / this.data.imageObject.height * img_height;} else {img_width = this.data.width;img_height = this.data.imageObject.height / this.data.imageObject.width * img_width;}} else if (this.data.INIT_IMGHEIGHT && !this.data.INIT_IMGWIDTH) {img_width = this.data.imageObject.width / this.data.imageObject.height * this.data.INIT_IMGHEIGHT;} else if (!this.data.INIT_IMGHEIGHT && this.data.INIT_IMGWIDTH) {img_height = this.data.imageObject.height / this.data.imageObject.width * this.data.INIT_IMGWIDTH;}this.setData({img_width: img_width,img_height: img_height});},//改变截取框大小_computeCutSize() {if (this.data.width > this.data.info.windowWidth) {this.setData({width: this.data.info.windowWidth,});} else if (this.data.width + this.data.cut_left > this.data.info.windowWidth) {this.setData({cut_left: this.data.info.windowWidth - this.data.cut_left,});};if (this.data.height > this.data.info.windowHeight) {this.setData({height: this.data.info.windowHeight,});} else if (this.data.height + this.data.cut_top > this.data.info.windowHeight) {this.setData({cut_top: this.data.info.windowHeight - this.data.cut_top,});}!this.data._canvas_overflow && this._draw();},//开始触摸_start(event) {this.data._flag_img_endtouch = false;if (event.touches.length == 1) {//单指拖动this.data._touch_img_relative[0] = {x: (event.touches[0].clientX - this.data._img_left),y: (event.touches[0].clientY - this.data._img_top)}} else {//双指放大let width = Math.abs(event.touches[0].clientX - event.touches[1].clientX);let height = Math.abs(event.touches[0].clientY - event.touches[1].clientY);this.data._touch_img_relative = [{x: (event.touches[0].clientX - this.data._img_left),y: (event.touches[0].clientY - this.data._img_top)}, {x: (event.touches[1].clientX - this.data._img_left),y: (event.touches[1].clientY - this.data._img_top)}];this.data._hypotenuse_length = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));}!this.data._canvas_overflow && this._draw();},_move_throttle() {//安卓需要节流if (this.data.info.platform == 'android') {clearTimeout(this.data.MOVE_THROTTLE);this.data.MOVE_THROTTLE = setTimeout(() => {this.data.MOVE_THROTTLE_FLAG = true;}, 1000 / 40)return this.data.MOVE_THROTTLE_FLAG;} else {this.data.MOVE_THROTTLE_FLAG = true;}},_move(event) {if (this.data._flag_img_endtouch || !this.data.MOVE_THROTTLE_FLAG) return;this.data.MOVE_THROTTLE_FLAG = false;this._move_throttle();this._moveDuring();if (event.touches.length == 1) {//单指拖动let left = (event.touches[0].clientX - this.data._touch_img_relative[0].x),top = (event.touches[0].clientY - this.data._touch_img_relative[0].y);//图像边缘检测,防止截取到空白this.data._img_left = left;this.data._img_top = top;this._imgMarginDetectionPosition();this.setData({_img_left: this.data._img_left,_img_top: this.data._img_top});} else {//双指放大let width = (Math.abs(event.touches[0].clientX - event.touches[1].clientX)),height = (Math.abs(event.touches[0].clientY - event.touches[1].clientY)),hypotenuse = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)),scale = this.data.scale * (hypotenuse / this.data._hypotenuse_length),current_deg = 0;scale = scale <= this.data.min_scale ? this.data.min_scale : scale;scale = scale >= this.data.max_scale ? this.data.max_scale : scale;//图像边缘检测,防止截取到空白this.data.scale = scale;this._imgMarginDetectionScale();//双指旋转(如果没禁用旋转)let _touch_img_relative = [{x: (event.touches[0].clientX - this.data._img_left),y: (event.touches[0].clientY - this.data._img_top)}, {x: (event.touches[1].clientX - this.data._img_left),y: (event.touches[1].clientY - this.data._img_top)}];if (!this.data.disable_rotate) {let first_atan = 180 / Math.PI * Math.atan2(_touch_img_relative[0].y, _touch_img_relative[0].x);let first_atan_old = 180 / Math.PI * Math.atan2(this.data._touch_img_relative[0].y, this.data._touch_img_relative[0].x);let second_atan = 180 / Math.PI * Math.atan2(_touch_img_relative[1].y, _touch_img_relative[1].x);let second_atan_old = 180 / Math.PI * Math.atan2(this.data._touch_img_relative[1].y, this.data._touch_img_relative[1].x);//当前旋转的角度let first_deg = first_atan - first_atan_old,second_deg = second_atan - second_atan_old;if (first_deg != 0) {current_deg = first_deg;} else if (second_deg != 0) {current_deg = second_deg;}}this.data._touch_img_relative = _touch_img_relative;this.data._hypotenuse_length = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));//更新视图this.setData({angle: this.data.angle + current_deg,scale: this.data.scale});}!this.data._canvas_overflow && this._draw();},//结束操作_end(event) {this.data._flag_img_endtouch = true;this._moveStop();},//点击中间剪裁框处理_click(event) {if (!this.data.imgSrc) {//调起上传this.upload();return;}this._draw(() => {let x = event.detail ? event.detail.x : event.touches[0].clientX;let y = event.detail ? event.detail.y : event.touches[0].clientY;if ((x >= this.data.cut_left && x <= (this.data.cut_left + this.data.width)) && (y >= this.data.cut_top && y <= (this.data.cut_top + this.data.height))) {//生成图片并回调wx.canvasToTempFilePath({width: this.data.width * this.data.export_scale,height: Math.round(this.data.height * this.data.export_scale),destWidth: this.data.width * this.data.export_scale,destHeight: Math.round(this.data.height) * this.data.export_scale,fileType: 'png',quality: this.data.quality,canvasId: this.data.el,success: (res) => {this.triggerEvent('tapcut', {url: res.tempFilePath,width: this.data.width * this.data.export_scale,height: this.data.height * this.data.export_scale});}}, this)}});},//渲染_draw(callback) {if (!this.data.imgSrc) return;let draw = () => {//图片实际大小let img_width = this.data.img_width * this.data.scale * this.data.export_scale;let img_height = this.data.img_height * this.data.scale * this.data.export_scale;//canvas和图片的相对距离var xpos = this.data._img_left - this.data.cut_left;var ypos = this.data._img_top - this.data.cut_top;//旋转画布this.data.ctx.translate(xpos * this.data.export_scale, ypos * this.data.export_scale);this.data.ctx.rotate(this.data.angle * Math.PI / 180);this.data.ctx.drawImage(this.data.imgSrc, -img_width / 2, -img_height / 2, img_width, img_height);this.data.ctx.draw(false, () => {callback && callback();});}if (this.data.ctx.width != this.data.width || this.data.ctx.height != this.data.height) {//优化拖动裁剪框,所以必须把宽高设置放在离用户触发渲染最近的地方this.setData({_canvas_height: this.data.height,_canvas_width: this.data.width,}, () => {//延迟40毫秒防止点击过快出现拉伸或裁剪过多setTimeout(() => {draw();}, 40);});} else {draw();}},//裁剪框处理_cutTouchMove(e) {if (this.data._flag_cut_touch && this.data.MOVE_THROTTLE_FLAG) {if (this.data.disable_ratio && (this.data.disable_width || this.data.disable_height)) return;//节流this.data.MOVE_THROTTLE_FLAG = false;this._move_throttle();let width = this.data.width,height = this.data.height,cut_top = this.data.cut_top,cut_left = this.data.cut_left,size_correct = () => {width = width <= this.data.max_width ? width >= this.data.min_width ? width : this.data.min_width : this.data.max_width;height = height <= this.data.max_height ? height >= this.data.min_height ? height : this.data.min_height : this.data.max_height;},size_inspect = () => {if ((width > this.data.max_width || width < this.data.min_width || height > this.data.max_height || height < this.data.min_height) && this.data.disable_ratio) {size_correct();return false;} else {size_correct();return true;}};height = this.data.CUT_START.height + ((this.data.CUT_START.corner > 1 && this.data.CUT_START.corner < 4 ? 1 : -1) * (this.data.CUT_START.y - e.touches[0].clientY));switch (this.data.CUT_START.corner) {case 1:width = this.data.CUT_START.width + this.data.CUT_START.x - e.touches[0].clientX;if (this.data.disable_ratio) {height = width / (this.data.width / this.data.height)}if (!size_inspect()) return;cut_left = this.data.CUT_START.cut_left - (width - this.data.CUT_START.width);breakcase 2:width = this.data.CUT_START.width + this.data.CUT_START.x - e.touches[0].clientX;if (this.data.disable_ratio) {height = width / (this.data.width / this.data.height)}if (!size_inspect()) return;cut_top = this.data.CUT_START.cut_top - (height - this.data.CUT_START.height)cut_left = this.data.CUT_START.cut_left - (width - this.data.CUT_START.width)breakcase 3:width = this.data.CUT_START.width - this.data.CUT_START.x + e.touches[0].clientX;if (this.data.disable_ratio) {height = width / (this.data.width / this.data.height)}if (!size_inspect()) return;cut_top = this.data.CUT_START.cut_top - (height - this.data.CUT_START.height);breakcase 4:width = this.data.CUT_START.width - this.data.CUT_START.x + e.touches[0].clientX;if (this.data.disable_ratio) {height = width / (this.data.width / this.data.height)}if (!size_inspect()) return;break}if (!this.data.disable_width && !this.data.disable_height) {this.setData({width: width,cut_left: cut_left,height: height,cut_top: cut_top,})} else if (!this.data.disable_width) {this.setData({width: width,cut_left: cut_left})} else if (!this.data.disable_height) {this.setData({height: height,cut_top: cut_top})}this._imgMarginDetectionScale();}},_cutTouchStart(e) {let currentX = e.touches[0].clientX;let currentY = e.touches[0].clientY;let cutbox_top4 = this.data.cut_top + this.data.height - 30;let cutbox_bottom4 = this.data.cut_top + this.data.height + 20;let cutbox_left4 = this.data.cut_left + this.data.width - 30;let cutbox_right4 = this.data.cut_left + this.data.width + 30;let cutbox_top3 = this.data.cut_top - 30;let cutbox_bottom3 = this.data.cut_top + 30;let cutbox_left3 = this.data.cut_left + this.data.width - 30;let cutbox_right3 = this.data.cut_left + this.data.width + 30;let cutbox_top2 = this.data.cut_top - 30;let cutbox_bottom2 = this.data.cut_top + 30;let cutbox_left2 = this.data.cut_left - 30;let cutbox_right2 = this.data.cut_left + 30;let cutbox_top1 = this.data.cut_top + this.data.height - 30;let cutbox_bottom1 = this.data.cut_top + this.data.height + 30;let cutbox_left1 = this.data.cut_left - 30;let cutbox_right1 = this.data.cut_left + 30;if (currentX > cutbox_left4 && currentX < cutbox_right4 && currentY > cutbox_top4 && currentY < cutbox_bottom4) {this._moveDuring();this.data._flag_cut_touch = true;this.data._flag_img_endtouch = true;this.data.CUT_START = {width: this.data.width,height: this.data.height,x: currentX,y: currentY,corner: 4}} else if (currentX > cutbox_left3 && currentX < cutbox_right3 && currentY > cutbox_top3 && currentY < cutbox_bottom3) {this._moveDuring();this.data._flag_cut_touch = true;this.data._flag_img_endtouch = true;this.data.CUT_START = {width: this.data.width,height: this.data.height,x: currentX,y: currentY,cut_top: this.data.cut_top,cut_left: this.data.cut_left,corner: 3}} else if (currentX > cutbox_left2 && currentX < cutbox_right2 && currentY > cutbox_top2 && currentY < cutbox_bottom2) {this._moveDuring();this.data._flag_cut_touch = true;this.data._flag_img_endtouch = true;this.data.CUT_START = {width: this.data.width,height: this.data.height,cut_top: this.data.cut_top,cut_left: this.data.cut_left,x: currentX,y: currentY,corner: 2}} else if (currentX > cutbox_left1 && currentX < cutbox_right1 && currentY > cutbox_top1 && currentY < cutbox_bottom1) {this._moveDuring();this.data._flag_cut_touch = true;this.data._flag_img_endtouch = true;this.data.CUT_START = {width: this.data.width,height: this.data.height,cut_top: this.data.cut_top,cut_left: this.data.cut_left,x: currentX,y: currentY,corner: 1}}},_cutTouchEnd(e) {this._moveStop();this.data._flag_cut_touch = false;},//停止移动时需要做的操作_moveStop() {//清空之前的自动居中延迟函数并添加最新的clearTimeout(this.data.TIME_CUT_CENTER);this.data.TIME_CUT_CENTER = setTimeout(() => {//动画启动if (!this.data._cut_animation) {this.setData({_cut_animation: true});}this.setCutCenter();}, 1000)//清空之前的背景变化延迟函数并添加最新的clearTimeout(this.data.TIME_BG);this.data.TIME_BG = setTimeout(() => {if (this.data._flag_bright) {this.setData({_flag_bright: false});}}, 2000)},//移动中_moveDuring() {//清空之前的自动居中延迟函数clearTimeout(this.data.TIME_CUT_CENTER);//清空之前的背景变化延迟函数clearTimeout(this.data.TIME_BG);//高亮背景if (!this.data._flag_bright) {this.setData({_flag_bright: true});}},//监听器_watcher() {Object.keys(this.data).forEach(v => {this._observe(this.data, v, this.data.watch[v]);})},_observe(obj, key, watchFun) {var val = obj[key];Object.defineProperty(obj, key, {configurable: true,enumerable: true,set: (value) => {val = value;watchFun && watchFun(val, this);},get() {if (val && '_img_top|img_left||width|height|min_width|max_width|min_height|max_height|export_scale|cut_top|cut_left|canvas_top|canvas_left|img_width|img_height|scale|angle|min_scale|max_scale'.indexOf(key) != -1) {let ret = parseFloat(parseFloat(val).toFixed(3));if (typeof val == "string" && val.indexOf("%") != -1) {ret += '%';}return ret;}return val;}})},_preventTouchMove() {}}
})

个人头像界面
myhead.wxml

<view class='page'><block wx:if="{{showPic==true}}"><image src="../../images/icon_more.png" class="btn-more" bindtap="goOperate" mode="aspectFit"></image><image src="{{srcUrl}}" class="pic" mode="aspectFill" bindtap="previewImg"></image></block><block wx:else><view><image-cropper id="image-cropper" limit_move="{{true}}" disable_rotate="{{true}}" width="{{width}}" height="{{height}}" imgSrc="{{tempFilePaths}}" bindload="cropperload" bindimageload="loadimage" bindtapcut="clickcut"></image-cropper><view class="btn-wrap"><view class="btn-cancel" bindtap="backUp">取消</view><view class="btn-sure" bindtap="submit">确定</view></view></view></block>
</view>

myhead.wxss

page {width: 100%;height: 100%;
}.page {width: 100%;height: 100%;background: #000;display: flex;align-items: center;flex-direction: column;justify-content: center;
}.btn-more {width: 56rpx;height: 16rpx;position: absolute;right: 27rpx;top: 27rpx;
}.pic {width: 100%;height: 750rpx;background: #e3f5fb;border: 2rpx solid #31be7c;box-shadow: 0px 1rpx 4rpx 0px rgba(85, 85, 85, 0.2);
}.btn-wrap {position: absolute;bottom: 60rpx;left: 30rpx;right: 30rpx;height: auto;overflow: hidden;z-index: 10;
}.btn-wrap .btn-cancel {width: 160rpx;height: 68rpx;background: #fff;border: 1rpx solid #31be7c;box-shadow: 0px 1rpx 4rpx 0px rgba(85, 85, 85, 0.2);border-radius: 34rpx;text-align: center;font-size: 32rpx;font-family: Source Han Sans CN;font-weight: 500;color: #31be7c;float: left;line-height: 68rpx;
}.btn-sure {width: 160rpx;height: 68rpx;background: #31be7c;box-shadow: 0px 1rpx 4rpx 0px rgba(85, 85, 85, 0.2);border-radius: 34rpx;float: right;font-size: 34rpx;font-family: Source Han Sans CN;font-weight: 500;color: #fff;text-align: center;line-height: 68rpx;
}

myhead.json

{"navigationBarTitleText": "个人头像","disableScroll": true,"usingComponents": {"image-cropper": "../../dist/image-cropper/image-cropper"}
}

myhead.js

const {ygPost,ygSwitch
} = require("../../utils/util.js");
Page({/*** 页面的初始数据*/data: {src: "",srcUrl: "",showPic: true,width: 300, //宽度height: 300, //高度tempFilePaths: "",},/*** 生命周期函数--监听页面加载*/onLoad: function (options) {if (options.url == "null" || options.url == "") {this.setData({srcUrl:"/images/icon_dhead.png"})}else{this.setData({srcUrl: ygSwitch.getImgUrl(options.url)})}},goOperate: function (e) {let that = thiswx.showActionSheet({itemList: ['拍照', '从手机相册选择', '保存照片'],success: function (res) {if (res.tapIndex == 2) {wx.getImageInfo({src: that.data.srcUrl,success: function (res) {var path = res.path;wx.saveImageToPhotosAlbum({filePath: path,success(res) {wx.showToast({title: '保存成功',icon: "none"})console.log(res);},fail(res) {wx.showToast({title: '保存失败',icon: "none"})console.log(res);}})}})} else {let sourceType = []if (res.tapIndex == 0) {sourceType.push("camera");} else {sourceType.push("album");}wx.chooseImage({count: 1, // 默认9sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有sourceType: sourceType, // 可以指定来源是相册还是相机,默认二者都有success: function (res) {const tempFilePaths = res.tempFilePaths[0];that.setData({tempFilePaths: tempFilePaths,showPic: false,})//获取到image-cropper实例that.cropper = that.selectComponent("#image-cropper");wx.showLoading({title: '加载中'})}})}},fail: function (res) {}})},loadimage(e) {console.log("图片加载完成", e.detail);wx.hideLoading();//重置图片角度、缩放、位置this.cropper.imgReset();},clickcut(e) {console.log(e.detail);//点击裁剪框阅览图片wx.previewImage({current: e.detail.srcUrl, // 当前显示图片的http链接urls: [e.detail.url] // 需要预览的图片http链接列表})},backUp() {wx.navigateBack({delta: 1,})},submit() {let that = this;this.cropper.getImg((obj) => {let url = obj.url;ygPost.uploadFile([url], function (ret) {let pic = ygSwitch.getImgUrl(ret);that.setData({src: ret,srcUrl: pic,showPic: true,});that.uploadHeadPic();});});},uploadHeadPic: function () {let that = this;ygPost.postGetSession("/User/UploadHeadPic", {pic: that.data.src,}, function (set) {if (set.data.isSuccess) {wx.showToast({title: '修改成功',icon: "success",})} else {wx.showModal({content: set.data.message,title: "提示",showCancel: false,})}});},previewImg: function (e) {let that = this;wx.previewImage({current: 0, //当前图片地址urls: [that.data.srcUrl], //所有要预览的图片的地址集合 数组形式success: function (res) {},fail: function (res) {},complete: function (res) {},})},/*** 生命周期函数--监听页面初次渲染完成*/onReady: function () {},/*** 生命周期函数--监听页面显示*/onShow: function (options) {},/*** 生命周期函数--监听页面隐藏*/onHide: function () {},/*** 生命周期函数--监听页面卸载*/onUnload: function () {let pages = getCurrentPages();let prevPage = pages[pages.length - 2]prevPage.getUserInfo();prevPage.setData({headPic: this.data.srcUrl})},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh: function () {},/*** 页面上拉触底事件的处理函数*/onReachBottom: function () {},/*** 用户点击右上角分享*/onShareAppMessage: function () {}
})

小程序图片裁剪插件image-cropper实现个人头像上传裁剪功能相关推荐

  1. 微信小程序开发 - 实现pdf、word等格式文件上传到后端的方法

    写在前面 我发现,微信的wx.uploadFile接口限制好多,而且会经常性出现bug,所以今天搜了一上午的资料, 看看能不能不要通过这个接口上传multipart/form-data格式的文件. 后 ...

  2. 微信小程序点击按钮弹出弹窗_微信小程序实现的点击按钮 弹出底部上拉菜单功能示例...

    本文实例讲述了微信小程序实现的点击按钮 弹出底部上拉菜单功能.分享给大家供大家参考,具体如下: index.wxml 弹出action sheet {{item.txt}} 取消 提示:您选择了菜单{ ...

  3. 调用android的拍照或本地相册选取再实现相片上传服务器,Android调用系统相机、本地相册上传图片(头像上传(裁剪)、多张图片上传)...

    开发中基本上都会有头像上传的功能,有的app还需要多张图片同时上传,下面简单将头像上传以及多张图片上传功能整理一下.图片选择仿照微信选择图片的界面.[参考] 多图片选择器 !!!推荐一个动态权限请求的 ...

  4. vue 移动端头像裁剪_vue头像上传裁剪组件_一个漂亮的Vue组件,用于图像裁剪和上传...

    vue头像上传裁剪组件 vue-image-crop-upload (vue-image-crop-upload) A beautiful vue component for image crop a ...

  5. win10打开程序响应很慢_小程序商城打开加载很慢?你上传的图片是不是太大了,压缩一下吧!...

    原创:轻栈 今天分享一篇纯干货,看完能给小程序商城提速. 打开小程序商城,有时能看到加载条,先看到内容要等等. 等待是一件消磨耐心的事情,在这个浮躁的时代,愿意等的人真的少.所以,我们要找出导致小程序 ...

  6. android 华为裁剪全图,华为手机头像上传裁剪操作 报错

    华为手机头像上传操作, 裁剪点存储--进入裁剪后--点确定, 就直接报错退出程序了,其他机型测都是成功的,求大神指教 报错代码 @Override protected void onActivityR ...

  7. 最新版人脸识别小程序 图片识别 生成二维码签到 地图上选点进行位置签到 计算签到距离 课程会议活动打卡日常考勤 上课签到打卡考勤口令签到

    技术选型 1,前端 小程序原生MINA框架 css JavaScript Wxml 2,管理后台 云开发Cms内容管理系统 web网页 3,数据后台 小程序云开发 云函数 云开发数据库(基于Mongo ...

  8. 小程序上传图片到七牛云(支持多张上传,预览,删除)

    以下为wxml (使用的vant小程序ui框架,需在json文件里自行引入) <view class='clearFloat'><view class='upload_title'& ...

  9. 小程序怎么实现授权登录,如何保存头像和上传头像?

    微信授权登录 授权成功后获取用户信息并显示 同时附带上传图片与下载图片api wxml代码如下 <view class="user" wx:if="{{userIn ...

最新文章

  1. SQL命令执行数据库备份
  2. 小程序云开发常用语句宝库
  3. python怎么安装各种模块_Python2.7安装和常用模块安装
  4. SqlCommand类,在与数据库交互式一定要用到的属性
  5. 传统企业建模原理及建模体系介绍
  6. Spring入门hello world常见问题及解决办法
  7. 对全局变量,static静态变量的理解
  8. C++ 使用extern C简单使用
  9. 谷歌浏览器如何安装crx插件
  10. Excel文件转换为XML以及Linux文件编码格式转换
  11. CSS Sprite雪碧图应用
  12. win10创建新的计算机用户名和密码,Win10怎么新建账户 Win10创建新用户图文教程...
  13. ITEXT PDF文件的拆分与合并
  14. python实现AES加密解密
  15. 节理玫瑰花图怎么画_利用Excel软件绘制节理走向玫瑰花图
  16. Mac上AI照片编辑工具:Luminar AI
  17. android音频焦点Audio Focus
  18. typeid的使用方法
  19. 激光测距仪的发展与介绍——TFN 10K KI 双目远距离激光测距仪
  20. python选课管理系统_Python开发程序:选课系统

热门文章

  1. android 获取monkey日志_monkey操作,获取包名,参数,日志,百分比
  2. dnf体验服显示服务器爆满,dnf100级上线体验服 服务器爆满临时加频道
  3. vasp结构优化英语_不用ChemOffice,用PPT绘制媲美专业软件的分子结构式
  4. Spring oxm入门实例
  5. zznuoj 1073 画个圈圈诅咒你
  6. Autodesk Eagle入门之-元器件搜索
  7. shell编程三剑客之grep与正则表达式
  8. <url-pattern>/</url-pattern>与<url-pattern>/*</url-pattern>的区别
  9. Vue打包时指定配置文件
  10. (四)如何使用Table