painter.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import Pen from './lib/pen';
  2. import Downloader from './lib/downloader';
  3. const util = require('./lib/util');
  4. const downloader = new Downloader();
  5. // 最大尝试的绘制次数
  6. const MAX_PAINT_COUNT = 5;
  7. Component({
  8. canvasWidthInPx: 0,
  9. canvasHeightInPx: 0,
  10. paintCount: 0,
  11. /**
  12. * 组件的属性列表
  13. */
  14. properties: {
  15. customStyle: {
  16. type: String,
  17. },
  18. palette: {
  19. type: Object,
  20. observer: function (newVal, oldVal) {
  21. if (this.isNeedRefresh(newVal, oldVal)) {
  22. this.paintCount = 0;
  23. this.startPaint();
  24. }
  25. },
  26. },
  27. // 启用脏检查,默认 false
  28. dirty: {
  29. type: Boolean,
  30. value: false,
  31. },
  32. },
  33. data: {
  34. picURL: '',
  35. showCanvas: true,
  36. painterStyle: '',
  37. },
  38. attached() {
  39. setStringPrototype();
  40. },
  41. methods: {
  42. /**
  43. * 判断一个 object 是否为 空
  44. * @param {object} object
  45. */
  46. isEmpty(object) {
  47. for (const i in object) {
  48. return false;
  49. }
  50. return true;
  51. },
  52. isNeedRefresh(newVal, oldVal) {
  53. if (!newVal || this.isEmpty(newVal) || (this.data.dirty && util.equal(newVal, oldVal))) {
  54. return false;
  55. }
  56. return true;
  57. },
  58. startPaint() {
  59. // console.log(this.properties.palette)
  60. if (this.isEmpty(this.properties.palette)) {
  61. return;
  62. }
  63. if (!(getApp().systemInfo && getApp().systemInfo.screenWidth)) {
  64. try {
  65. getApp().systemInfo = wx.getSystemInfoSync();
  66. } catch (e) {
  67. const error = `Painter get system info failed, ${JSON.stringify(e)}`;
  68. that.triggerEvent('imgErr', {
  69. error: error
  70. });
  71. console.error(error);
  72. return;
  73. }
  74. }
  75. screenK = getApp().systemInfo.screenWidth / 750;
  76. this.downloadImages().then((palette) => {
  77. const {
  78. width,
  79. height
  80. } = palette;
  81. try {
  82. this.canvasWidthInPx = width.toPx();
  83. this.canvasHeightInPx = height.toPx();
  84. if (!width || !height) {
  85. console.error(`You should set width and height correctly for painter, width: ${width}, height: ${height}`);
  86. return;
  87. }
  88. this.setData({
  89. painterStyle: `width:${width};height:${height};`,
  90. });
  91. const ctx = wx.createCanvasContext('k-canvas', this);
  92. const pen = new Pen(ctx, palette);
  93. pen.paint(() => {
  94. this.saveImgToLocal();
  95. });
  96. } catch (error) {
  97. console.error(`You should set width and height correctly for painter, width: ${width}, height: ${height}`);
  98. }
  99. });
  100. },
  101. downloadImages() {
  102. return new Promise((resolve, reject) => {
  103. let preCount = 0;
  104. let completeCount = 0;
  105. const paletteCopy = JSON.parse(JSON.stringify(this.properties.palette));
  106. if (paletteCopy.background) {
  107. preCount++;
  108. downloader.download(paletteCopy.background).then((path) => {
  109. paletteCopy.background = path;
  110. completeCount++;
  111. if (preCount === completeCount) {
  112. resolve(paletteCopy);
  113. }
  114. }, () => {
  115. completeCount++;
  116. if (preCount === completeCount) {
  117. resolve(paletteCopy);
  118. }
  119. });
  120. }
  121. if (paletteCopy.views) {
  122. for (const view of paletteCopy.views) {
  123. if (view && view.type === 'image' && view.url) {
  124. preCount++;
  125. /* eslint-disable no-loop-func */
  126. downloader.download(view.url).then((path) => {
  127. view.url = path;
  128. wx.getImageInfo({
  129. src: view.url,
  130. success: (res) => {
  131. // 获得一下图片信息,供后续裁减使用
  132. view.sWidth = res.width;
  133. view.sHeight = res.height;
  134. view.width = res.width;
  135. view.height = res.height;
  136. },
  137. fail: (error) => {
  138. console.error(`getImageInfo failed, ${Json.stringify(error)}`);
  139. },
  140. complete: () => {
  141. completeCount++;
  142. if (preCount === completeCount) {
  143. resolve(paletteCopy);
  144. }
  145. },
  146. });
  147. }, () => {
  148. completeCount++;
  149. if (preCount === completeCount) {
  150. resolve(paletteCopy);
  151. }
  152. });
  153. }
  154. }
  155. }
  156. if (preCount === 0) {
  157. resolve(paletteCopy);
  158. }
  159. });
  160. },
  161. saveImgToLocal() {
  162. const that = this;
  163. setTimeout(() => {
  164. wx.canvasToTempFilePath({
  165. canvasId: 'k-canvas',
  166. success: function (res) {
  167. that.getImageInfo(res.tempFilePath);
  168. },
  169. fail: function (error) {
  170. console.error(`canvasToTempFilePath failed, ${JSON.stringify(error)}`);
  171. that.triggerEvent('imgErr', {
  172. error: error
  173. });
  174. },
  175. }, this);
  176. }, 300);
  177. },
  178. getImageInfo(filePath) {
  179. const that = this;
  180. wx.getImageInfo({
  181. src: filePath,
  182. success: (infoRes) => {
  183. if (that.paintCount > MAX_PAINT_COUNT) {
  184. const error = `The result is always fault, even we tried ${MAX_PAINT_COUNT} times`;
  185. console.error(error);
  186. that.triggerEvent('imgErr', {
  187. error: error
  188. });
  189. return;
  190. }
  191. // 比例相符时才证明绘制成功,否则进行强制重绘制
  192. if (Math.abs((infoRes.width * that.canvasHeightInPx - that.canvasWidthInPx * infoRes.height) / (infoRes.height * that.canvasHeightInPx)) < 0.01) {
  193. that.triggerEvent('imgOK', {
  194. path: filePath
  195. });
  196. } else {
  197. that.startPaint();
  198. }
  199. that.paintCount++;
  200. },
  201. fail: (error) => {
  202. console.error(`getImageInfo failed, ${JSON.stringify(error)}`);
  203. that.triggerEvent('imgErr', {
  204. error: error
  205. });
  206. },
  207. });
  208. },
  209. },
  210. });
  211. let screenK = 0.5;
  212. function setStringPrototype() {
  213. /* eslint-disable no-extend-native */
  214. /**
  215. * 是否支持负数
  216. * @param {Boolean} minus 是否支持负数
  217. */
  218. String.prototype.toPx = function toPx(minus) {
  219. let reg;
  220. if (minus) {
  221. reg = /^-?[0-9]+([.]{1}[0-9]+){0,1}(rpx|px)$/g;
  222. } else {
  223. reg = /^[0-9]+([.]{1}[0-9]+){0,1}(rpx|px)$/g;
  224. }
  225. const results = reg.exec(this);
  226. if (!this || !results) {
  227. console.error(`The size: ${this} is illegal`);
  228. return 0;
  229. }
  230. const unit = results[2];
  231. const value = parseFloat(this);
  232. let res = 0;
  233. if (unit === 'rpx') {
  234. res = Math.round(value * screenK);
  235. } else if (unit === 'px') {
  236. res = value;
  237. }
  238. return res;
  239. };
  240. }