wm-poster.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. <template>
  2. <view class="Canvas-view">
  3. <canvas :canvas-id="CanvasID" :style="{ width: canvasW + 'px', height: canvasH + 'px' }"></canvas>
  4. <view class="Canvas-two"><canvas canvas-id="CanvasTwo" :style="{ width: canvasWT + 'px', height: canvasHT + 'px' }"></canvas></view>
  5. <view class="btn-view primary-bg" @click="saveImg">保存图片</view>
  6. <view class="btn-view-close" @click="cancelImg">取消</view>
  7. </view>
  8. </template>
  9. <script>
  10. import { getName } from '@/access/common.js';
  11. var _this;
  12. export default {
  13. name: 'wm-poster',
  14. props: {
  15. CanvasID: {
  16. //CanvasID 等同于 canvas-id
  17. Type: String,
  18. default: 'PosterCanvas'
  19. },
  20. imgSrc: {
  21. //展示图
  22. Type: String,
  23. default: ''
  24. },
  25. QrSrc: {
  26. //二维码
  27. Type: String,
  28. default: ''
  29. },
  30. Title: {
  31. //文本内容
  32. Type: String,
  33. default: ''
  34. },
  35. TitleColor: {
  36. //标题颜色
  37. Type: String,
  38. default: '#333333'
  39. },
  40. LineType: {
  41. //标题显示行数 (注超出2行显示会导致画布布局絮乱)
  42. Type: [String, Boolean],
  43. default: true
  44. },
  45. PriceTxt: {
  46. //价格值
  47. Type: String,
  48. default: ''
  49. },
  50. PriceColor: {
  51. //价格颜色
  52. Type: String,
  53. default: '#e31d1a'
  54. },
  55. OriginalTxt: {
  56. //原价值
  57. Type: String,
  58. default: ''
  59. },
  60. OriginalColor: {
  61. //默认颜色(如原价与扫描二维码颜色)
  62. Type: String,
  63. default: '#b8b8b8'
  64. },
  65. Width: {
  66. //画布宽度 (高度根据图片比例计算 单位upx)
  67. Type: String,
  68. default: 750
  69. },
  70. WidthT: {
  71. //画布宽度 (高度根据图片比例计算 单位upx)
  72. Type: String,
  73. default: 1500
  74. },
  75. CanvasBg: {
  76. //canvas画布背景色
  77. Type: String,
  78. default: '#ffffff'
  79. },
  80. Referrer: {
  81. //推荐人信息
  82. Type: String,
  83. default: getName() + '推荐给你'
  84. },
  85. ViewDetails: {
  86. //描述提示文字
  87. Type: String,
  88. // #ifdef MP-TOUTIAO
  89. default: '抖音/头条APP扫描查看商品'
  90. // #endif
  91. // #ifdef MP-WEIXIN || H5 || APP-PLUS
  92. default: '微信长按或扫描查看商品',
  93. // #endif
  94. }
  95. },
  96. data() {
  97. return {
  98. canvasW: 0,
  99. canvasH: 0,
  100. canvasWT: 0,
  101. canvasHT: 0,
  102. canvasImgSrc: '',
  103. ctx: null
  104. };
  105. },
  106. methods: {
  107. async OnCanvas() {
  108. uni.showLoading({
  109. title: '海报生成中'
  110. });
  111. let imgSrc = this.imgSrc;
  112. if (this.imgSrc.slice(0, this.imgSrc.indexOf('://')) === 'http') {
  113. imgSrc = 'https' + this.imgSrc.slice(this.imgSrc.indexOf('://'));
  114. }
  115. let QrSrc = this.QrSrc;
  116. if (this.QrSrc.slice(0, this.QrSrc.indexOf('://')) === 'http') {
  117. QrSrc = 'https' + this.QrSrc.slice(this.QrSrc.indexOf('://'));
  118. }
  119. _this.ctx = uni.createCanvasContext(_this.CanvasID, this);
  120. const C_W = uni.upx2px(_this.Width), //canvas宽度
  121. C_P = uni.upx2px(0), //canvas Paddng 间距
  122. C_Q = uni.upx2px(150); //二维码或太阳码宽高
  123. let _strlineW = 0; //文本宽度
  124. let _imgInfo = await _this.getImageInfo({ imgSrc: imgSrc }); //广告图
  125. let _QrCode = await _this.getImageInfo({ imgSrc: QrSrc }); //二维码或太阳码
  126. let r = [_imgInfo.width, _imgInfo.height];
  127. let q = [_QrCode.width, _QrCode.height];
  128. let imgW = C_W - C_P * 2;
  129. if (r[1] != imgW) {
  130. r[0] = Math.floor((imgW / r[1]) * r[0]);
  131. r[1] = imgW;
  132. }
  133. if (q[0] != C_Q) {
  134. q[1] = Math.floor((C_Q / q[0]) * q[1]);
  135. q[0] = C_Q;
  136. }
  137. _this.canvasW = C_W;
  138. _this.canvasH = r[1] + q[1] + 128;
  139. _this.ctx.setFillStyle(_this.CanvasBg); //canvas背景颜色
  140. _this.ctx.fillRect(0, 0, C_W, _this.canvasH); //canvas画布大小
  141. //添加图片展示
  142. _this.ctx.drawImage(_imgInfo.path, (C_W-r[0])/2 , C_P, r[0], r[1]);
  143. //添加图片展示 end
  144. //设置文本
  145. _this.ctx.setFontSize(uni.upx2px(28)); //设置标题字体大小
  146. _this.ctx.setFillStyle(_this.TitleColor); //设置标题文本颜色
  147. let _strLastIndex = 0; //每次开始截取的字符串的索引
  148. let _strHeight = r[1] + C_P * 2 + 20; //绘制字体距离canvas顶部的初始高度
  149. let _num = 1;
  150. for (let i = 0; i < _this.Title.length; i++) {
  151. _strlineW += _this.ctx.measureText(_this.Title[i]).width;
  152. if (_strlineW > r[0]) {
  153. if (_num == 2 && _this.LineType) {
  154. //文字换行数量大于二进行省略号处理
  155. // _this.ctx.fillText(_this.Title.substring(_strLastIndex, i - 8) + '...', C_P, _strHeight);
  156. _this.ctx.fillText(_this.Title.substring(_strLastIndex, i - 8) + '...', 15, _strHeight, C_W - 30);
  157. _strlineW = 0;
  158. _strLastIndex = i;
  159. _num++;
  160. break;
  161. } else {
  162. // _this.ctx.fillText(_this.Title.substring(_strLastIndex, i), C_P, _strHeight);
  163. _this.ctx.fillText(_this.Title.substring(_strLastIndex, i), 15, _strHeight, C_W - 30);
  164. _strlineW = 0;
  165. _strHeight += 20;
  166. _strLastIndex = i;
  167. _num++;
  168. }
  169. } else if (i == _this.Title.length - 1) {
  170. // _this.ctx.fillText(_this.Title.substring(_strLastIndex, i + 1), C_P, _strHeight);
  171. _this.ctx.fillText(_this.Title.substring(_strLastIndex, i + 1), 15, _strHeight, C_W - 30);
  172. _strlineW = 0;
  173. }
  174. }
  175. //设置文本 end
  176. //设置价格
  177. _strlineW = C_P;
  178. _strHeight += uni.upx2px(60);
  179. if (_num == 1) {
  180. _strHeight += 20; //单行标题时占位符
  181. }
  182. if (_this.PriceTxt != '') {
  183. //判断是否有销售价格
  184. _this.ctx.setFillStyle(_this.PriceColor);
  185. _this.ctx.setFontSize(uni.upx2px(38));
  186. // _this.ctx.fillText(_this.PriceTxt, _strlineW, _strHeight); //商品价格
  187. _this.ctx.fillText(_this.PriceTxt, 15, _strHeight); //商品价格
  188. console.log('_this.PriceTxt:', _this.PriceTxt.length*10)
  189. // _strlineW += _this.ctx.measureText(_this.PriceTxt).width + uni.upx2px(10);
  190. _strlineW += _this.PriceTxt.length*10
  191. }
  192. if (_this.PriceTxt != '' && _this.OriginalTxt != '') {
  193. //判断是否有销售价格且原价
  194. _this.ctx.setFillStyle(_this.OriginalColor);
  195. _this.ctx.setFontSize(uni.upx2px(24));
  196. _this.ctx.fillText(_this.OriginalTxt, _strlineW + 25, _strHeight); //商品原价
  197. }
  198. _this.ctx.strokeStyle = _this.OriginalColor;
  199. _this.ctx.moveTo(_strlineW + 25, _strHeight - uni.upx2px(10)); //起点
  200. _this.ctx.lineTo(_strlineW + _this.ctx.measureText(_this.OriginalTxt).width + 25, _strHeight - uni.upx2px(10)); //终点
  201. _this.ctx.stroke();
  202. //设置价格 end
  203. //添加二维码
  204. _strHeight += uni.upx2px(20);
  205. // _this.ctx.drawImage(_QrCode.path, r[0] - q[0] + C_P, _strHeight, q[0], q[1]);
  206. _this.ctx.drawImage(_QrCode.path, q[0] + 30, _strHeight, q[0], q[1]);
  207. //添加二维码 end
  208. //添加推荐人与描述
  209. // #ifdef MP
  210. // _this.ctx.setFillStyle(_this.TitleColor);
  211. // _this.ctx.setFontSize(uni.upx2px(30));
  212. // _this.ctx.fillText(_this.Referrer, C_P, _strHeight + q[1] / 2);
  213. // #endif
  214. _this.ctx.setFillStyle(_this.OriginalColor);
  215. _this.ctx.setFontSize(uni.upx2px(24));
  216. _this.ctx.fillText(_this.ViewDetails, q[0] + 10, _strHeight + q[1] + 20);
  217. //添加推荐人与描述 end
  218. //延迟后渲染至canvas上
  219. setTimeout(function() {
  220. _this.ctx.draw(true, ret => {
  221. _this.getNewImage();
  222. });
  223. }, 600);
  224. },
  225. async OnCanvasTwo() {
  226. let imgSrc = this.imgSrc;
  227. if (this.imgSrc.slice(0, this.imgSrc.indexOf('://')) === 'http') {
  228. imgSrc = 'https' + this.imgSrc.slice(this.imgSrc.indexOf('://'));
  229. }
  230. let QrSrc = this.QrSrc;
  231. if (this.QrSrc.slice(0, this.QrSrc.indexOf('://')) === 'http') {
  232. QrSrc = 'https' + this.QrSrc.slice(this.QrSrc.indexOf('://'));
  233. }
  234. _this.ctx = uni.createCanvasContext(_this.CanvasID, this);
  235. const C_W = uni.upx2px(_this.WidthT), //canvas宽度
  236. C_P = uni.upx2px(30), //canvas Paddng 间距
  237. C_Q = uni.upx2px(150); //二维码或太阳码宽高
  238. let _strlineW = 0; //文本宽度
  239. let _imgInfo = await _this.getImageInfo({ imgSrc: imgSrc }); //广告图
  240. let _QrCode = await _this.getImageInfo({ imgSrc: QrSrc }); //二维码或太阳码
  241. let r = [_imgInfo.width, _imgInfo.height];
  242. let q = [_QrCode.width, _QrCode.height];
  243. let imgW = C_W - C_P * 2;
  244. if (r[0] != imgW) {
  245. r[1] = Math.floor((imgW / r[0]) * r[1]);
  246. r[0] = imgW;
  247. }
  248. if (q[0] != C_Q) {
  249. q[1] = Math.floor((C_Q / q[0]) * q[1]);
  250. q[0] = C_Q;
  251. }
  252. _this.canvasW = C_W;
  253. _this.canvasH = r[1] + q[1] + 128;
  254. _this.ctx.setFillStyle(_this.CanvasBg); //canvas背景颜色
  255. _this.ctx.fillRect(0, 0, C_W, _this.canvasH); //canvas画布大小
  256. //添加图片展示
  257. _this.ctx.drawImage(_imgInfo.path, C_P, C_P, r[0], r[1]);
  258. //添加图片展示 end
  259. //设置文本
  260. _this.ctx.setFontSize(uni.upx2px(28)); //设置标题字体大小
  261. _this.ctx.setFillStyle(_this.TitleColor); //设置标题文本颜色
  262. let _strLastIndex = 0; //每次开始截取的字符串的索引
  263. let _strHeight = r[1] + C_P * 2 + 10; //绘制字体距离canvas顶部的初始高度
  264. let _num = 1;
  265. for (let i = 0; i < _this.Title.length; i++) {
  266. _strlineW += _this.ctx.measureText(_this.Title[i]).width;
  267. if (_strlineW > r[0]) {
  268. if (_num == 2 && _this.LineType) {
  269. //文字换行数量大于二进行省略号处理
  270. _this.ctx.fillText(_this.Title.substring(_strLastIndex, i - 8) + '...', C_P, _strHeight);
  271. _strlineW = 0;
  272. _strLastIndex = i;
  273. _num++;
  274. break;
  275. } else {
  276. _this.ctx.fillText(_this.Title.substring(_strLastIndex, i), C_P, _strHeight);
  277. _strlineW = 0;
  278. _strHeight += 20;
  279. _strLastIndex = i;
  280. _num++;
  281. }
  282. } else if (i == _this.Title.length - 1) {
  283. _this.ctx.fillText(_this.Title.substring(_strLastIndex, i + 1), C_P, _strHeight);
  284. _strlineW = 0;
  285. }
  286. }
  287. //设置文本 end
  288. //设置价格
  289. _strlineW = C_P;
  290. _strHeight += uni.upx2px(60);
  291. if (_num == 1) {
  292. _strHeight += 20; //单行标题时占位符
  293. }
  294. if (_this.PriceTxt != '') {
  295. //判断是否有销售价格
  296. _this.ctx.setFillStyle(_this.PriceColor);
  297. _this.ctx.setFontSize(uni.upx2px(38));
  298. _this.ctx.fillText(_this.PriceTxt, _strlineW, _strHeight); //商品价格
  299. _strlineW += _this.ctx.measureText(_this.PriceTxt).width + uni.upx2px(10);
  300. }
  301. if (_this.PriceTxt != '' && _this.OriginalTxt != '') {
  302. //判断是否有销售价格且原价
  303. _this.ctx.setFillStyle(_this.OriginalColor);
  304. _this.ctx.setFontSize(uni.upx2px(24));
  305. _this.ctx.fillText(_this.OriginalTxt, _strlineW, _strHeight); //商品原价
  306. }
  307. _this.ctx.strokeStyle = _this.OriginalColor;
  308. _this.ctx.moveTo(_strlineW, _strHeight - uni.upx2px(10)); //起点
  309. _this.ctx.lineTo(_strlineW + _this.ctx.measureText(_this.OriginalTxt).width, _strHeight - uni.upx2px(10)); //终点
  310. _this.ctx.stroke();
  311. //设置价格 end
  312. //添加二维码
  313. _strHeight += uni.upx2px(20);
  314. _this.ctx.drawImage(_QrCode.path, r[0] - q[0] + C_P, _strHeight, q[0], q[1]);
  315. //添加二维码 end
  316. //添加推荐人与描述
  317. _this.ctx.setFillStyle(_this.TitleColor);
  318. _this.ctx.setFontSize(uni.upx2px(30));
  319. // _this.ctx.fillText(_this.Referrer, C_P, _strHeight + q[1] / 2);
  320. _this.ctx.setFillStyle(_this.OriginalColor);
  321. _this.ctx.setFontSize(uni.upx2px(24));
  322. _this.ctx.fillText(_this.ViewDetails, C_P, _strHeight + q[1] / 2 + 20);
  323. //添加推荐人与描述 end
  324. //延迟后渲染至canvas上
  325. setTimeout(function() {
  326. _this.ctx.draw(true, ret => {
  327. _this.getNewImage();
  328. });
  329. }, 600);
  330. },
  331. async getImageInfo({ imgSrc }) {
  332. return new Promise((resolve, errs) => {
  333. uni.getImageInfo({
  334. src: imgSrc,
  335. success: function(image) {
  336. resolve(image);
  337. },
  338. fail(err) {
  339. errs(err);
  340. }
  341. });
  342. });
  343. },
  344. getNewImage() {
  345. uni.canvasToTempFilePath(
  346. {
  347. fileType: 'jpg',
  348. canvasId: _this.CanvasID,
  349. quality: 1,
  350. complete: res => {
  351. console.log(res.tempFilePath);
  352. _this.$emit('success', res);
  353. }
  354. },
  355. this
  356. );
  357. uni.hideLoading();
  358. },
  359. cancelImg() {
  360. this.$emit('cancelImg');
  361. },
  362. saveImg() {
  363. // destWidth: _this.canvasW * 2,
  364. // destHeight: _this.canvasH * 2,
  365. uni.canvasToTempFilePath(
  366. {
  367. fileType: 'jpg',
  368. canvasId: _this.CanvasID,
  369. quality: 1,
  370. destWidth: _this.canvasW * 2,
  371. destHeight: _this.canvasH * 2,
  372. complete: res => {
  373. console.log(res.tempFilePath);
  374. uni.saveImageToPhotosAlbum({
  375. filePath: res.tempFilePath,
  376. success: function() {
  377. _this.$emit('saveImage');
  378. }
  379. });
  380. }
  381. },
  382. this
  383. );
  384. }
  385. },
  386. mounted() {
  387. _this = this;
  388. _this.OnCanvas();
  389. // _this.OnCanvasTwo();
  390. }
  391. };
  392. </script>
  393. <style lang="scss">
  394. .Canvas-view {
  395. .btn-view-close {
  396. text-align: center;
  397. line-height: 78upx;
  398. font-size: 28upx;
  399. color: #fff;
  400. border-radius: 12upx;
  401. width: 400upx;
  402. margin: 0 auto;
  403. }
  404. .btn-view {
  405. background: $base-btn-bg;
  406. text-align: center;
  407. line-height: 78upx;
  408. font-size: 28upx;
  409. color: #fff;
  410. border-radius: 12upx;
  411. width: 400upx;
  412. margin: 0 auto;
  413. margin-top: 30upx;
  414. }
  415. .Canvas-two {
  416. display: none;
  417. transform: translate(-1600upx, -1600upx);
  418. }
  419. }
  420. </style>