cropper.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. // component/cropper/cropper.js
  2. const device = wx.getSystemInfoSync();
  3. var twoPoint = {
  4. x1: 0,
  5. y1: 0,
  6. x2: 0,
  7. y2: 0
  8. }
  9. Component({
  10. /**
  11. * 组件的属性列表
  12. */
  13. properties: {
  14. ratio: {
  15. type: Number,
  16. observer: function (newVal, oldVal) {
  17. this.setData({
  18. width: device.windowWidth * 0.8,
  19. height: device.windowWidth * 0.8 / newVal
  20. })
  21. }
  22. },
  23. url: {
  24. type: String,
  25. observer ( newVal, oldVal ) {
  26. this.initImg( newVal )
  27. }
  28. }
  29. },
  30. /**
  31. * 组件的初始数据
  32. */
  33. data: {
  34. width: device.windowWidth * 0.8, //剪裁框的宽度
  35. height: device.windowWidth * 0.8 / (102 / 152), //剪裁框的长度
  36. originImg: null, //存放原图信息
  37. stv: {
  38. offsetX: 0, //剪裁图片左上角坐标x
  39. offsetY: 0, //剪裁图片左上角坐标y
  40. zoom: false, //是否缩放状态
  41. distance: 0, //两指距离
  42. scale: 1, //缩放倍数
  43. rotate: 0 //旋转角度
  44. },
  45. },
  46. /**
  47. * 组件的方法列表
  48. */
  49. methods: {
  50. uploadTap() {
  51. let _this = this
  52. wx.chooseImage({
  53. count: 1, // 默认9
  54. sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
  55. sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
  56. success(res) {
  57. _this.initImg( res.tempFilePaths[0]);
  58. }
  59. })
  60. },
  61. rotate() {
  62. let _this = this;
  63. _this.setData({
  64. 'stv.rotate': _this.data.stv.rotate % 90 == 0 ? _this.data.stv.rotate = _this.data.stv.rotate + 90 : _this.data.stv.rotate = 0
  65. })
  66. },
  67. back(){
  68. this.triggerEvent("getBack", {})
  69. },
  70. // canvas剪裁图片
  71. cropperImg() {
  72. let _this = this;
  73. let ctx = wx.createCanvasContext('imgcrop',this);
  74. let cropData = _this.data.stv;
  75. ctx.save();
  76. // 缩放偏移值
  77. let x = (_this.data.originImg.width - _this.data.originImg.width * cropData.scale) / 2;
  78. let y = (_this.data.originImg.height - _this.data.originImg.height * cropData.scale) / 2;
  79. //画布中点坐标转移到图片中心
  80. let movex = (cropData.offsetX + x) * 2 + _this.data.originImg.width * cropData.scale;
  81. let movey = (cropData.offsetY + y) * 2 + _this.data.originImg.height * cropData.scale;
  82. ctx.translate(movex, movey);
  83. ctx.rotate(cropData.rotate * Math.PI / 180);
  84. ctx.translate(-movex, -movey);
  85. ctx.drawImage(_this.data.originImg.url, (cropData.offsetX + x) * 2, (cropData.offsetY + y) * 2, _this.data.originImg.width * 2 * cropData.scale, _this.data.originImg.height * 2 * cropData.scale);
  86. ctx.restore();
  87. ctx.draw(false, ()=> {
  88. wx.canvasToTempFilePath({
  89. canvasId: 'imgcrop',
  90. success(response) {
  91. _this.triggerEvent("getCropperImg", { url: response.tempFilePath, type: _this.data.originImg.type });
  92. },
  93. fail( e ) {
  94. wx.showToast({
  95. title: '生成图片失败',
  96. icon: 'none'
  97. })
  98. }
  99. }, this);
  100. });
  101. },
  102. initImg(url) {
  103. wx.showToast({
  104. icon: 'loading',
  105. mask: true
  106. });
  107. let _this = this;
  108. wx.getImageInfo({
  109. src: url,
  110. success(resopne) {
  111. let innerAspectRadio = resopne.width / resopne.height;
  112. if (innerAspectRadio < _this.data.width / _this.data.height) {
  113. _this.setData({
  114. originImg: {
  115. url: url,
  116. width: _this.data.width,
  117. height: _this.data.width / innerAspectRadio
  118. },
  119. stv: {
  120. offsetX: 0,
  121. offsetY: 0 - Math.abs((_this.data.height - _this.data.width / innerAspectRadio) / 2),
  122. zoom: false, //是否缩放状态
  123. distance: 0, //两指距离
  124. scale: 1, //缩放倍数
  125. rotate: 0
  126. },
  127. })
  128. } else {
  129. _this.setData({
  130. originImg: {
  131. url: url,
  132. height: _this.data.height,
  133. width: _this.data.height * innerAspectRadio
  134. },
  135. stv: {
  136. offsetX: 0 - Math.abs((_this.data.width - _this.data.height * innerAspectRadio) / 2),
  137. offsetY: 0,
  138. zoom: false, //是否缩放状态
  139. distance: 0, //两指距离
  140. scale: 1, //缩放倍数
  141. rotate: 0
  142. }
  143. })
  144. }
  145. }
  146. })
  147. },
  148. //事件处理函数
  149. touchstartCallback: function (e) {
  150. if (e.touches.length === 1) {
  151. let { clientX, clientY } = e.touches[0];
  152. this.startX = clientX;
  153. this.startY = clientY;
  154. this.touchStartEvent = e.touches;
  155. } else {
  156. let xMove = e.touches[1].clientX - e.touches[0].clientX;
  157. let yMove = e.touches[1].clientY - e.touches[0].clientY;
  158. let distance = Math.sqrt(xMove * xMove + yMove * yMove);
  159. twoPoint.x1 = e.touches[0].pageX * 2
  160. twoPoint.y1 = e.touches[0].pageY * 2
  161. twoPoint.x2 = e.touches[1].pageX * 2
  162. twoPoint.y2 = e.touches[1].pageY * 2
  163. this.setData({
  164. 'stv.distance': distance,
  165. 'stv.zoom': true, //缩放状态
  166. })
  167. }
  168. },
  169. //图片手势动态缩放
  170. touchmoveCallback: function (e) {
  171. let _this = this
  172. fn(_this, e)
  173. },
  174. touchendCallback: function (e) {
  175. //触摸结束
  176. if (e.touches.length === 0) {
  177. this.setData({
  178. 'stv.zoom': false, //重置缩放状态
  179. })
  180. }
  181. }
  182. }
  183. })
  184. /**
  185. * fn:延时调用函数
  186. * delay:延迟多长时间
  187. * mustRun:至少多长时间触发一次
  188. */
  189. var throttle = function (fn, delay, mustRun) {
  190. var timer = null,
  191. previous = null;
  192. return function () {
  193. var now = +new Date(),
  194. context = this,
  195. args = arguments;
  196. if (!previous) previous = now;
  197. var remaining = now - previous;
  198. if (mustRun && remaining >= mustRun) {
  199. fn.apply(context, args);
  200. previous = now;
  201. } else {
  202. clearTimeout(timer);
  203. timer = setTimeout(function () {
  204. fn.apply(context, args);
  205. }, delay);
  206. }
  207. }
  208. }
  209. var touchMove = function (_this, e) {
  210. //触摸移动中
  211. if (e.touches.length === 1) {
  212. //单指移动
  213. if (_this.data.stv.zoom) {
  214. //缩放状态,不处理单指
  215. return;
  216. }
  217. let { clientX, clientY } = e.touches[0];
  218. let offsetX = clientX - _this.startX;
  219. let offsetY = clientY - _this.startY;
  220. _this.startX = clientX;
  221. _this.startY = clientY;
  222. let { stv } = _this.data;
  223. stv.offsetX += offsetX;
  224. stv.offsetY += offsetY;
  225. stv.offsetLeftX = -stv.offsetX;
  226. stv.offsetLeftY = -stv.offsetLeftY;
  227. _this.setData({
  228. stv: stv
  229. });
  230. } else if (e.touches.length === 2) {
  231. //计算旋转
  232. let preTwoPoint = JSON.parse(JSON.stringify(twoPoint))
  233. twoPoint.x1 = e.touches[0].pageX * 2
  234. twoPoint.y1 = e.touches[0].pageY * 2
  235. twoPoint.x2 = e.touches[1].pageX * 2
  236. function vector(x1, y1, x2, y2) {
  237. this.x = x2 - x1;
  238. this.y = y2 - y1;
  239. };
  240. //计算点乘
  241. function calculateVM(vector1, vector2) {
  242. return (vector1.x * vector2.x + vector1.y * vector2.y) / (Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y) * Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y));
  243. }
  244. //计算叉乘
  245. function calculateVC(vector1, vector2) {
  246. return (vector1.x * vector2.y - vector2.x * vector1.y) > 0 ? 1 : -1;
  247. }
  248. let vector1 = new vector(preTwoPoint.x1, preTwoPoint.y1, preTwoPoint.x2, preTwoPoint.y2);
  249. let vector2 = new vector(twoPoint.x1, twoPoint.y1, twoPoint.x2, twoPoint.y2);
  250. let cos = calculateVM(vector1, vector2);
  251. let angle = Math.acos(cos) * 180 / Math.PI;
  252. let direction = calculateVC(vector1, vector2);
  253. let _allDeg = direction * angle;
  254. // 双指缩放
  255. let xMove = e.touches[1].clientX - e.touches[0].clientX;
  256. let yMove = e.touches[1].clientY - e.touches[0].clientY;
  257. let distance = Math.sqrt(xMove * xMove + yMove * yMove);
  258. let distanceDiff = distance - _this.data.stv.distance;
  259. let newScale = _this.data.stv.scale + 0.005 * distanceDiff;
  260. if (Math.abs(_allDeg) > 1) {
  261. _this.setData({
  262. 'stv.rotate': _this.data.stv.rotate + _allDeg
  263. })
  264. } else {
  265. //双指缩放
  266. let xMove = e.touches[1].clientX - e.touches[0].clientX;
  267. let yMove = e.touches[1].clientY - e.touches[0].clientY;
  268. let distance = Math.sqrt(xMove * xMove + yMove * yMove);
  269. let distanceDiff = distance - _this.data.stv.distance;
  270. let newScale = _this.data.stv.scale + 0.005 * distanceDiff;
  271. if (newScale < 0.2 || newScale > 2.5) {
  272. return;
  273. }
  274. _this.setData({
  275. 'stv.distance': distance,
  276. 'stv.scale': newScale,
  277. })
  278. }
  279. } else {
  280. return;
  281. }
  282. }
  283. //为touchMove函数节流
  284. const fn = throttle(touchMove, 10, 10);