painter.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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 (
  54. !newVal ||
  55. this.isEmpty(newVal) ||
  56. (this.data.dirty && util.equal(newVal, oldVal))
  57. ) {
  58. return false;
  59. }
  60. return true;
  61. },
  62. startPaint() {
  63. // console.log(this.properties.palette)
  64. if (this.isEmpty(this.properties.palette)) {
  65. return;
  66. }
  67. if (!(getApp().systemInfo && getApp().systemInfo.screenWidth)) {
  68. try {
  69. getApp().systemInfo = wx.getSystemInfoSync();
  70. } catch (e) {
  71. const error = `Painter get system info failed, ${JSON.stringify(e)}`;
  72. that.triggerEvent("imgErr", {
  73. error: error,
  74. });
  75. console.error(error);
  76. return;
  77. }
  78. }
  79. screenK = getApp().systemInfo.screenWidth / 750;
  80. this.downloadImages().then((palette) => {
  81. const { width, height } = palette;
  82. this.canvasWidthInPx = width.toPx();
  83. this.canvasHeightInPx = height.toPx();
  84. if (!width || !height) {
  85. console.error(
  86. `You should set width and height correctly for painter, width: ${width}, height: ${height}`
  87. );
  88. return;
  89. }
  90. this.setData({
  91. painterStyle: `width:${width};height:${height};`,
  92. });
  93. const ctx = wx.createCanvasContext("k-canvas", this);
  94. const pen = new Pen(ctx, palette);
  95. pen.paint(() => {
  96. this.saveImgToLocal();
  97. });
  98. });
  99. },
  100. downloadImages() {
  101. return new Promise((resolve, reject) => {
  102. let preCount = 0;
  103. let completeCount = 0;
  104. const paletteCopy = JSON.parse(JSON.stringify(this.properties.palette));
  105. if (paletteCopy.background) {
  106. preCount++;
  107. downloader.download(paletteCopy.background).then(
  108. (path) => {
  109. paletteCopy.background = path;
  110. completeCount++;
  111. if (preCount === completeCount) {
  112. resolve(paletteCopy);
  113. }
  114. },
  115. () => {
  116. completeCount++;
  117. if (preCount === completeCount) {
  118. resolve(paletteCopy);
  119. }
  120. }
  121. );
  122. }
  123. if (paletteCopy.views) {
  124. for (const view of paletteCopy.views) {
  125. if (view && view.type === "image" && view.url) {
  126. preCount++;
  127. /* eslint-disable no-loop-func */
  128. downloader.download(view.url).then(
  129. (path) => {
  130. view.url = path;
  131. wx.getImageInfo({
  132. src: view.url,
  133. success: (res) => {
  134. // 获得一下图片信息,供后续裁减使用
  135. view.sWidth = res.width;
  136. view.sHeight = res.height;
  137. },
  138. fail: (error) => {
  139. console.error(
  140. `getImageInfo failed, ${JSON.stringify(error)}`
  141. );
  142. },
  143. complete: () => {
  144. completeCount++;
  145. if (preCount === completeCount) {
  146. resolve(paletteCopy);
  147. }
  148. },
  149. });
  150. },
  151. () => {
  152. completeCount++;
  153. if (preCount === completeCount) {
  154. resolve(paletteCopy);
  155. }
  156. }
  157. );
  158. }
  159. }
  160. }
  161. if (preCount === 0) {
  162. resolve(paletteCopy);
  163. }
  164. });
  165. },
  166. saveImgToLocal() {
  167. const that = this;
  168. setTimeout(() => {
  169. wx.canvasToTempFilePath(
  170. {
  171. canvasId: "k-canvas",
  172. success: function (res) {
  173. that.getImageInfo(res.tempFilePath);
  174. },
  175. fail: function (error) {
  176. console.error(
  177. `canvasToTempFilePath failed, ${JSON.stringify(error)}`
  178. );
  179. that.triggerEvent("imgErr", {
  180. error: error,
  181. });
  182. },
  183. },
  184. this
  185. );
  186. }, 300);
  187. },
  188. getImageInfo(filePath) {
  189. const that = this;
  190. wx.getImageInfo({
  191. src: filePath,
  192. success: (infoRes) => {
  193. if (that.paintCount > MAX_PAINT_COUNT) {
  194. const error = `The result is always fault, even we tried ${MAX_PAINT_COUNT} times`;
  195. console.error(error);
  196. that.triggerEvent("imgErr", {
  197. error: error,
  198. });
  199. return;
  200. }
  201. // 比例相符时才证明绘制成功,否则进行强制重绘制
  202. if (
  203. Math.abs(
  204. (infoRes.width * that.canvasHeightInPx -
  205. that.canvasWidthInPx * infoRes.height) /
  206. (infoRes.height * that.canvasHeightInPx)
  207. ) < 0.01
  208. ) {
  209. that.triggerEvent("imgOK", {
  210. path: filePath,
  211. });
  212. } else {
  213. that.startPaint();
  214. }
  215. that.paintCount++;
  216. },
  217. fail: (error) => {
  218. console.error(`getImageInfo failed, ${JSON.stringify(error)}`);
  219. that.triggerEvent("imgErr", {
  220. error: error,
  221. });
  222. },
  223. });
  224. },
  225. },
  226. });
  227. let screenK = 0.5;
  228. function setStringPrototype() {
  229. /* eslint-disable no-extend-native */
  230. /**
  231. * 是否支持负数
  232. * @param {Boolean} minus 是否支持负数
  233. */
  234. String.prototype.toPx = function toPx(minus) {
  235. let reg;
  236. if (minus) {
  237. reg = /^-?[0-9]+([.]{1}[0-9]+){0,1}(rpx|px)$/g;
  238. } else {
  239. reg = /^[0-9]+([.]{1}[0-9]+){0,1}(rpx|px)$/g;
  240. }
  241. const results = reg.exec(this);
  242. if (!this || !results) {
  243. console.error(`The size: ${this} is illegal`);
  244. return 0;
  245. }
  246. const unit = results[2];
  247. const value = parseFloat(this);
  248. let res = 0;
  249. if (unit === "rpx") {
  250. res = Math.round(value * screenK);
  251. } else if (unit === "px") {
  252. res = value;
  253. }
  254. return res;
  255. };
  256. }