modelQr.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. <template>
  2. <view class="container">
  3. <view class="" style="height: 80rpx;">
  4. </view>
  5. <canvas :style="{ width: canvasW + 'px', height: canvasH + 'px',}" canvas-id="myCanvas" id="myCanvas"
  6. class="hb"></canvas>
  7. <!-- #ifndef MP -->
  8. <div class="preserve">
  9. <div class="line"></div>
  10. <div class="tip">长按保存图片</div>
  11. <div class="line"></div>
  12. </div>
  13. <!-- #endif -->
  14. <!-- #ifdef MP -->
  15. <view class='keep' @click='saveShareQrcode' v-if="!loading">保存海报</view>
  16. <!-- #endif -->
  17. </view>
  18. </template>
  19. <script>
  20. import {
  21. getWxmpShowQrcode
  22. } from '@/api/model.js';
  23. import {
  24. mapState
  25. } from 'vuex';
  26. export default {
  27. // #ifdef MP
  28. onShareAppMessage: function(res) {
  29. // if (res.from === 'button') {
  30. // 保存邀请人
  31. let path = '/pages/index/index?' + 'spread=' + this.userInfo.uid;
  32. let data = {
  33. path: path,
  34. imageUrl: this.poster,
  35. title: this.userInfo.nickname + '邀请您进入母婴界严选'
  36. };
  37. return data;
  38. // }
  39. },
  40. // #endif
  41. data() {
  42. return {
  43. bl: 0.95, //画布比例
  44. canvasW: '',
  45. canvasH: '',
  46. bgimg: '',
  47. qrimg: '',
  48. shareList: [],
  49. swiperIndex: 0,
  50. poster: '', // 当前海报
  51. loading: true,
  52. modelid: '',
  53. avatar: '' //分享头像
  54. }
  55. },
  56. onLoad(option) {
  57. // 保存邀请图模版id
  58. this.modelid = option.modelid;
  59. this.avatar = option.avatar
  60. this.loadData();
  61. },
  62. computed: {
  63. ...mapState('user', ['userInfo']),
  64. },
  65. methods: {
  66. bindchange(e) {
  67. let shareList = this.shareList;
  68. this.swiperIndex = e.detail.current;
  69. // #ifdef MP
  70. this.poster = shareList[this.swiperIndex].poster;
  71. // #endif
  72. // // #ifndef MP
  73. // this.poster = shareList[this.swiperIndex].wap_poster;
  74. // // #endif
  75. },
  76. // 保存海报
  77. savePosterPath: function() {
  78. let that = this;
  79. if (that.poster == '') {
  80. // that.poster = that.shareList[0].poster;
  81. that.poster = this.bgimg
  82. }
  83. uni.downloadFile({
  84. url: that.poster,
  85. success(resFile) {
  86. if (resFile.statusCode === 200) {
  87. uni.getSetting({
  88. success(res) {
  89. if (!res.authSetting['scope.writePhotosAlbum']) {
  90. uni.authorize({
  91. scope: 'scope.writePhotosAlbum',
  92. success() {
  93. uni.saveImageToPhotosAlbum({
  94. filePath: resFile.tempFilePath,
  95. success: function(res) {
  96. return that.$api.msg(
  97. '保存成功');
  98. },
  99. fail: function(res) {
  100. return that.$api.msg(res
  101. .errMsg);
  102. },
  103. complete: function(res) {},
  104. })
  105. },
  106. fail() {
  107. uni.showModal({
  108. title: '您已拒绝获取相册权限',
  109. content: '是否进入权限管理,调整授权?',
  110. success(res) {
  111. if (res.confirm) {
  112. uni.openSetting({
  113. success: function(
  114. res) {
  115. console
  116. .log(
  117. res
  118. .authSetting
  119. )
  120. }
  121. });
  122. } else if (res.cancel) {
  123. return that.$api.msg(
  124. '已取消!');
  125. }
  126. }
  127. })
  128. }
  129. })
  130. } else {
  131. uni.saveImageToPhotosAlbum({
  132. filePath: resFile.tempFilePath,
  133. success: function(res) {
  134. return that.$api.msg('保存成功');
  135. },
  136. fail: function(res) {
  137. return that.$api.msg(res.errMsg);
  138. },
  139. complete: function(res) {},
  140. })
  141. }
  142. },
  143. fail(res) {
  144. }
  145. })
  146. } else {
  147. return that.$api.msg(resFile.errMsg);
  148. }
  149. },
  150. fail(res) {
  151. return that.$api.msg(res.errMsg);
  152. }
  153. })
  154. },
  155. // #ifdef MP-WEIXIN
  156. // 保存画图图片到本地
  157. seav(url) {
  158. uni.showLoading({
  159. title: '生成中...',
  160. mask: true
  161. });
  162. uni.saveImageToPhotosAlbum({
  163. filePath: this.poster,
  164. complete(result) {
  165. uni.hideLoading();
  166. uni.showToast({
  167. title: '保存图片成功!',
  168. duration: 2000,
  169. icon: 'none'
  170. });
  171. }
  172. });
  173. },
  174. // #endif
  175. // 获取海报
  176. loadData() {
  177. let obj = this;
  178. uni.showLoading({
  179. title: '获取中',
  180. mask: true,
  181. });
  182. getWxmpShowQrcode({
  183. id: obj.modelid
  184. }).then(res => {
  185. obj.bgimg = "/static/image/modelQshare.png"
  186. obj.qrcode = res.data.qrcode
  187. uni.hideLoading();
  188. obj.createPoster()
  189. })
  190. },
  191. async createPoster() {
  192. let that = this
  193. // 获取设备信息,主要获取宽度,赋值给canvasW 也就是宽度:100%
  194. this.SystemInfo = await this.getSystemInfo();
  195. // 获取商品主图,二维码信息,APP端会返回图片的本地路径(H5端只能返回原路径)
  196. this.goodsImg = await this.getImageInfo(that.bgimg);
  197. this.ewmImg = await this.getImageInfo(that.qrcode);
  198. this.avatarImg = await this.getImageInfo(that.avatar);
  199. this.canvasW = this.SystemInfo.windowWidth * that.bl; // 画布宽度
  200. this.ratio = this.SystemInfo.windowWidth / 750;
  201. let x = 750 * this.goodsImg.height / this.goodsImg.width
  202. // this.canvasW =750 * this.ratio;
  203. this.canvasH = x * this.ratio * that.bl;
  204. // 二维码宽度
  205. let ewmW = 220 * this.ratio;
  206. // 头像宽度
  207. let avatarW = 320 * this.ratio;
  208. // this.canvasH = this.goodsImg.height + this.ewmW + 200; // 画布高度 = 主图高度+二维码高度 + 文字图片的间距(大概50)
  209. // 如果主图,二维码图片,设备信息都获取成功,开始绘制海报,这里需要用setTimeout延时绘制,否则可能会出现图片不显示。
  210. if (this.avatarImg.errMsg == 'getImageInfo:ok' && this.goodsImg.errMsg == 'getImageInfo:ok' && this
  211. .ewmImg.errMsg == 'getImageInfo:ok' && this
  212. .SystemInfo.errMsg == 'getSystemInfo:ok') {
  213. uni.showToast({
  214. icon: 'loading',
  215. mask: true,
  216. duration: 10000,
  217. title: '海报绘制中',
  218. });
  219. setTimeout(() => {
  220. var ctx = uni.createCanvasContext('myCanvas', this);
  221. // 填充背景色,白色
  222. ctx.setFillStyle('#FFF'); // 默认白色
  223. ctx.fillRect(0, 0, this.canvasW, this.canvasH) // fillRect(x,y,宽度,高度)
  224. // 绘制头像
  225. ctx.drawImage(this.avatarImg.path, 195 * this.ratio, 165 * this.ratio, avatarW,
  226. avatarW)
  227. // 绘制商品主图
  228. ctx.drawImage('/' + this.goodsImg.path, 0, 0, this.canvasW, this.canvasH)
  229. // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度,二维码的宽,高)
  230. // 二维码
  231. ctx.drawImage(this.ewmImg.path, 40 * this.ratio, this.canvasH - 50 * this.ratio - ewmW,
  232. ewmW, ewmW)
  233. // 3、绘制商品标题,多余文字自动换行
  234. ctx.setFontSize(14); // setFontSize() 设置字体字号
  235. ctx.setFillStyle('#555'); // setFillStyle() 设置字体颜色
  236. let name = this.userInfo.nickname
  237. let len = 9;
  238. if (name.length > len) {
  239. name = name.substr(0, len) + '...'
  240. }
  241. ctx.fillText(name, 50 * this.ratio + ewmW, this.canvasH - ewmW / 2 - 80 * this.ratio)
  242. ctx.fillText('邀请您加入母婴数字人才库', 50 * this.ratio + ewmW, this.canvasH - ewmW / 2 - 16 * this
  243. .ratio)
  244. ctx.draw(false, (ret) => { // draw方法 把以上内容画到 canvas 中。
  245. uni.showToast({
  246. icon: 'none',
  247. title: '生成成功!',
  248. });
  249. that.loading = false
  250. that.fina = true
  251. uni.canvasToTempFilePath({ // 保存canvas为图片
  252. canvasId: 'myCanvas',
  253. quality: 1,
  254. fileType: 'jpg',
  255. complete: function(res) {
  256. // 在H5平台下,tempFilePath 为 base64, // 图片提示跨域 H5保存base64失败,APP端正常输出临时路径
  257. that.canvasShow = false
  258. that.shareQrcodeUrl = res.tempFilePath
  259. that.$forceUpdate()
  260. },
  261. })
  262. });
  263. }, 1500)
  264. } else {
  265. console.log('err')
  266. }
  267. },
  268. // 获取设备信息
  269. getSystemInfo() {
  270. return new Promise((req, rej) => {
  271. uni.getSystemInfo({
  272. success: function(res) {
  273. req(res)
  274. }
  275. });
  276. })
  277. },
  278. // 获取图片信息
  279. getImageInfo(image) {
  280. return new Promise((req, rej) => {
  281. uni.getImageInfo({
  282. src: image,
  283. success: function(res) {
  284. req(res)
  285. },
  286. fail(ee) {
  287. console.log(ee, 'ee')
  288. }
  289. });
  290. })
  291. },
  292. //保存图片
  293. saveShareQrcode() {
  294. uni.saveImageToPhotosAlbum({
  295. filePath: this.shareQrcodeUrl,
  296. success: (res) => {
  297. uni.showToast({
  298. icon: 'none',
  299. position: 'bottom',
  300. title: "成功保存到相册",
  301. });
  302. },
  303. fail: (err) => {
  304. //重新提示用户打开保存图片的授权
  305. if (err.errMsg === "saveImageToPhotosAlbum:fail auth deny") {
  306. uni.showModal({
  307. title: '提示',
  308. content: '需要您授权保存相册',
  309. showCancel: false,
  310. success(res) {
  311. if (res.confirm) {
  312. uni.openSetting({
  313. success(settingdata) {
  314. if (settingdata.authSetting[
  315. 'scope.writePhotosAlbum']) {
  316. uni.showModal({
  317. title: '提示',
  318. content: '获取权限成功,再次保存图片即可成功',
  319. showCancel: false,
  320. })
  321. } else {
  322. uni.showModal({
  323. title: '提示',
  324. content: '获取权限失败,无法保存到相册',
  325. showCancel: false
  326. })
  327. }
  328. }
  329. })
  330. }
  331. }
  332. })
  333. }
  334. },
  335. })
  336. }
  337. }
  338. }
  339. </script>
  340. <style lang="scss">
  341. page {
  342. background: #a3a3a3;
  343. height: 100%;
  344. }
  345. .container {
  346. width: 100%;
  347. padding-bottom: 100rpx;
  348. .posters-box {
  349. width: 100%;
  350. height: 1000rpx;
  351. margin-top: 40rpx;
  352. .slide-image {
  353. width: 100%;
  354. height: 100%;
  355. border-radius: 15rpx;
  356. }
  357. }
  358. .posters-box .slide-image.active {
  359. transform: none;
  360. transition: all 0.2s ease-in 0s;
  361. }
  362. .posters-box .slide-image.quiet {
  363. transform: scale(0.8333333);
  364. transition: all 0.2s ease-in 0s;
  365. }
  366. .keep {
  367. font-size: 30rpx;
  368. background: $base-color;
  369. color: #fff;
  370. width: 750rpx;
  371. position: fixed;
  372. height: 100rpx;
  373. text-align: center;
  374. line-height: 100rpx;
  375. bottom: 0;
  376. left: 0;
  377. }
  378. }
  379. .preserve {
  380. color: #fff;
  381. text-align: center;
  382. margin-top: 38rpx;
  383. display: flex;
  384. align-items: center;
  385. justify-content: center;
  386. .line {
  387. width: 100rpx;
  388. height: 1px;
  389. background-color: #fff;
  390. }
  391. .tip {
  392. margin: 0 20rpx;
  393. font-size: 28rpx;
  394. }
  395. }
  396. .hb {
  397. margin: auto;
  398. border-radius: 20rpx;
  399. overflow: hidden;
  400. }
  401. </style>