25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

r-canvas.js 26 KiB

2 yıl önce
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. export default{
  2. data(){
  3. return{
  4. system_info:{}, //system info
  5. canvas_width:0, //canvas width px
  6. canvas_height:0, //canvas height px
  7. ctx:null, //canvas object
  8. canvas_id:null, //canvas id
  9. hidden:false,//Whether to hide canvas
  10. scale:1,//canvas scale
  11. r_canvas_scale:1,
  12. if_ctx:true
  13. }
  14. },
  15. methods:{
  16. /**
  17. * save r-canvas.vue object
  18. * @param {Object} that
  19. */
  20. // saveThis(that){
  21. // rCanvasThis = that
  22. // },
  23. /**
  24. * Draw round rect text
  25. * @param {Object} config
  26. * @param {Number} config.x x坐标
  27. * @param {Number} config.y y坐标
  28. * @param {Number} config.w 宽度
  29. * @param {Number} config.h 高度
  30. * @param {Number} config.radius 圆角弧度
  31. * @param {String} config.fill_color 矩形颜色
  32. */
  33. fillRoundRect(config) {
  34. return new Promise((resolve,reject)=>{
  35. let x = this.compatibilitySize(parseFloat(config.x)*this.scale)
  36. let y = this.compatibilitySize(parseFloat(config.y)*this.scale)
  37. let w = this.compatibilitySize(parseFloat(config.w)*this.scale)
  38. let h = this.compatibilitySize(parseFloat(config.h)*this.scale)
  39. let radius = config.radius?parseFloat(config.radius)*this.scale:10*this.scale
  40. let fill_color = config.fill_color || "black"
  41. // The diameter of the circle must be less than the width and height of the rectangle
  42. if (2 * radius > w || 2 * radius > h) {
  43. reject("The diameter of the circle must be less than the width and height of the rectangle")
  44. return false;
  45. }
  46. this.ctx.save();
  47. this.ctx.translate(x, y);
  48. //
  49. this.drawRoundRectPath({
  50. w: w,
  51. h: h,
  52. radius: radius
  53. });
  54. this.ctx.fillStyle = fill_color
  55. this.ctx.fill();
  56. this.ctx.restore();
  57. resolve()
  58. })
  59. },
  60. /**
  61. * Draws the sides of a rounded rectangle
  62. * @param {Object} config
  63. * @param {Number} config.w 宽度
  64. * @param {Number} config.h 高度
  65. * @param {Number} config.radius 圆角弧度
  66. */
  67. drawRoundRectPath(config) {
  68. this.ctx.beginPath(0);
  69. this.ctx.arc(config.w - config.radius, config.h - config.radius, config.radius, 0, Math.PI / 2);
  70. this.ctx.lineTo(config.radius, config.h);
  71. this.ctx.arc(config.radius, config.h - config.radius, config.radius, Math.PI / 2, Math.PI);
  72. this.ctx.lineTo(0, config.radius);
  73. this.ctx.arc(config.radius, config.radius, config.radius, Math.PI, Math.PI * 3 / 2);
  74. this.ctx.lineTo(config.w - config.radius, 0);
  75. this.ctx.arc(config.w - config.radius, config.radius, config.radius, Math.PI * 3 / 2, Math.PI * 2);
  76. this.ctx.lineTo(config.w, config.h - config.radius);
  77. this.ctx.closePath();
  78. },
  79. /**
  80. * Draw special Text,line wrapping is not supported
  81. * @param {Object} config
  82. * @param {String} config.text 文字
  83. * @param {Number} config.x x坐标
  84. * @param {Number} config.y y坐标
  85. * @param {String} config.font_color 文字颜色
  86. * @param {String} config.font_family 文字字体
  87. * @param {Number} config.font_size 文字大小(px)
  88. */
  89. drawSpecialText(params){
  90. let general = params.general
  91. let list = params.list
  92. return new Promise(async (resolve,reject)=>{
  93. if(!general){
  94. reject("general cannot be empty:101")
  95. return;
  96. }else if(list && list.length>0){
  97. for(let i in list){
  98. if(i != 0){
  99. let font_size = list[i-1].font_size?parseFloat(list[i-1].font_size):20
  100. this.ctx.setFontSize(font_size)
  101. general.x = parseFloat(general.x) + this.ctx.measureText(list[i-1].text).width
  102. }
  103. list[i].x = general.x
  104. list[i].y = general.y + (list[i].margin_top?parseFloat(list[i].margin_top):0)
  105. await this.drawText(list[i])
  106. }
  107. resolve()
  108. }else{
  109. reject("The length of config arr is less than 0")
  110. return;
  111. }
  112. })
  113. },
  114. /**
  115. * array delete empty
  116. * @param {Object} arr
  117. */
  118. arrDeleteEmpty(arr){
  119. let newArr = []
  120. for(let i in arr){
  121. if(arr[i]){
  122. newArr.push(arr[i])
  123. }
  124. }
  125. return newArr
  126. },
  127. /**
  128. * Draw Text,support line
  129. * @param {Object} config
  130. * @param {String} config.text 文字
  131. * @param {Number} config.max_width 文字最大宽度(大于宽度自动换行)
  132. * @param {Number} config.line_height 文字上下行间距
  133. * @param {Number} config.x x坐标
  134. * @param {Number} config.y y坐标
  135. * @param {String} config.font_color 文字颜色
  136. * @param {String} config.font_family 文字字体 默认值:Arial
  137. * @param {String} config.text_align 文字对齐方式(left/center/right)
  138. * @param {Number} config.font_size 文字大小(px)
  139. * @param {Boolean} config.line_through_height 中划线大小
  140. * @param {Boolean} config.line_through_color 中划线颜色
  141. * @param {String} config.font_style 规定文字样式
  142. * @param {String} config.font_variant 规定字体变体
  143. * @param {String} config.font_weight 规定字体粗细
  144. * @param {String} config.line_through_cap 线末端类型
  145. * @param {String} config.line_clamp 最大行数
  146. * @param {String} config.line_clamp_hint 超过line_clamp后,尾部显示的自定义标识 如 ...
  147. * @param {String} config.is_line_break 是否开启换行符换行
  148. *
  149. */
  150. drawText(config,configuration = {}){
  151. configuration['line_num'] = configuration.line_num?configuration.line_num:0
  152. configuration['text_width'] = configuration.text_width?configuration.text_width:0
  153. return new Promise(async (resolve,reject)=>{
  154. if(config.text){
  155. let draw_width = 0,draw_height = 0,draw_x = config.x,draw_y = config.y
  156. let font_size = config.font_size?(parseFloat(config.font_size)*this.scale):(20*this.scale)
  157. let font_color = config.font_color || "#000"
  158. let font_family = config.font_family || "Arial"
  159. let line_height = config.line_height || config.font_size || 20
  160. let text_align = config.text_align || "left"
  161. let font_weight = config.font_weight || "normal"
  162. let font_variant = config.font_variant || "normal"
  163. let font_style = config.font_style || "normal"
  164. let line_clamp_hint = config.line_clamp_hint || '...'
  165. let lineBreakJoinText = ""
  166. let max_width = config.max_width?parseFloat(config.max_width)*this.scale:0
  167. // checkout is line break
  168. if(config.is_line_break){
  169. let splitTextArr = config.text.split(/[\n]/g)
  170. if(splitTextArr && splitTextArr.length > 0){
  171. let newSplitTextArr = this.arrDeleteEmpty(splitTextArr)
  172. if(newSplitTextArr && newSplitTextArr.length > 0){
  173. lineBreakJoinText = newSplitTextArr.slice(1).join("\n")
  174. config.text = newSplitTextArr[0]
  175. }else{
  176. reject("Text cannot be empty:103")
  177. return
  178. }
  179. }else{
  180. reject("Text cannot be empty:102")
  181. return
  182. }
  183. }
  184. this.ctx.setFillStyle(font_color) // color
  185. this.ctx.textAlign = text_align;
  186. this.ctx.font = `${font_style} ${font_variant} ${font_weight} ${parseInt(font_size)}px ${font_family}`
  187. if(configuration.text_width >= this.ctx.measureText(config.text).width){
  188. draw_width = configuration.text_width
  189. }else if(max_width > 0){
  190. draw_width = max_width < this.ctx.measureText(config.text).width ? this.resetCompatibilitySize(max_width) : this.resetCompatibilitySize(this.ctx.measureText(config.text).width)
  191. }else{
  192. draw_width = this.ctx.measureText(config.text).width
  193. }
  194. configuration.text_width = draw_width / this.scale
  195. if( max_width && this.compatibilitySize(this.ctx.measureText(config.text).width) > this.compatibilitySize(max_width)){
  196. let current_text = ""
  197. let text_arr = config.text.split("")
  198. for(let i in text_arr){
  199. if( this.compatibilitySize(this.ctx.measureText(current_text+text_arr[i]).width) > this.compatibilitySize(max_width) ){
  200. // Hyphenation that is greater than the drawable width continues to draw
  201. if(config.line_clamp && parseInt(config.line_clamp) == 1){
  202. // Subtracting the current_text tail width from the line_clamp_hint width
  203. let current_text_arr = current_text.split('')
  204. let json_current_text = ''
  205. while(true){
  206. current_text_arr = current_text_arr.slice(1)
  207. json_current_text = current_text_arr.join('')
  208. if(this.compatibilitySize(this.ctx.measureText(json_current_text).width) <= this.compatibilitySize(this.ctx.measureText(line_clamp_hint).width)){
  209. current_text = current_text.replace(json_current_text,'')
  210. break;
  211. }
  212. }
  213. configuration.line_num += 1
  214. this.ctx.setFontSize(parseInt(this.compatibilitySize(font_size))) // font size
  215. this.ctx.fillText(current_text + line_clamp_hint, this.compatibilitySize(parseFloat(config.x)*this.scale), this.compatibilitySize(parseFloat(config.y)*this.scale));
  216. }else{
  217. configuration.line_num += 1
  218. this.ctx.setFontSize(parseInt(this.compatibilitySize(font_size))) // font size
  219. this.ctx.fillText(current_text, this.compatibilitySize(parseFloat(config.x)*this.scale), this.compatibilitySize(parseFloat(config.y)*this.scale));
  220. config.text = text_arr.slice(i).join("")
  221. config.y = config.y + line_height
  222. if(config.line_clamp){
  223. config.line_clamp = parseInt(config.line_clamp) - 1
  224. }
  225. await this.drawText(config,configuration)
  226. }
  227. break;
  228. }else{
  229. current_text = current_text+text_arr[i]
  230. }
  231. }
  232. }else{
  233. if(config.line_through_height){
  234. let x = parseFloat(config.x)*this.scale
  235. let w
  236. let y = parseFloat(config.y)*this.scale - (font_size / 2.6)
  237. if(text_align == "left"){
  238. w = this.ctx.measureText(config.text).width/1.1 + parseFloat(config.x)*this.scale
  239. }else if(text_align == "right"){
  240. w = parseFloat(config.x)*this.scale - this.ctx.measureText(config.text).width/1.1
  241. }else if(text_align == "center"){
  242. x = parseFloat(config.x)*this.scale - this.ctx.measureText(config.text).width / 1.1 / 2
  243. w = parseFloat(config.x)*this.scale + this.ctx.measureText(config.text).width / 1.1 / 2
  244. }
  245. this.drawLineTo({
  246. x:x,
  247. y:y,
  248. w:w,
  249. h:y,
  250. line_width:config.line_through_height,
  251. line_color:config.line_through_color,
  252. line_cap:config.line_through_cap
  253. })
  254. }
  255. configuration.line_num += 1
  256. this.ctx.setFontSize(parseInt(this.compatibilitySize(font_size))) // font size
  257. this.ctx.fillText(config.text, this.compatibilitySize(parseFloat(config.x)*this.scale), this.compatibilitySize(parseFloat(config.y)*this.scale));
  258. if(config.line_clamp){
  259. config.line_clamp = parseInt(config.line_clamp) - 1
  260. }
  261. }
  262. if(lineBreakJoinText){
  263. await this.drawText({...config,text:lineBreakJoinText,y:config.y + line_height},configuration)
  264. }
  265. draw_height = config.font_size * configuration.line_num
  266. draw_width = configuration.text_width
  267. resolve({draw_width,draw_height,draw_x,draw_y})
  268. }else{
  269. reject("Text cannot be empty:101")
  270. }
  271. })
  272. },
  273. /**
  274. * Draw Line
  275. * @param {Object} config
  276. * @param {Object} config.x x坐标
  277. * @param {Object} config.y y坐标
  278. * @param {Object} config.w 线的宽度
  279. * @param {Object} config.h 线的高度
  280. * @param {Object} config.line_width 线的宽度
  281. * @param {Object} config.line_color 线条颜色
  282. */
  283. drawLineTo(config){
  284. let x = this.compatibilitySize(config.x)
  285. let y = this.compatibilitySize(config.y)
  286. let w = this.compatibilitySize(config.w)
  287. let h = this.compatibilitySize(config.h)
  288. let line_width = config.line_width?parseFloat(config.line_width)*this.scale:1*this.scale
  289. let line_color = config.line_color || "black"
  290. let line_cap = config.line_cap || "butt"
  291. this.ctx.beginPath()
  292. this.ctx.lineCap = line_cap
  293. this.ctx.lineWidth = line_width
  294. this.ctx.strokeStyle = line_color
  295. this.ctx.moveTo(x,y)
  296. this.ctx.lineTo(w,h)
  297. this.ctx.stroke()
  298. },
  299. /**
  300. * Compatibility px
  301. * @param {Object} size
  302. */
  303. compatibilitySize(size) {
  304. let canvasSize = (parseFloat(size) / 750) * this.system_info.windowWidth
  305. canvasSize = parseFloat(canvasSize * 2)
  306. return canvasSize
  307. },
  308. /**
  309. * Restore compatibility px
  310. * @param {Object} size
  311. */
  312. resetCompatibilitySize(size) {
  313. let canvasSize = (parseFloat(size/2)/this.system_info.windowWidth) * 750
  314. return canvasSize
  315. },
  316. /**
  317. * Init canvas
  318. */
  319. init(config){
  320. return new Promise(async (resolve,reject)=>{
  321. if(!config.canvas_id){
  322. reject("Canvas ID cannot be empty, please refer to the usage example")
  323. return;
  324. }
  325. this.hidden = config.hidden
  326. this.canvas_id = config.canvas_id
  327. let system_info = await uni.getSystemInfoSync()
  328. this.system_info = system_info
  329. this.scale = config.scale&&parseFloat(config.scale)>0?parseInt(config.scale):1
  330. this.canvas_width = (config.canvas_width ? this.compatibilitySize(config.canvas_width) : system_info.windowWidth) * this.scale
  331. this.canvas_height = (config.canvas_height ? this.compatibilitySize(config.canvas_height) : system_info.windowHeight) * this.scale,
  332. this.r_canvas_scale = 1/this.scale
  333. this.ctx = uni.createCanvasContext(this.canvas_id,this)
  334. this.setCanvasConfig({
  335. global_alpha:config.global_alpha?parseFloat(config.global_alpha):1,
  336. backgroundColor:config.background_color?config.background_color:"#fff"
  337. })
  338. resolve()
  339. })
  340. },
  341. /**
  342. * clear canvas all path
  343. */
  344. clearCanvas(){
  345. return new Promise(async (resolve,reject)=>{
  346. if(!this.ctx){
  347. reject("canvas is not initialized:101")
  348. return
  349. }else{
  350. this.ctx.clearRect(0,0,parseFloat(this.canvas_width)*this.scale,parseFloat(this.canvas_height)*this.scale)
  351. await this.draw()
  352. resolve()
  353. }
  354. })
  355. },
  356. /**
  357. * Set canvas config
  358. * @param {Object} config
  359. */
  360. setCanvasConfig(config){
  361. this.ctx.globalAlpha = config.global_alpha
  362. this.ctx.fillStyle = config.backgroundColor
  363. this.ctx.fillRect(0, 0, parseFloat(this.canvas_width)*this.scale, parseFloat(this.canvas_height)*this.scale)
  364. },
  365. /**
  366. * set canvas width
  367. * @param {Object} width
  368. */
  369. setCanvasWidth(width){
  370. if(!width){
  371. uni.showToast({
  372. title:'setCanvasWidth:width error',
  373. icon:'none'
  374. })
  375. }
  376. this.canvas_width = this.compatibilitySize(parseFloat(width)) * this.scale
  377. this.ctx.width = this.canvas_width
  378. },
  379. /**
  380. * set canvas height
  381. * @param {Object} height
  382. */
  383. setCanvasHeight(height){
  384. if(!height){
  385. uni.showToast({
  386. title:'setCanvasWidth:height error',
  387. icon:'none'
  388. })
  389. }
  390. this.canvas_height = this.compatibilitySize(parseFloat(height)) * this.scale
  391. this.ctx.height = this.canvas_height
  392. },
  393. /**
  394. * Draw to filepath
  395. */
  396. draw(callback){
  397. return new Promise((resolve,reject)=>{
  398. let stop = setTimeout(()=>{
  399. this.ctx.draw(false,setTimeout(()=>{
  400. uni.canvasToTempFilePath({
  401. canvasId: this.canvas_id,
  402. quality: 1,
  403. success: (res)=>{
  404. console.log('res',res)
  405. resolve(res)
  406. callback && callback(res)
  407. },
  408. fail:(err)=>{
  409. reject(JSON.stringify(err)|| "Failed to generate poster:101")
  410. }
  411. },this)
  412. },300))
  413. clearTimeout(stop)
  414. },300)
  415. })
  416. },
  417. /**
  418. * draw rect
  419. * @param {Number} config.x x坐标
  420. * @param {Number} config.y y坐标
  421. * @param {Number} config.w 图形宽度(px)
  422. * @param {Number} config.h 图形高度(px)
  423. * @param {Number} config.color 图形颜色
  424. * @param {Number} config.is_radius 是否开启圆图(1.1.6及以下版本废弃,请使用border_radius)
  425. * @param {Number} config.border_width 边框大小
  426. * @param {Number} config.border_color 边框颜色
  427. *
  428. */
  429. drawRect(config){
  430. return new Promise(async (resolve,reject)=>{
  431. if(!config.border_width || config.border_width <=0){
  432. config.border_width = 0
  433. }else{
  434. config.border_width = parseFloat(config.border_width)
  435. }
  436. if(parseFloat(config.border_width) > 0){
  437. let sub_config = JSON.parse(JSON.stringify(config))
  438. sub_config.border_width = 0
  439. sub_config.w = config.w + config.border_width
  440. sub_config.h = config.h + config.border_width
  441. sub_config.color = config.border_color || 'black'
  442. if(sub_config.border_radius){
  443. sub_config.border_radius = parseFloat(sub_config.border_radius) + parseFloat(config.border_width) / 2
  444. }
  445. await this.drawRect(sub_config)
  446. }
  447. let color = config.color || 'white'
  448. config.x = (parseFloat(config.x) + config.border_width / 2)
  449. config.y = (parseFloat(config.y) + config.border_width / 2)
  450. config['color'] = color
  451. this.ctx.fillStyle = color;
  452. if(config.is_radius || config.border_radius){
  453. this.setNativeBorderRadius(config)
  454. this.ctx.fill()
  455. }else{
  456. console.log('config.border_width',config.border_width)
  457. this.ctx.fillRect(this.compatibilitySize(config.x*this.scale),this.compatibilitySize(config.y*this.scale),this.compatibilitySize(parseFloat(config.w)*this.scale),this.compatibilitySize(parseFloat(config.h)*this.scale))
  458. }
  459. resolve()
  460. })
  461. },
  462. /**
  463. * Draw image
  464. * @param {Object} config
  465. * @param {String} config.url 图片链接
  466. * @param {Number} config.x x坐标
  467. * @param {Number} config.y y坐标
  468. * @param {Number} config.w 图片宽度(px)
  469. * @param {Number} config.h 图片高度(px)
  470. * @param {Number} config.border_width 边大小
  471. * @param {Number} config.border_color 边颜色
  472. * @param {Number} config.is_radius 是否开启圆图(1.1.6及以下版本废弃,请使用border_radius)
  473. * @param {Number} config.border_radius 圆角弧度
  474. */
  475. drawImage(config){
  476. return new Promise(async (resolve,reject)=>{
  477. if(config.url){
  478. let type = 0 // 1、network image 2、native image 3、base64 image
  479. let image_url
  480. let reg = /^https?/ig;
  481. if(reg.test(config.url)){
  482. type = 1
  483. }else{
  484. if((config.url.indexOf("data:image/png;base64") != -1) || config.url.indexOf("data:image/jpeg;base64") != -1 || config.url.indexOf("data:image/gif;base64") != -1){
  485. type = 3
  486. }else{
  487. type = 2
  488. }
  489. }
  490. if(type == 1){
  491. // network image
  492. await this.downLoadNetworkFile(config.url).then(res=>{ // two function
  493. image_url = res
  494. }).catch(err=>{
  495. reject(err)
  496. return;
  497. })
  498. }else if(type == 2){
  499. // native image
  500. const imageInfoResult = await uni.getImageInfo({
  501. src: config.url
  502. });
  503. try{
  504. if(imageInfoResult.length <= 1){
  505. reject(imageInfoResult[0].errMsg + ':404')
  506. return
  507. }
  508. }catch(e){
  509. reject(e+':500')
  510. return
  511. }
  512. let base64 = await this.urlToBase64({url:imageInfoResult[1].path})
  513. // #ifdef MP-WEIXIN
  514. await this.base64ToNative({url:base64}).then(res=>{
  515. image_url = res
  516. }).catch(err=>{
  517. reject(JSON.stringify(err)+":501")
  518. return;
  519. })
  520. // #endif
  521. // #ifndef MP-WEIXIN
  522. image_url = base64
  523. // #endif
  524. }else if(type == 3){
  525. // #ifdef MP-WEIXIN
  526. await this.base64ToNative({url:config.url}).then(res=>{
  527. image_url = res
  528. }).catch(err=>{
  529. reject(JSON.stringify(err)+":500")
  530. return;
  531. })
  532. // #endif
  533. // #ifndef MP-WEIXIN
  534. image_url = config.url
  535. // #endif
  536. }else{
  537. reject("Other Type Errors:101")
  538. return
  539. }
  540. if(config.border_width){
  541. let border_radius = 0
  542. if(config.border_radius){
  543. let multiple = config.w / config.border_radius
  544. border_radius = (parseFloat(config.w) + parseFloat(config.border_width)) / multiple
  545. }
  546. // drawRect
  547. await this.drawRect({
  548. x:parseFloat(config.x) - parseFloat(config.border_width)/2,
  549. y:parseFloat(config.y) - parseFloat(config.border_width)/2,
  550. w:parseFloat(config.w) + parseFloat(config.border_width),
  551. h:parseFloat(config.h) + parseFloat(config.border_width),
  552. color:config.border_color,
  553. border_radius:border_radius,
  554. border_width:config.border_width,
  555. is_radius:config.is_radius
  556. })
  557. }
  558. if(config.border_radius){
  559. config.color = config.color?config.color:'rgba(0,0,0,0)'
  560. // 圆角有白边,+0.5的误差
  561. config.w = config.w + 0.3
  562. config.h = config.h + 0.3
  563. this.setNativeBorderRadius(config)
  564. }else if(config.is_radius){
  565. //已废弃 is_radius
  566. this.ctx.setStrokeStyle("rgba(0,0,0,0)")
  567. this.ctx.save()
  568. this.ctx.beginPath()
  569. this.ctx.arc(this.compatibilitySize(parseFloat(config.x)*this.scale+parseFloat(config.w)*this.scale/2), this.compatibilitySize(parseFloat(config.y)*this.scale+parseFloat(config.h)*this.scale/2), this.compatibilitySize(parseFloat(config.w)*this.scale/2), 0, 2 * Math.PI, false)
  570. this.ctx.stroke();
  571. this.ctx.clip()
  572. }
  573. await this.ctx.drawImage(image_url,this.compatibilitySize(parseFloat(config.x)*this.scale),this.compatibilitySize(parseFloat(config.y)*this.scale),this.compatibilitySize(parseFloat(config.w)*this.scale),this.compatibilitySize(parseFloat(config.h)*this.scale))
  574. this.ctx.restore() //Restore previously saved drawing context
  575. resolve()
  576. }else{
  577. let err_msg = "Links cannot be empty:101"
  578. reject(err_msg)
  579. }
  580. })
  581. },
  582. /**
  583. * base64 to native available path
  584. * @param {Object} config
  585. */
  586. base64ToNative(config){
  587. return new Promise((resolve,reject)=>{
  588. let fileName = new Date().getTime()
  589. var filePath = `${wx.env.USER_DATA_PATH}/${fileName}_rCanvas.png`
  590. wx.getFileSystemManager().writeFile({
  591. filePath: filePath,
  592. data: config.url.replace(/^data:\S+\/\S+;base64,/, ''),
  593. encoding: 'base64',
  594. success: function() {
  595. resolve(filePath)
  596. },
  597. fail: function(error) {
  598. reject(error)
  599. }
  600. })
  601. })
  602. },
  603. /**
  604. * native url to base64
  605. * @param {Object} config
  606. */
  607. urlToBase64(config){
  608. return new Promise(async (resolve,reject)=>{
  609. if (typeof window != 'undefined') {
  610. await this.downLoadNetworkFile(config.url).then(res=>{ // two function
  611. resolve(res)
  612. }).catch(err=>{
  613. reject(err)
  614. })
  615. }else if (typeof plus != 'undefined') {
  616. plus.io.resolveLocalFileSystemURL(config.url,(obj)=>{
  617. obj.file((file)=>{
  618. let fileReader = new plus.io.FileReader()
  619. fileReader.onload = (res)=>{
  620. resolve(res.target.result)
  621. }
  622. fileReader.onerror = (err)=>{
  623. reject(err)
  624. }
  625. fileReader.readAsDataURL(file)
  626. }, (err)=>{
  627. reject(err)
  628. })
  629. },(err)=>{
  630. reject(err)
  631. })
  632. }else if(typeof wx != 'undefined'){
  633. wx.getFileSystemManager().readFile({
  634. filePath: config.url,
  635. encoding: 'base64',
  636. success: function(res) {
  637. resolve('data:image/png;base64,' + res.data)
  638. },
  639. fail: function(error) {
  640. reject(error)
  641. }
  642. })
  643. }
  644. })
  645. },
  646. setNativeBorderRadius(config){
  647. let border_radius = config.border_radius?(parseFloat(config.border_radius)*this.scale):(20*this.scale)
  648. if ((parseFloat(config.w)*this.scale) < 2 * border_radius) border_radius = (parseFloat(config.w)*this.scale) / 2;
  649. if ((parseFloat(config.h)*this.scale) < 2 * border_radius) border_radius = (parseFloat(config.h)*this.scale) / 2;
  650. this.ctx.beginPath();
  651. this.ctx.moveTo(this.compatibilitySize((parseFloat(config.x)*this.scale) + border_radius), this.compatibilitySize((parseFloat(config.y)*this.scale)));
  652. this.ctx.arcTo(this.compatibilitySize((parseFloat(config.x)*this.scale) + (parseFloat(config.w)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale)), this.compatibilitySize((parseFloat(config.x)*this.scale) + (parseFloat(config.w)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale) + (parseFloat(config.h)*this.scale)), this.compatibilitySize(border_radius));
  653. this.ctx.arcTo(this.compatibilitySize((parseFloat(config.x)*this.scale) + (parseFloat(config.w)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale) + (parseFloat(config.h)*this.scale)), this.compatibilitySize((parseFloat(config.x)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale) + (parseFloat(config.h)*this.scale)), this.compatibilitySize(border_radius));
  654. this.ctx.arcTo((this.compatibilitySize(parseFloat(config.x)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale) + (parseFloat(config.h)*this.scale)), this.compatibilitySize((parseFloat(config.x)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale)), this.compatibilitySize(border_radius));
  655. this.ctx.arcTo(this.compatibilitySize((parseFloat(config.x)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale)), this.compatibilitySize((parseFloat(config.x)*this.scale) + (parseFloat(config.w)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale)), this.compatibilitySize(border_radius));
  656. this.ctx.closePath();
  657. this.ctx.strokeStyle = config.color || config.border_color || 'rgba(0,0,0,0)'; // 设置绘制边框的颜色
  658. this.ctx.stroke();
  659. this.ctx.save()
  660. this.ctx.clip();
  661. },
  662. /**
  663. * Download network file
  664. * @param {Object} url : download url
  665. */
  666. downLoadNetworkFile(url){
  667. return new Promise((resolve,reject)=>{
  668. uni.downloadFile({
  669. url,
  670. success:(res)=>{
  671. if(res.statusCode == 200){
  672. resolve(res.tempFilePath)
  673. }else{
  674. reject("Download Image Fail:102")
  675. }
  676. },
  677. fail:(err)=>{
  678. reject("Download Image Fail:101")
  679. }
  680. })
  681. })
  682. },
  683. /**
  684. * Save image to natice
  685. * @param {Object} filePath : native imageUrl
  686. */
  687. saveImage(filePath){
  688. return new Promise((resolve,reject)=>{
  689. if(!filePath){
  690. reject("FilePath cannot be null:101")
  691. return;
  692. }
  693. // #ifdef H5
  694. var createA = document.createElement("a");
  695. createA.download = filePath;
  696. createA.href = filePath;
  697. document.body.appendChild(createA);
  698. createA.click();
  699. createA.remove();
  700. resolve()
  701. // #endif
  702. // #ifndef H5
  703. uni.saveImageToPhotosAlbum({
  704. filePath: filePath,
  705. success:(res)=>{
  706. resolve(res)
  707. },
  708. fail:(err)=>{
  709. reject(err)
  710. }
  711. })
  712. // #endif
  713. })
  714. }
  715. }
  716. }