poster.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. <template>
  2. <view class="content" v-if="isShow">
  3. <view style="position: relative;">
  4. <image :show-menu-by-longpress="true" :src="img" style="height: 950rpx;width: 550rpx;" @load="load"></image>
  5. <image @click.stop="isShow=false"
  6. style="position: absolute;right: -50rpx;top: -60rpx;width: 50rpx;height: 50rpx;"
  7. src="@/static/icon/close.png" mode=""></image>
  8. </view>
  9. <view class="btn" v-if="imgLoad">
  10. <!-- <view class="save-btn" @click.stop="saveImage">保存图片</view> -->
  11. <text style="color: #e8e8e8;margin-top: 16rpx;font-size: 28rpx;" >长按保存图片</text>
  12. <!-- <view class="save-btn" @click.stop="goLottery" v-if="subTitle"> 去抽奖 </view> -->
  13. </view>
  14. <canvas style="position: fixed;top: 999999999rpx;" @click.stop=""
  15. :style="{ width: canvasW + 'px', height: canvasH + 'px' }" canvas-id="my-canvas"></canvas>
  16. </view>
  17. </template>
  18. <script>
  19. export default {
  20. props: {
  21. activeId:{
  22. type:String,
  23. default:''
  24. },
  25. avatar: {
  26. type: String,
  27. default: ''
  28. },
  29. title: {
  30. type: String,
  31. default: '我是第888位大赛代言人!'
  32. },
  33. subTitle: {
  34. type: String,
  35. default: '邀请您为888号作品投票!'
  36. },
  37. desc: {
  38. type: String,
  39. default: '关注公众号参与投票!'
  40. },
  41. },
  42. data() {
  43. return {
  44. posterParams:{
  45. //背景图
  46. bgImg:"https://vote-obs.guosen-fumao.cn/ce6e19c78f1b4220b4716ea11af960f5-z70fIvNXVEQ11b0ee4e6d35913e6f22391705967fe3f.jpg",
  47. //头像大小
  48. avatarSize:160,
  49. //头像的Y坐标
  50. avatarY:500,
  51. //标题的y坐标
  52. titleY:80,
  53. //副标题的y坐标
  54. subTitleY:130
  55. },
  56. imgLoad: false,
  57. img: '',
  58. canvasW: 0,
  59. canvasH: 0,
  60. ctx: null,
  61. isShow: false,
  62. qrcode: '',
  63. }
  64. },
  65. created() {
  66. this.initParams()
  67. },
  68. methods: {
  69. initParams(){
  70. let params={
  71. activeId:this.activeId
  72. }
  73. this.$api.poster.list(params).then(res=>{
  74. const records=res.data.data.records
  75. console.log("123");
  76. if (this.$isNotEmpty(records)) {
  77. this.posterParams=records[0]
  78. }
  79. console.log(this.posterParams,"/******");
  80. })
  81. },
  82. goLottery() {
  83. uni.navigateTo({
  84. url: "/pages/webview/webview?url= " + encodeURIComponent(
  85. 'https://api.jijipai.cn/h5/#/pages/activity/lottery?drawId=12')
  86. })
  87. },
  88. load() {
  89. this.imgLoad = true
  90. },
  91. chkstrlen(str) {
  92. var strlen = 0;
  93. for (var i = 0; i < str.length; i++) {
  94. if (str.charCodeAt(i) > 255) //如果是汉字,则字符串长度加2
  95. strlen += 2;
  96. else
  97. strlen++;
  98. }
  99. return strlen;
  100. },
  101. //显示
  102. showCanvas() {
  103. this.isShow = true
  104. this.__init()
  105. },
  106. //初始化画布
  107. async __init() {
  108. uni.showLoading({
  109. title: '加载中...',
  110. mask: true
  111. })
  112. this.ctx = uni.createCanvasContext('my-canvas', this)
  113. this.canvasW = uni.upx2px(750);
  114. this.canvasH = uni.upx2px(1333);
  115. //设置画布背景透明
  116. this.ctx.setFillStyle('rgba(255, 255, 255, 0)')
  117. //设置画布大小
  118. this.ctx.fillRect(0, 0, this.canvasW, this.canvasH)
  119. //绘制圆角背景
  120. this.drawRoundRect(this.ctx, 0, 0, this.canvasW, this.canvasH, 0)
  121. //绘制标题图
  122. let bgImg = await this.getImageInfo(this.posterParams.bgImg)
  123. let hW = uni.upx2px(750);
  124. let hH = uni.upx2px(1333);
  125. this.drawRoundImg(this.ctx, bgImg.path, ((this.canvasW - hW) / 2), ((this.canvasW - hW) / 2), hW,
  126. hH, 0)
  127. //绘制头像
  128. // const userInfo = uni.getStorageSync("userInfo");
  129. let avatar = await this.getImageInfo(this.avatar)
  130. let aW = uni.upx2px(this.posterParams.avatarSize);
  131. this.drawRoundImg(this.ctx, avatar.path, (hW - aW) / 2, uni.upx2px(this.posterParams.avatarY), aW, aW, uni.upx2px(this.posterParams.avatarSize /
  132. 2))
  133. //绘制标题
  134. let titleSize = this.$isNotEmpty(this.posterParams.fontSize) && this.posterParams.fontSize != -1 ? this.posterParams.fontSize : 20
  135. let titleLen=this.chkstrlen(this.title) / 2
  136. let titleParams = {
  137. color: this.posterParams.fontColor || '#fff',
  138. x: (hW / 2) - ( titleLen * titleSize / 2),
  139. y: (hH / 2) + uni.upx2px(this.posterParams.titleY)
  140. }
  141. this.ctx.font = "19px 华文楷体"
  142. this.ctx.setFontSize(titleSize); //设置标题字体大小
  143. this.ctx.setFillStyle(titleParams.color); //设置标题文本颜色
  144. this.ctx.fillText(this.title, titleParams.x, titleParams.y)
  145. //绘制副标题
  146. let subTitleLen=this.chkstrlen(this.subTitle) / 2
  147. let subTitleParams = {
  148. color: this.posterParams.fontColor || '#fff',
  149. x: (hW / 2) - ( subTitleLen * titleSize / 2),
  150. y: (hH / 2) + uni.upx2px(this.posterParams.subTitleY)
  151. }
  152. this.ctx.setFontSize(titleSize); //设置标题字体大小
  153. this.ctx.setFillStyle(subTitleParams.color); //设置标题文本颜色
  154. this.ctx.fillText(this.subTitle, subTitleParams.x, subTitleParams.y);
  155. // this.ctx.setTextAlign('center'); //设置文字的对齐
  156. // this.textPrewrap(this.ctx, this.subTitle, subTitleParams.x, subTitleParams.y, 24, this.canvasW, 2);
  157. //小程序码
  158. // let qrSize = uni.upx2px(200)
  159. // let qrcodeImg = await this.getImageInfo(this.qrcode)
  160. // this.ctx.drawImage(qrcodeImg.path, hW / 2 + uni.upx2px(96), hH - uni.upx2px(396), qrSize, qrSize)
  161. //绘制描述
  162. // let descSize = 14
  163. // let descParams = {
  164. // color: '#fff',
  165. // x: hW / 2 + uni.upx2px(70),
  166. // y: hH - uni.upx2px(358) + qrSize
  167. // }
  168. // this.ctx.setFontSize(descSize); //设置标题字体大小
  169. // this.ctx.setFillStyle('#fff'); //设置标题文本颜色
  170. // this.ctx.fillText(this.desc, descParams.x, descParams.y)
  171. //渲染
  172. this.ctx.draw(true, (res) => {
  173. uni.hideLoading()
  174. this.canvasToTempFilePath()
  175. })
  176. },
  177. //绘制实心圆
  178. drawEmptyRound(ctx, x, y, radius) {
  179. ctx.save()
  180. ctx.beginPath();
  181. ctx.arc(x, y, radius, 0, 2 * Math.PI, true);
  182. ctx.setFillStyle('rgba(0, 0, 0, .4)')
  183. ctx.fill();
  184. ctx.restore()
  185. ctx.closePath()
  186. },
  187. //绘制虚线
  188. drawDashLine(ctx, x1, y1, x2, y2, dashLength) {
  189. ctx.setStrokeStyle("#cccccc") //设置线条的颜色
  190. ctx.setLineWidth(1) //设置线条宽度
  191. var dashLen = dashLength,
  192. xpos = x2 - x1, //得到横向的宽度;
  193. ypos = y2 - y1, //得到纵向的高度;
  194. numDashes = Math.floor(Math.sqrt(xpos * xpos + ypos * ypos) / dashLen);
  195. //利用正切获取斜边的长度除以虚线长度,得到要分为多少段;
  196. for (var i = 0; i < numDashes; i++) {
  197. if (i % 2 === 0) {
  198. ctx.moveTo(x1 + (xpos / numDashes) * i, y1 + (ypos / numDashes) * i);
  199. //有了横向宽度和多少段,得出每一段是多长,起点 + 每段长度 * i = 要绘制的起点;
  200. } else {
  201. ctx.lineTo(x1 + (xpos / numDashes) * i, y1 + (ypos / numDashes) * i);
  202. }
  203. }
  204. ctx.stroke();
  205. },
  206. //带圆角图片
  207. drawRoundImg(ctx, img, x, y, width, height, radius) {
  208. ctx.beginPath()
  209. ctx.save()
  210. // 左上角
  211. ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 1.5)
  212. // 右上角
  213. ctx.arc(x + width - radius, y + radius, radius, Math.PI * 1.5, Math.PI * 2)
  214. // 右下角
  215. ctx.arc(x + width - radius, y + height - radius, radius, 0, Math.PI * 0.5)
  216. // 左下角
  217. ctx.arc(x + radius, y + height - radius, radius, Math.PI * 0.5, Math.PI)
  218. ctx.stroke()
  219. ctx.clip()
  220. ctx.drawImage(img, x, y, width, height);
  221. ctx.restore()
  222. ctx.closePath()
  223. },
  224. //圆角矩形
  225. drawRoundRect(ctx, x, y, width, height, radius, color) {
  226. ctx.save();
  227. ctx.beginPath();
  228. ctx.setFillStyle(color);
  229. ctx.setStrokeStyle(color)
  230. ctx.setLineJoin('round'); //交点设置成圆角
  231. ctx.setLineWidth(radius);
  232. ctx.strokeRect(x + radius / 2, y + radius / 2, width - radius, height - radius);
  233. ctx.fillRect(x + radius, y + radius, width - radius * 2, height - radius * 2);
  234. ctx.stroke();
  235. ctx.closePath();
  236. },
  237. //获取图片
  238. getImageInfo(imgSrc) {
  239. return new Promise((resolve, reject) => {
  240. uni.getImageInfo({
  241. src: imgSrc,
  242. success: (image) => {
  243. resolve(image);
  244. console.log('获取图片成功', image)
  245. },
  246. fail: (err) => {
  247. reject(err);
  248. console.log('获取图片失败', err)
  249. }
  250. });
  251. });
  252. },
  253. canvasToTempFilePath() {
  254. var that = this
  255. uni.canvasToTempFilePath({
  256. canvasId: 'my-canvas',
  257. quality: 1,
  258. complete: (res) => {
  259. this.img = res.tempFilePath
  260. }
  261. }, this);
  262. },
  263. //保存图片到相册
  264. saveImage() {
  265. //判断用户授权
  266. var that = this
  267. uni.saveImageToPhotosAlbum({
  268. filePath: that.img,
  269. success(res) {
  270. uni.showToast({
  271. title: '已保存到相册',
  272. icon: 'none',
  273. duration: 2000
  274. })
  275. setTimeout(() => {
  276. that.isShow = false
  277. }, 200)
  278. }
  279. })
  280. },
  281. /**
  282. * 文字自动换行
  283. * ctx: 画布的上下文环境
  284. * content: 需要绘制的文本内容
  285. * drawX: 绘制文本的x坐标
  286. * drawY: 绘制文本的y坐标
  287. * lineHeight:文本之间的行高
  288. * lineMaxWidth:每行文本的最大宽度
  289. * lineNum:最多绘制的行数
  290. **/
  291. textPrewrap(ctx, content, drawX, drawY, lineHeight, lineMaxWidth, lineNum) {
  292. var drawTxt = ''; // 当前绘制的内容
  293. var drawLine = 1; // 第几行开始绘制
  294. var drawIndex = 0; // 当前绘制内容的索引
  295. // 判断内容是否可以一行绘制完毕
  296. if(ctx.measureText(content).width <= lineMaxWidth) {
  297. ctx.fillText(content.substring(drawIndex, i), drawX, drawY);
  298. } else {
  299. for (var i = 0; i < content.length; i++) {
  300. drawTxt += content[i];
  301. if (ctx.measureText(drawTxt).width >= lineMaxWidth) {
  302. if (drawLine >= lineNum) {
  303. ctx.fillText(content.substring(drawIndex, i) + '..', drawX, drawY);
  304. break;
  305. } else {
  306. ctx.fillText(content.substring(drawIndex, i + 1), drawX, drawY);
  307. drawIndex = i + 1;
  308. drawLine += 1;
  309. drawY += lineHeight;
  310. drawTxt = '';
  311. }
  312. } else {
  313. // 内容绘制完毕,但是剩下的内容宽度不到lineMaxWidth
  314. if (i === content.length - 1) {
  315. ctx.fillText(content.substring(drawIndex), drawX, drawY);
  316. }
  317. }
  318. }
  319. }
  320. }
  321. }
  322. }
  323. </script>
  324. <style scoped lang="scss">
  325. .content {
  326. position: fixed;
  327. top: 0;
  328. left: 0;
  329. right: 0;
  330. bottom: 0;
  331. background: rgba(0, 0, 0, .5);
  332. display: flex;
  333. flex-direction: column;
  334. justify-content: center;
  335. align-items: center;
  336. z-index: 999;
  337. padding-top: 60rpx;
  338. .save-btn {
  339. margin-top: 50rpx;
  340. color: #FFFFFF;
  341. background-color: #E72226;
  342. width: 200rpx;
  343. height: 60rpx;
  344. line-height: 60rpx;
  345. text-align: center;
  346. border-radius: 50rpx;
  347. margin: 30rpx;
  348. }
  349. .cancel-btn {
  350. background-color: #FFFFFF;
  351. color: #E72226;
  352. }
  353. }
  354. .btn {
  355. width: 100%;
  356. display: flex;
  357. justify-content: center;
  358. }
  359. </style>