canvasdrawer.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. Component({
  2. properties: {
  3. painting: {
  4. type: Object,
  5. value: {view: []},
  6. observer (newVal, oldVal) {
  7. if (!this.data.isPainting) {
  8. if (newVal && newVal.width && newVal.height) {
  9. this.setData({
  10. showCanvas: true,
  11. isPainting: true
  12. })
  13. this.readyPigment()
  14. }
  15. }
  16. }
  17. }
  18. },
  19. data: {
  20. showCanvas: false,
  21. width: 100,
  22. height: 100,
  23. index: 0,
  24. imageList: [],
  25. tempFileList: [],
  26. isPainting: false
  27. },
  28. ctx: null,
  29. cache: {},
  30. ready () {
  31. wx.removeStorageSync('canvasdrawer_pic_cache')
  32. this.cache = wx.getStorageSync('canvasdrawer_pic_cache') || {}
  33. this.ctx = wx.createCanvasContext('canvasdrawer', this)
  34. },
  35. methods: {
  36. readyPigment () {
  37. const { width, height, views } = this.data.painting
  38. this.setData({
  39. width,
  40. height
  41. })
  42. const inter = setInterval(() => {
  43. if (this.ctx) {
  44. clearInterval(inter)
  45. this.ctx.clearActions()
  46. this.ctx.save()
  47. this.getImageList(views)
  48. this.downLoadImages(0)
  49. }
  50. }, 100)
  51. },
  52. getImageList (views) {
  53. const imageList = []
  54. for (let i = 0; i < views.length; i++) {
  55. if (views[i].type === 'image') {
  56. imageList.push(views[i].url)
  57. }
  58. }
  59. this.setData({
  60. imageList
  61. })
  62. },
  63. downLoadImages (index) {
  64. const { imageList, tempFileList } = this.data
  65. if (index < imageList.length) {
  66. this.getImageInfo(imageList[index]).then(file => {
  67. tempFileList.push(file)
  68. this.setData({
  69. tempFileList
  70. })
  71. this.downLoadImages(index + 1)
  72. })
  73. } else {
  74. this.startPainting()
  75. }
  76. },
  77. startPainting () {
  78. const { tempFileList, painting: { views } } = this.data
  79. for (let i = 0, imageIndex = 0; i < views.length; i++) {
  80. if (views[i].type === 'image') {
  81. this.drawImage({
  82. ...views[i],
  83. url: tempFileList[imageIndex]
  84. })
  85. imageIndex++
  86. } else if (views[i].type === 'text') {
  87. if (!this.ctx.measureText) {
  88. wx.showModal({
  89. title: '提示',
  90. content: '当前微信版本过低,无法使用 measureText 功能,请升级到最新微信版本后重试。'
  91. })
  92. this.triggerEvent('getImage', {errMsg: 'canvasdrawer:version too low'})
  93. return
  94. } else {
  95. this.drawText(views[i])
  96. }
  97. } else if (views[i].type === 'rect') {
  98. this.drawRect(views[i])
  99. }
  100. }
  101. this.ctx.draw(false, () => {
  102. wx.setStorageSync('canvasdrawer_pic_cache', this.cache)
  103. this.saveImageToLocal()
  104. })
  105. },
  106. drawImage (params) {
  107. this.ctx.save();
  108. const { url, top = 0, left = 0, width = 0, height = 0,borderRadius = 0, arcType = "", x = 0, y = 0, r = 0 } = params;
  109. if (borderRadius) {
  110. this.ctx.beginPath()
  111. if (arcType == "arc"){
  112. let borderRadiusLeft, borderRadiusRight, borderRadiusTop, borderRadiusBottom;
  113. if (borderRadius.toString().indexOf("/") != -1){
  114. let radiusArr = borderRadius.replace(/\s+/,"").split("/");
  115. borderRadiusTop = parseFloat(radiusArr[0]);
  116. borderRadiusRight = parseFloat(radiusArr[1]);
  117. borderRadiusLeft = parseFloat(radiusArr[2]);
  118. borderRadiusBottom = parseFloat(radiusArr[3]);
  119. }else{
  120. borderRadiusLeft = borderRadius, borderRadiusRight = borderRadius, borderRadiusTop = borderRadius, borderRadiusBottom = borderRadius;
  121. }
  122. // 绘制左上角圆弧
  123. this.ctx.arc(left + borderRadiusTop, top + borderRadiusTop, borderRadiusTop, Math.PI, Math.PI * 1.5);
  124. // 画一条线 x终点、y终点
  125. this.ctx.lineTo(left + width - borderRadiusTop, top);
  126. // 绘制右上角圆弧
  127. this.ctx.arc(left + width - borderRadiusRight, top + borderRadiusRight, borderRadiusRight, Math.PI * 1.5, Math.PI * 2);
  128. this.ctx.lineTo(left + width, top + height - borderRadiusRight);
  129. // 绘制右下角圆弧
  130. this.ctx.arc(left + width - borderRadiusBottom, top + height - borderRadiusBottom, borderRadiusBottom, 0, Math.PI * 0.5);
  131. this.ctx.lineTo(left + borderRadiusBottom, top + height);
  132. // 绘制左下角圆弧
  133. this.ctx.arc(left + borderRadiusLeft, top + height - borderRadiusLeft, borderRadiusLeft, Math.PI * 0.5, Math.PI);
  134. this.ctx.lineTo(left, top + borderRadiusLeft);
  135. }else{
  136. this.ctx.arc(left + borderRadius, top + borderRadius, borderRadius, 0, 2 * Math.PI)
  137. }
  138. this.ctx.clip();
  139. this.ctx.drawImage(url, left, top, width, height)
  140. } else {
  141. this.ctx.drawImage(url, left, top, width, height);
  142. }
  143. this.ctx.restore()
  144. },
  145. drawText (params) {
  146. this.ctx.save()
  147. const {
  148. MaxLineNumber = 2,
  149. breakWord = false,
  150. color = 'black',
  151. content = '',
  152. fontSize = 16,
  153. top = 0,
  154. left = 0,
  155. lineHeight = 20,
  156. textAlign = 'left',
  157. width,
  158. bolder = false,
  159. textDecoration = 'none'
  160. } = params
  161. this.ctx.beginPath()
  162. this.ctx.setTextBaseline('top')
  163. this.ctx.setTextAlign(textAlign)
  164. this.ctx.setFillStyle(color)
  165. this.ctx.setFontSize(fontSize)
  166. if (!breakWord) {
  167. this.ctx.fillText(content, left, top)
  168. this.drawTextLine(left, top, textDecoration, color, fontSize, content)
  169. } else {
  170. let fillText = ''
  171. let fillTop = top
  172. let lineNum = 1
  173. for (let i = 0; i < content.length; i++) {
  174. fillText += [content[i]]
  175. if (this.ctx.measureText(fillText).width > width) {
  176. if (lineNum === MaxLineNumber) {
  177. if (i !== content.length) {
  178. fillText = fillText.substring(0, fillText.length - 1) + '...'
  179. this.ctx.fillText(fillText, left, fillTop)
  180. this.drawTextLine(left, fillTop, textDecoration, color, fontSize, fillText)
  181. fillText = ''
  182. break
  183. }
  184. }
  185. this.ctx.fillText(fillText, left, fillTop)
  186. this.drawTextLine(left, fillTop, textDecoration, color, fontSize, fillText)
  187. fillText = ''
  188. fillTop += lineHeight
  189. lineNum ++
  190. }
  191. }
  192. this.ctx.fillText(fillText, left, fillTop)
  193. this.drawTextLine(left, fillTop, textDecoration, color, fontSize, fillText)
  194. }
  195. this.ctx.restore()
  196. if (bolder) {
  197. this.drawText({
  198. ...params,
  199. left: left + 0.3,
  200. top: top + 0.3,
  201. bolder: false,
  202. textDecoration: 'none'
  203. })
  204. }
  205. },
  206. drawTextLine (left, top, textDecoration, color, fontSize, content) {
  207. if (textDecoration === 'underline') {
  208. this.drawRect({
  209. background: color,
  210. top: top + fontSize * 1.2,
  211. left: left - 1,
  212. width: this.ctx.measureText(content).width + 3,
  213. height: 1
  214. })
  215. } else if (textDecoration === 'line-through') {
  216. this.drawRect({
  217. background: color,
  218. top: top + fontSize * 0.6,
  219. left: left - 1,
  220. width: this.ctx.measureText(content).width + 3,
  221. height: 1
  222. })
  223. }
  224. },
  225. drawRect (params) {
  226. this.ctx.save()
  227. const { background, top = 0, left = 0, width = 0, height = 0 } = params
  228. this.ctx.setFillStyle(background)
  229. this.ctx.fillRect(left, top, width, height)
  230. this.ctx.restore()
  231. },
  232. getImageInfo (url) {
  233. return new Promise((resolve, reject) => {
  234. if (this.cache[url]) {
  235. resolve(this.cache[url])
  236. } else {
  237. const objExp = new RegExp(/^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/)
  238. if (objExp.test(url)) {
  239. wx.getImageInfo({
  240. src: url,
  241. complete: res => {
  242. if (res.errMsg === 'getImageInfo:ok') {
  243. this.cache[url] = res.path
  244. resolve(res.path)
  245. } else {
  246. this.triggerEvent('getImage', {errMsg: 'canvasdrawer:download fail'})
  247. reject(new Error('getImageInfo fail'))
  248. }
  249. }
  250. })
  251. } else {
  252. this.cache[url] = url
  253. resolve(url)
  254. }
  255. }
  256. })
  257. },
  258. saveImageToLocal () {
  259. const { width, height } = this.data
  260. wx.canvasToTempFilePath({
  261. x: 0,
  262. y: 0,
  263. width,
  264. height,
  265. canvasId: 'canvasdrawer',
  266. complete: res => {
  267. if (res.errMsg === 'canvasToTempFilePath:ok') {
  268. this.setData({
  269. showCanvas: false,
  270. isPainting: false,
  271. imageList: [],
  272. tempFileList: []
  273. })
  274. this.triggerEvent('getImage', {tempFilePath: res.tempFilePath, errMsg: 'canvasdrawer:ok'})
  275. } else {
  276. this.triggerEvent('getImage', {errMsg: 'canvasdrawer:fail'})
  277. }
  278. }
  279. }, this)
  280. }
  281. }
  282. })