diff --git a/README.md b/README.md
index 8874e7cac146df8ada4ee0c7959d468611ab01dc..b7d971f7ca27a9f6df8f3e03010292b7016de0a8 100644
--- a/README.md
+++ b/README.md
@@ -101,6 +101,7 @@
| imgReset |无 | 无 |重置图片的角度、缩放、位置(可以在onloadImage回调里使用)|否|
Demo地址:https://github.com/wx-plugin/image-cropper-demo
+[点击导入代码片段](https://developers.weixin.qq.com/s/dIQvo5mr7cpm)
如果有什么好的建议欢迎提issues或者提pr
diff --git a/demo/app.js b/demo/app.js
new file mode 100644
index 0000000000000000000000000000000000000000..c41246d25e2247edcf90c759cf6d880140135211
--- /dev/null
+++ b/demo/app.js
@@ -0,0 +1,6 @@
+App({
+ onLaunch: function () {
+
+ },
+ globalData:{}
+})
diff --git a/demo/app.json b/demo/app.json
new file mode 100644
index 0000000000000000000000000000000000000000..802d4a02fd0ce8a910073019cb6c7f300f949c18
--- /dev/null
+++ b/demo/app.json
@@ -0,0 +1,13 @@
+{
+ "pages":[
+ "index/index",
+ "cropper/cropper"
+ ],
+ "window":{
+ "backgroundTextStyle":"light",
+ "navigationBarBackgroundColor": "#fff",
+ "navigationBarTitleText": "Weixin",
+ "navigationBarTextStyle":"black"
+ },
+ "sitemapLocation": "sitemap.json"
+}
diff --git a/demo/app.wxss b/demo/app.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..4270e5ec062af83932c8aed43c55c575498b17a9
--- /dev/null
+++ b/demo/app.wxss
@@ -0,0 +1,4 @@
+page{
+ height: 100%;
+ width: 100%;
+}
\ No newline at end of file
diff --git a/demo/component/image-cropper/image-cropper.js b/demo/component/image-cropper/image-cropper.js
new file mode 100644
index 0000000000000000000000000000000000000000..91f3b21844f4924ef76b74c43460dfc4480ee8e6
--- /dev/null
+++ b/demo/component/image-cropper/image-cropper.js
@@ -0,0 +1,1117 @@
+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, //触摸移动节流settimeout
+ MOVE_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();
+ //设置裁剪框大小>设置图片尺寸>绘制canvas
+ this._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() {
+ //初始化canvas
+ if (!this.data.ctx) {
+ this.data.ctx = wx.createCanvasContext("image-cropper", this);
+ }
+ },
+ /**
+ * 根据开发者设置的图片目标尺寸计算实际尺寸
+ */
+ _initImageSize() {
+ //处理宽高特殊单位 %>px
+ if (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);
+ break
+ case 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)
+ break
+ case 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);
+ break
+ case 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() {}
+ }
+})
\ No newline at end of file
diff --git a/demo/component/image-cropper/image-cropper.json b/demo/component/image-cropper/image-cropper.json
new file mode 100644
index 0000000000000000000000000000000000000000..d577adeaf94bed8407ab4f2560b1ddff63dd8bdd
--- /dev/null
+++ b/demo/component/image-cropper/image-cropper.json
@@ -0,0 +1,3 @@
+{
+ "component": true
+}
\ No newline at end of file
diff --git a/demo/component/image-cropper/image-cropper.wxml b/demo/component/image-cropper/image-cropper.wxml
new file mode 100644
index 0000000000000000000000000000000000000000..a4a752685797d8389f0cbd4ee20888d6108d11c6
--- /dev/null
+++ b/demo/component/image-cropper/image-cropper.wxml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/component/image-cropper/image-cropper.wxss b/demo/component/image-cropper/image-cropper.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..80eb895801fca31e1a4885ff8b5ff9fedac03992
--- /dev/null
+++ b/demo/component/image-cropper/image-cropper.wxss
@@ -0,0 +1,143 @@
+.image-cropper {
+ background: rgba(14, 13, 13, .8);
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ z-index: 1;
+}
+
+.image-cropper .main {
+ position: absolute;
+ width: 100vw;
+ height: 100vh;
+ overflow: hidden;
+}
+
+.image-cropper .content {
+ z-index: 9;
+ position: absolute;
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ pointer-events: none;
+}
+
+.image-cropper .bg_black {
+ background: rgba(0, 0, 0, 0.8) !important;
+}
+
+.image-cropper .bg_gray {
+ background: rgba(0, 0, 0, 0.45);
+ transition-duration: .35s;
+}
+
+.image-cropper .content>.content_top {
+ pointer-events: none;
+}
+
+.image-cropper .content>.content_middle {
+ display: flex;
+ height: 200px;
+ width: 100%;
+}
+
+.image-cropper .content_middle_middle {
+ width: 200px;
+ box-sizing: border-box;
+ position: relative;
+ transition-duration: .3s;
+}
+
+.image-cropper .content_middle_right {
+ flex: auto;
+}
+
+.image-cropper .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 .image-cropper-canvas {
+ position: fixed;
+ background: white;
+ width: 150px;
+ height: 150px;
+ z-index: 10;
+ top: -200%;
+ pointer-events: none;
+}
+
+.image-cropper .border {
+ background: white;
+ pointer-events: auto;
+ position: absolute;
+}
+
+.image-cropper .border-top-left {
+ left: -2.5px;
+ top: -2.5px;
+ height: 2.5px;
+ width: 33rpx;
+}
+
+.image-cropper .border-top-right {
+ right: -2.5px;
+ top: -2.5px;
+ height: 2.5px;
+ width: 33rpx;
+}
+
+.image-cropper .border-right-top {
+ top: -1px;
+ width: 2.5px;
+ height: 30rpx;
+ right: -2.5px;
+}
+
+.image-cropper .border-right-bottom {
+ width: 2.5px;
+ height: 30rpx;
+ right: -2.5px;
+ bottom: -1px;
+}
+
+.image-cropper .border-bottom-left {
+ height: 2.5px;
+ width: 33rpx;
+ bottom: -2.5px;
+ left: -2.5px;
+}
+
+.image-cropper .border-bottom-right {
+ height: 2.5px;
+ width: 33rpx;
+ bottom: -2.5px;
+ right: -2.5px;
+}
+
+.image-cropper .border-left-top {
+ top: -1px;
+ width: 2.5px;
+ height: 30rpx;
+ left: -2.5px;
+}
+
+.image-cropper .border-left-bottom {
+ width: 2.5px;
+ height: 30rpx;
+ left: -2.5px;
+ bottom: -1px;
+}
\ No newline at end of file
diff --git a/demo/cropper/cropper.js b/demo/cropper/cropper.js
new file mode 100644
index 0000000000000000000000000000000000000000..2e45db9a6a57a16cd9864296637e9d7089cdcf9d
--- /dev/null
+++ b/demo/cropper/cropper.js
@@ -0,0 +1,183 @@
+//获取应用实例
+const app = getApp()
+Page({
+ data: {
+ src: '',
+ width: 250, //宽度
+ height: 200, //高度
+ max_width: 300,
+ max_height: 300,
+ disable_rotate: true, //是否禁用旋转
+ disable_ratio: false, //锁定比例
+ limit_move: true, //是否限制移动
+ },
+ onLoad: function (options) {
+ this.cropper = this.selectComponent("#image-cropper");
+ this.setData({
+ src: options.imgSrc
+ });
+ this.cropper.upload(); //上传图片
+ },
+ cropperload(e) {
+ console.log('cropper加载完成');
+ },
+ loadimage(e) {
+ wx.hideLoading();
+ console.log('图片');
+ this.cropper.imgReset();
+ },
+ clickcut(e) {
+ console.log(e.detail);
+ //图片预览
+ wx.previewImage({
+ current: e.detail.url, // 当前显示图片的http链接
+ urls: [e.detail.url] // 需要预览的图片http链接列表
+ })
+ },
+ upload() {
+ let that = this;
+ wx.chooseImage({
+ count: 1,
+ sizeType: ['original', 'compressed'],
+ sourceType: ['album', 'camera'],
+ success(res) {
+ wx.showLoading({
+ title: '加载中',
+ })
+ const tempFilePaths = res.tempFilePaths[0];
+ //重置图片角度、缩放、位置
+ that.cropper.imgReset();
+ that.setData({
+ src: tempFilePaths
+ });
+ }
+ })
+ },
+ setWidth(e) {
+ this.setData({
+ width: e.detail.value < 10 ? 10 : e.detail.value
+ });
+ this.setData({
+ cut_left: this.cropper.data.cut_left
+ });
+ },
+ setHeight(e) {
+ this.setData({
+ height: e.detail.value < 10 ? 10 : e.detail.value
+ });
+ this.setData({
+ cut_top: this.cropper.data.cut_top
+ });
+ },
+ switchChangeDisableRatio(e) {
+ //设置宽度之后使剪裁框居中
+ this.setData({
+ disable_ratio: e.detail.value
+ });
+ },
+ setCutTop(e) {
+ this.setData({
+ cut_top: e.detail.value
+ });
+ this.setData({
+ cut_top: this.cropper.data.cut_top
+ });
+ },
+ setCutLeft(e) {
+ this.setData({
+ cut_left: e.detail.value
+ });
+ this.setData({
+ cut_left: this.cropper.data.cut_left
+ });
+ },
+ switchChangeDisableRotate(e) {
+ //开启旋转的同时不限制移动
+ if (!e.detail.value) {
+ this.setData({
+ limit_move: false,
+ disable_rotate: e.detail.value
+ });
+ } else {
+ this.setData({
+ disable_rotate: e.detail.value
+ });
+ }
+ },
+ switchChangeLimitMove(e) {
+ //限制移动的同时锁定旋转
+ if (e.detail.value) {
+ this.setData({
+ disable_rotate: true
+ });
+ }
+ this.cropper.setLimitMove(e.detail.value);
+ },
+ switchChangeDisableWidth(e) {
+ this.setData({
+ disable_width: e.detail.value
+ });
+ },
+ switchChangeDisableHeight(e) {
+ this.setData({
+ disable_height: e.detail.value
+ });
+ },
+ submit() {
+ this.cropper.getImg((obj) => {
+ app.globalData.imgSrc = obj.url;
+ wx.navigateBack({
+ delta: -1
+ })
+ });
+ },
+ rotate() {
+ //在用户旋转的基础上旋转90°
+ this.cropper.setAngle(this.cropper.data.angle += 90);
+ },
+ top() {
+ this.data.top = setInterval(() => {
+ this.cropper.setTransform({
+ y: -3
+ });
+ }, 1000 / 60)
+ },
+ bottom() {
+ this.data.bottom = setInterval(() => {
+ this.cropper.setTransform({
+ y: 3
+ });
+ }, 1000 / 60)
+ },
+ left() {
+ this.data.left = setInterval(() => {
+ this.cropper.setTransform({
+ x: -3
+ });
+ }, 1000 / 60)
+ },
+ right() {
+ this.data.right = setInterval(() => {
+ this.cropper.setTransform({
+ x: 3
+ });
+ }, 1000 / 60)
+ },
+ narrow() {
+ this.data.narrow = setInterval(() => {
+ this.cropper.setTransform({
+ scale: -0.02
+ });
+ }, 1000 / 60)
+ },
+ enlarge() {
+ this.data.enlarge = setInterval(() => {
+ this.cropper.setTransform({
+ scale: 0.02
+ });
+ }, 1000 / 60)
+ },
+ end(e) {
+ clearInterval(this.data[e.currentTarget.dataset.type]);
+ },
+})
\ No newline at end of file
diff --git a/demo/cropper/cropper.json b/demo/cropper/cropper.json
new file mode 100644
index 0000000000000000000000000000000000000000..ffa73b7db795348e47a510305a4646fcbb6d4103
--- /dev/null
+++ b/demo/cropper/cropper.json
@@ -0,0 +1,10 @@
+{
+ "navigationBarTitleText": "image-cropper",
+ "disableScroll": true,
+ "navigationBarBackgroundColor": "#292929",
+ "navigationBarTextStyle": "white",
+ "backgroundColor": "#292929",
+ "usingComponents": {
+ "image-cropper": "../component/image-cropper/image-cropper"
+ }
+}
\ No newline at end of file
diff --git a/demo/cropper/cropper.wxml b/demo/cropper/cropper.wxml
new file mode 100644
index 0000000000000000000000000000000000000000..b86834297376776defc4122539b8327a3c525e98
--- /dev/null
+++ b/demo/cropper/cropper.wxml
@@ -0,0 +1,27 @@
+
+
+
+
+点击中间裁剪框可查看裁剪后的图片
+
+ 锁定裁剪框宽
+
+ 锁定裁剪框高
+
+
+ 锁定比例
+
+
+ 锁定旋转
+
+ 限制移动
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/cropper/cropper.wxss b/demo/cropper/cropper.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..1421bfa877c41d6040415b68e63ffa32b326d597
--- /dev/null
+++ b/demo/cropper/cropper.wxss
@@ -0,0 +1,218 @@
+.image-cropper {
+ background: rgba(14, 13, 13, .8);
+ position: fixed;
+
+ .top {
+ position: absolute;
+ width: 100%;
+ top: 10rpx;
+ display: flex;
+ flex-flow: wrap;
+ z-index: 10;
+ color: white;
+ justify-content: space-around;
+ }
+
+ .hint {
+ position: absolute;
+ top: 10rpx;
+ width: 100%;
+ font-size: 33rpx;
+ text-align: center;
+ color: white;
+ z-index: 10;
+ }
+
+ page {
+ background: white;
+ }
+
+ view {
+ font-size: 30rpx;
+ }
+
+ .bottom {
+ position: absolute;
+ width: 100%;
+ bottom: 50rpx;
+ display: flex;
+ z-index: 10;
+ justify-content: center;
+ align-items: center;
+ flex-wrap: wrap;
+ height: 210rpx;
+ }
+
+ button {
+ font-size: 27rpx;
+ z-index: 2;
+ padding: 0 20rpx;
+ height: 60rpx;
+ min-width: 70rpx;
+ margin: 0 4rpx;
+ }
+
+ .input {
+ display: flex;
+ height: 50rpx;
+ width: 50%;
+ }
+
+ .input>.label {
+ min-width: 150rpx;
+ font-size: 30rpx;
+ height: 50rpx;
+ line-height: 50rpx;
+ }
+
+ .input>input {
+ margin-left: 10rpx;
+ text-align: center;
+ max-width: 160rpx;
+ border: 1px solid rgb(255, 255, 255);
+ height: 50rpx;
+ line-height: 50rpx;
+ min-height: 50rpx;
+ box-sizing: border-box;
+ }
+
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ z-index: 1;
+}
+
+.image-cropper .main {
+ position: absolute;
+ width: 100vw;
+ height: 100vh;
+ overflow: hidden;
+}
+
+.image-cropper .content {
+ z-index: 9;
+ position: absolute;
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ pointer-events: none;
+}
+
+.image-cropper .bg_black {
+ background: rgba(0, 0, 0, 0.8) !important;
+}
+
+.image-cropper .bg_gray {
+ background: rgba(0, 0, 0, 0.45);
+ transition-duration: .35s;
+}
+
+.image-cropper .content>.content_top {
+ pointer-events: none;
+}
+
+.image-cropper .content>.content_middle {
+ display: flex;
+ height: 200px;
+ width: 100%;
+}
+
+.image-cropper .content_middle_middle {
+ width: 200px;
+ box-sizing: border-box;
+ position: relative;
+ transition-duration: .3s;
+}
+
+.image-cropper .content_middle_right {
+ flex: auto;
+}
+
+.image-cropper .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 .image-cropper-canvas {
+ position: fixed;
+ background: white;
+ width: 150px;
+ height: 150px;
+ z-index: 10;
+ top: -200%;
+ pointer-events: none;
+}
+
+.image-cropper .border {
+ background: white;
+ pointer-events: auto;
+ position: absolute;
+}
+
+.image-cropper .border-top-left {
+ left: -2.5px;
+ top: -2.5px;
+ height: 2.5px;
+ width: 33rpx;
+}
+
+.image-cropper .border-top-right {
+ right: -2.5px;
+ top: -2.5px;
+ height: 2.5px;
+ width: 33rpx;
+}
+
+.image-cropper .border-right-top {
+ top: -1px;
+ width: 2.5px;
+ height: 30rpx;
+ right: -2.5px;
+}
+
+.image-cropper .border-right-bottom {
+ width: 2.5px;
+ height: 30rpx;
+ right: -2.5px;
+ bottom: -1px;
+}
+
+.image-cropper .border-bottom-left {
+ height: 2.5px;
+ width: 33rpx;
+ bottom: -2.5px;
+ left: -2.5px;
+}
+
+.image-cropper .border-bottom-right {
+ height: 2.5px;
+ width: 33rpx;
+ bottom: -2.5px;
+ right: -2.5px;
+}
+
+.image-cropper .border-left-top {
+ top: -1px;
+ width: 2.5px;
+ height: 30rpx;
+ left: -2.5px;
+}
+
+.image-cropper .border-left-bottom {
+ width: 2.5px;
+ height: 30rpx;
+ left: -2.5px;
+ bottom: -1px;
+}
\ No newline at end of file
diff --git a/demo/index/index.js b/demo/index/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..7c21264a2e9be5d3f2d12fa81569d0f9d30d811e
--- /dev/null
+++ b/demo/index/index.js
@@ -0,0 +1,25 @@
+const app = getApp()
+
+Page({
+ data: {
+ src: ''
+ },
+ toCropper() {
+ let url = 'https://pubser-res.zhenai.com/activity/202007/30/21480199030108.png';
+ url = '';
+ wx.navigateTo({
+ url: `/cropper/cropper?imgSrc=${url}`
+ })
+ },
+ onShow() {
+ if (app.globalData.imgSrc) {
+ this.setData({
+ src: app.globalData.imgSrc
+ })
+ }
+ },
+ onLoad: function () {
+ console.log('代码片段是一种迷你、可分享的小程序或小游戏项目,可用于分享小程序和小游戏的开发经验、展示组件和 API 的使用、复现开发问题和 Bug 等。可点击以下链接查看代码片段的详细文档:')
+ console.log('https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/devtools.html')
+ },
+})
\ No newline at end of file
diff --git a/demo/index/index.json b/demo/index/index.json
new file mode 100644
index 0000000000000000000000000000000000000000..397534a6a393ff527a837c6e733da17adcc90bd9
--- /dev/null
+++ b/demo/index/index.json
@@ -0,0 +1,7 @@
+{
+ "usingComponents": {},
+ "navigationBarTitleText": "image-cropper",
+ "navigationBarBackgroundColor": "#292929",
+ "navigationBarTextStyle": "white",
+ "backgroundColor": "#292929"
+}
\ No newline at end of file
diff --git a/demo/index/index.wxml b/demo/index/index.wxml
new file mode 100644
index 0000000000000000000000000000000000000000..da532d9f7a57a6a7e525bcbfe1fc314da1733581
--- /dev/null
+++ b/demo/index/index.wxml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+ 点击裁剪
+
+
+
\ No newline at end of file
diff --git a/demo/index/index.wxss b/demo/index/index.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..582b7ba2059ddbca82bfcb040cf7207a8a3e075d
--- /dev/null
+++ b/demo/index/index.wxss
@@ -0,0 +1,24 @@
+.intro {
+ background: #292929;
+ height: 100%;
+ width: 100%;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.head {
+ margin-top: -20%;
+ overflow: hidden;
+ border-radius: 100%;
+ width: 200rpx;
+ height: 200rpx;
+ border: 4rpx solid #fff5f7;
+ line-height: 200rpx;
+ color: #fff5f7;
+}
+
+.head>image {
+ width: 100%;
+}
\ No newline at end of file
diff --git a/demo/project.config.json b/demo/project.config.json
new file mode 100644
index 0000000000000000000000000000000000000000..b1f4e3e31a0fb22c54124da9cb6c150e6e5357e1
--- /dev/null
+++ b/demo/project.config.json
@@ -0,0 +1,70 @@
+{
+ "description": "项目配置文件",
+ "packOptions": {
+ "ignore": []
+ },
+ "setting": {
+ "urlCheck": true,
+ "scopeDataCheck": false,
+ "coverView": true,
+ "es6": true,
+ "postcss": true,
+ "compileHotReLoad": false,
+ "preloadBackgroundData": false,
+ "minified": true,
+ "autoAudits": false,
+ "newFeature": false,
+ "uglifyFileName": false,
+ "uploadWithSourceMap": true,
+ "useIsolateContext": true,
+ "nodeModules": false,
+ "enhance": false,
+ "useCompilerModule": true,
+ "userConfirmedUseCompilerModuleSwitch": false,
+ "useMultiFrameRuntime": true,
+ "useApiHook": true,
+ "useApiHostProcess": true,
+ "showShadowRootInWxmlPanel": true,
+ "packNpmManually": false,
+ "packNpmRelationList": [],
+ "minifyWXSS": true,
+ "bundle": false
+ },
+ "compileType": "miniprogram",
+ "libVersion": "2.15.0",
+ "appid": "wxef81ad8a767198db",
+ "projectname": "image-cropper",
+ "debugOptions": {
+ "hidedInDevtools": []
+ },
+ "simulatorType": "wechat",
+ "simulatorPluginLibVersion": {},
+ "condition": {
+ "search": {
+ "list": []
+ },
+ "conversation": {
+ "list": []
+ },
+ "plugin": {
+ "list": []
+ },
+ "game": {
+ "currentL": -1,
+ "list": []
+ },
+ "gamePlugin": {
+ "list": []
+ },
+ "miniprogram": {
+ "list": [
+ {
+ "id": -1,
+ "name": "cropper/cropper",
+ "pathName": "cropper/cropper",
+ "scene": null
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/demo/sitemap.json b/demo/sitemap.json
new file mode 100644
index 0000000000000000000000000000000000000000..ca02add20b581be471b8d17f887b8e8337070546
--- /dev/null
+++ b/demo/sitemap.json
@@ -0,0 +1,7 @@
+{
+ "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
+ "rules": [{
+ "action": "allow",
+ "page": "*"
+ }]
+}
\ No newline at end of file
diff --git a/src/image-cropper.js b/src/image-cropper.js
index 6957179eed6e0493977399c2c63d905c54ccdaa3..91f3b21844f4924ef76b74c43460dfc4480ee8e6 100644
--- a/src/image-cropper.js
+++ b/src/image-cropper.js
@@ -1,1120 +1,1117 @@
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,//触摸移动节流settimeout
- MOVE_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();
- //设置裁剪框大小>设置图片尺寸>绘制canvas
- this._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();
+ properties: {
+ /**
+ * 图片路径
+ */
+ 'imgSrc': {
+ type: String
},
- 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() {
- //初始化canvas
- if (!this.data.ctx){
- this.data.ctx = wx.createCanvasContext("image-cropper", this);
- }
- },
- /**
- * 根据开发者设置的图片目标尺寸计算实际尺寸
- */
- _initImageSize(){
- //处理宽高特殊单位 %>px
- if (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];
+ /**
+ * 裁剪框高度
+ */
+ '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
}
- }
- 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;
+ data: {
+ el: 'image-cropper', //暂时无用
+ info: wx.getSystemInfoSync(),
+ MOVE_THROTTLE: null, //触摸移动节流settimeout
+ MOVE_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();
+ }
+ }
}
- } 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) {
+ 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({
- width: this.data.info.windowWidth,
+ _canvas_height: this.data.height,
+ _canvas_width: this.data.width,
});
- } 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,
+ this._initCanvas();
+ this.data.imgSrc && (this.data.imgSrc = this.data.imgSrc);
+ //根据开发者设置的图片目标尺寸计算实际尺寸
+ this._initImageSize();
+ //设置裁剪框大小>设置图片尺寸>绘制canvas
+ this._computeCutSize();
+ //检查裁剪框是否在范围内
+ this._cutDetectionPosition();
+ //检查canvas是否在范围内
+ this._canvasDetectionPosition();
+ //初始化完成
+ this.triggerEvent('load', {
+ cropper: this
});
- } 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);
- break
- case 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)
- break
- case 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);
- break
- case 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;
+ 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;
+ }
- 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;
+ // getImageInfo接口传入 src: '' 会导致内存泄漏
- 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;
+ 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);
- 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() {
+ }, 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() {
+ //初始化canvas
+ if (!this.data.ctx) {
+ this.data.ctx = wx.createCanvasContext("image-cropper", this);
+ }
+ },
+ /**
+ * 根据开发者设置的图片目标尺寸计算实际尺寸
+ */
+ _initImageSize() {
+ //处理宽高特殊单位 %>px
+ if (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);
+ break
+ case 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)
+ break
+ case 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);
+ break
+ case 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() {}
}
- }
-})
+})
\ No newline at end of file
diff --git a/src/image-cropper.json b/src/image-cropper.json
index 32640e0dc44a0e9b160f85b9124714ea071ae7b6..d577adeaf94bed8407ab4f2560b1ddff63dd8bdd 100644
--- a/src/image-cropper.json
+++ b/src/image-cropper.json
@@ -1,3 +1,3 @@
{
- "component": true
+ "component": true
}
\ No newline at end of file
diff --git a/src/image-cropper.wxml b/src/image-cropper.wxml
index 12079bbb2751818e7446a2bcae7eb8ca9b4afa3e..a4a752685797d8389f0cbd4ee20888d6108d11c6 100644
--- a/src/image-cropper.wxml
+++ b/src/image-cropper.wxml
@@ -1,24 +1,24 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
-
+
diff --git a/src/image-cropper.wxss b/src/image-cropper.wxss
index b5ef0c07e5847bcf19e73c5bf41fcc389bee4169..b52c12aebd6735d94577ec61708683a7d35e241c 100644
--- a/src/image-cropper.wxss
+++ b/src/image-cropper.wxss
@@ -1,123 +1,143 @@
-.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;
-}
\ No newline at end of file
+.image-cropper {
+ background: rgba(14, 13, 13, .8);
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ z-index: 1;
+}
+
+.image-cropper .main {
+ position: absolute;
+ width: 100vw;
+ height: 100vh;
+ overflow: hidden;
+}
+
+.image-cropper .content {
+ z-index: 9;
+ position: absolute;
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ pointer-events: none;
+}
+
+.image-cropper .bg_black {
+ background: rgba(0, 0, 0, 0.8) !important;
+}
+
+.image-cropper .bg_gray {
+ background: rgba(0, 0, 0, 0.45);
+ transition-duration: .35s;
+}
+
+.image-cropper .content>.content_top {
+ pointer-events: none;
+}
+
+.image-cropper .content>.content_middle {
+ display: flex;
+ height: 200px;
+ width: 100%;
+}
+
+.image-cropper .content_middle_middle {
+ width: 200px;
+ box-sizing: border-box;
+ position: relative;
+ transition-duration: .3s;
+}
+
+.image-cropper .content_middle_right {
+ flex: auto;
+}
+
+.image-cropper .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 .image-cropper-canvas {
+ position: fixed;
+ background: white;
+ width: 150px;
+ height: 150px;
+ z-index: 10;
+ top: -200%;
+ pointer-events: none;
+}
+
+.image-cropper .border {
+ background: white;
+ pointer-events: auto;
+ position: absolute;
+}
+
+.image-cropper .border-top-left {
+ left: -2.5px;
+ top: -2.5px;
+ height: 2.5px;
+ width: 33rpx;
+}
+
+.image-cropper .border-top-right {
+ right: -2.5px;
+ top: -2.5px;
+ height: 2.5px;
+ width: 33rpx;
+}
+
+.image-cropper .border-right-top {
+ top: -1px;
+ width: 2.5px;
+ height: 30rpx;
+ right: -2.5px;
+}
+
+.image-cropper .border-right-bottom {
+ width: 2.5px;
+ height: 30rpx;
+ right: -2.5px;
+ bottom: -1px;
+}
+
+.image-cropper .border-bottom-left {
+ height: 2.5px;
+ width: 33rpx;
+ bottom: -2.5px;
+ left: -2.5px;
+}
+
+.image-cropper .border-bottom-right {
+ height: 2.5px;
+ width: 33rpx;
+ bottom: -2.5px;
+ right: -2.5px;
+}
+
+.image-cropper .border-left-top {
+ top: -1px;
+ width: 2.5px;
+ height: 30rpx;
+ left: -2.5px;
+}
+
+.image-cropper .border-left-bottom {
+ width: 2.5px;
+ height: 30rpx;
+ left: -2.5px;
+ bottom: -1px;
+}