案例1
实现背景:
- 海报的尺寸依据图片大小来的,宽度百分百,高度同比例缩放,图上笼罩二维码
- 如果没有设置海报图片,应用别的图片,在底部增加一个底部的固定图片,和二维码,和图片的左上角笼罩log图
难点: - 因为是应用canvas渲染的图片,然而图片的尺寸不是固定,也就是canvas的尺寸不是固定(解决:在内部先获取图片的尺寸,传进这个组件中)
- h5是渲染失常的,微信小程序图片渲染不进去(解决:微信不反对canvas间接渲染网络图片,所以须要先缓存到本地)
<template> <view class="poster-box" ref="posterBox" @click="hidePoster"> <canvas :style="{ width: posterOptions.poster.canvasWidth + 'px', height: posterOptions.poster.canvasHeight + 'px', }" canvas-id="myCanvas" ></canvas> <!-- #ifdef MP --> <image :src="base64" class="poster-image" :draggable="false" show-menu-by-longpress="true" ></image> <!-- #endif --> <!-- #ifdef H5 --> <image class="poster-image" :src="base64" :draggable="false"></image> <!-- #endif --> </view></template><script>import Qr from '@/utils/wxqrcode'export default { name: 'poster_canvas', data() { return { base64: '', // 海报绘制成图片以便保留 } }, props: { //图片地址 posterOptions: { type: Object, require: true, }, }, mounted() { this.$nextTick(() => { this.imgToCanvas() }) }, methods: { async imgToCanvas() { let that = this let canvasHeight = this.posterOptions.isHavePosterImg ? this.posterOptions.poster.canvasHeight : this.posterOptions.poster.canvasHeight - this.posterOptions.buttonOption.h // 渲染大图 let ctx = uni.createCanvasContext('myCanvas', that) console.log(11111,this.posterOptions) ctx.drawImage( this.posterOptions.poster.url, 0, 0, this.posterOptions.poster.canvasWidth, canvasHeight ) if (!this.posterOptions.isHavePosterImg) { //渲染底部的图片 ctx.drawImage( this.posterOptions.buttonOption.bottomUrl, this.posterOptions.buttonOption.x, this.posterOptions.buttonOption.y, this.posterOptions.buttonOption.w, this.posterOptions.buttonOption.h ) } // 渲染二维码 if (this.posterOptions.QrOption.isWeChatMiniApp) { ctx.drawImage( this.posterOptions.QrOption.QrUrl, this.posterOptions.QrOption.x, this.posterOptions.QrOption.y, this.posterOptions.QrOption.w, this.posterOptions.QrOption.h ) } else { //一般二维码 let qrCodeImg = Qr.createQrCodeImg(this.posterOptions.QrOption.QrUrl, { size: parseInt(300), }) ctx.drawImage( qrCodeImg, this.posterOptions.QrOption.x, this.posterOptions.QrOption.y, this.posterOptions.QrOption.w, this.posterOptions.QrOption.h ) } if (!this.posterOptions.isHavePosterImg) { // 渲染log 和文字 ctx.drawImage( this.posterOptions.logOption.logUrl, this.posterOptions.logOption.x, this.posterOptions.logOption.y, this.posterOptions.logOption.w, this.posterOptions.logOption.h ) ctx.fillStyle = this.posterOptions.textOption.color ctx.setFontSize(22) ctx.font = 'bold arial' ctx.fillText( this.posterOptions.textOption.text, this.posterOptions.textOption.x, this.posterOptions.textOption.y ) } ctx.save() //保留 ctx.draw() //绘制 // 不加提早的话,base64有时候会赋予undefined // 把以后画布指定区域的内容导出生成指定大小的图片,并返回文件门路 setTimeout(() => { uni.canvasToTempFilePath( { canvasId: "myCanvas", fileType: "jpg", width:that.posterOptions.poster.canvasWidth, height:that.posterOptions.poster.canvasHeight, destWidth:that.posterOptions.poster.canvasWidth, destHeight:that.posterOptions.poster.canvasHeight, success: function (res) { that.base64 = res.tempFilePath; }, fail: function (error) { console.log(error, "谬误"); }, },that ); }, 500); }, hidePoster() { // #ifdef H5 this.$parent.$parent.hidePoster() // #endif // #ifndef H5 this.$parent.hidePoster() // #endif }, },}</script><style lang="scss" scoped>.poster-box { position: fixed; top: 0; z-index: 999; background-color: rgba(0, 0, 0, 0.8); width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;}.poster-image { position: absolute; top: 0; left: 0; z-index: 1000; width: 100%; height: 100%; opacity: 0;}</style>
应用
<template> <view class="contentBgc" :style="{ backgroundColor: detailObj.backgroundColor || defaultBgc }" > <image class="width-100" :src="detailObj.activityDetailHeadImg" mode="widthFix" /> <view v-for="item in detailObj.plateDtoList" :key="item.id"> <TopicThree v-if="detailObj.currentTemplate == 3" :itemObj="item" /> <TopicTwo v-if="detailObj.currentTemplate == 2" :itemObj="item" /> <TopicOne v-if="detailObj.currentTemplate == 1" :itemObj="item" /> </view> <generateaposter v-if="isGetOk" @pChangeType="showPoster" /> <SharePoster v-if="posterShow" :posterOptions="posterOptions"></SharePoster> </view></template><script>import TopicOne from './components/topicOne.vue'import TopicTwo from './components/topicTwo.vue'import TopicThree from './components/topicThree.vue'import { getSpecialTopicData } from '@/api/specialTopic.js'// #ifdef H5import { getShopWxConfig, share } from '@/utils/wxShare.js'// #endifimport mixinWxshare from '@/utils/wxShareTimeline.js' //微信分享朋友圈import generateaposter from '@/components/gpage/generateaposter.vue'import SharePoster from './components/sharePoster.vue'import { cmsWxacodeGetwxacode } from '@/api/product'export default { mixins: [mixinWxshare], components: { TopicOne, TopicTwo, TopicThree, generateaposter, SharePoster, }, onLoad(options) { this.activityUniqueCode = options.activityUniqueCode this.initData() }, data() { return { defaultBgc: '#fff', //默认背景色 share: this.$utils.getImgUrl('/static/mcShopImage/share.png'), //分享 detailObj: {}, activityUniqueCode: '', posterShow: false, isGetOk: false, posterOptions: { isHavePosterImg: false, //用户是否上传了分享海报的图片 activityName: '', textOption: { text: '', x: 0, // X轴坐标 y: 20, // Y轴坐标 color: '#9c6d06', }, poster: { url: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fci.xiaohongshu.com%2F0429f29e-85f3-5826-9608-946fc1ee103a%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fci.xiaohongshu.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664356680&t=349aa739d52cd748c50b3afd89da2ba2', canvasWidth: 0, canvasHeight: 0, }, QrOption: { QrUrl: '', // 二维码链接 x: 20, // 图片X轴坐标 y: 20, // 图片Y轴坐标 w: 90, // 图片宽度 h: 90, // 图片高度 isWeChatMiniApp: false, // 是否是微信小程序二维码 true是微信小程序, false 不是微信小程序 }, logOption: { //log logUrl: 'https://jemsfile.mochouu.com/provider/91949676/20220831/1661926456418.png', x: 15, // 图片X轴坐标 y: 15, // 图片Y轴坐标 w: 120, // 图片宽度 h: 45, // 图片高度 }, buttonOption: { //底部的图片 bottomUrl: 'https://jemsfile.mochouu.com/provider/91949676/20220831/1661930915954.png', x: 0, // 图片X轴坐标 y: 20, // 图片Y轴坐标 w: 100, // 图片宽度 h: 100, // 图片高度 }, }, screenWidth: 0, } }, mounted() {}, // #ifndef H5 /* 微信小程序分享 */ onShareAppMessage: function(res) { // console.log('分享的userCode:'+this.userInfo.userCode) let userInfo = this.$utils.getStorageSync('userInfo') let routes = getCurrentPages() // 获取以后关上过的页面路由数组 let curParam = routes[routes.length - 1].options || routes[routes.length - 1].$route.query //获取路由参数 if (userInfo) { curParam.userCode = userInfo.userCode } let a = this.$utils.pageliks(curParam, routes) let obj = { title: this.detailObj.shareTitle || this.detailObj.shareContent || '', desc:this.detailObj.shareContent||'', path: a, imageUrl: this.detailObj.shareImg || this.detailObj.verticalImgUrl || '', } return obj }, // #endif methods: { hidePoster() { this.posterShow = false }, getWxCode() { uni.showLoading({ title: '加载中', }) let that = this this.isGetOk = false let routes = getCurrentPages() // 获取以后关上过的页面路由数组 let curRoute = routes[routes.length - 1].route //获取以后页面路由 let curParam = routes[routes.length - 1].options //获取路由参数 let obj = {} obj.curRoute = curRoute obj.curParam = curParam // 拼接参数 let str = '' for (let key in obj.curParam) { str += '&' + key + '=' + obj.curParam[key] } str = str ? '?' + str.substr(1, str.length) : '' let data = { page: '/' + obj.curRoute + str, } cmsWxacodeGetwxacode(data).then((res) => { uni.hideLoading() if (res.code === 1000) { this.downImg(res.data, that.posterOptions.QrOption, 'QrUrl') that.$set(that.posterOptions.QrOption, 'isWeChatMiniApp', true) that.isGetOk = true } }) }, // 显示分享海报 showPoster() { this.posterShow = true }, /* 任务分配 */ assignTasks(data) { /* 设置头部的title */ uni.setNavigationBarTitle({ title: data.activityName, }) // 初始化分享朋友圈 // #ifndef H5 this.initWxShareData( this.detailObj.shareTitle || this.detailObj.shareContent, this.detailObj.shareImg || this.detailObj.verticalImgUrl ) // #endif // #ifdef H5 switch (uni.getSystemInfoSync().platform) { case 'android': this.fx(1) break case 'ios': this.fx(2) break } // #endif }, /* 初始化数据 */ initData() { let that = this getSpecialTopicData(this.activityUniqueCode).then((res) => { if (res.code == 1000) { const { plateDtoList, ...rest } = res.data let filterData = plateDtoList.map((it) => { const { productJson, ...re } = it let objArr = JSON.parse(productJson) return { productJson: objArr||[], ...re, } }) this.detailObj = { plateDtoList: filterData, ...rest } this.assignTasks(this.detailObj) // #ifdef MP uni.getSystemInfo({ success: (res) => { that.screenWidth = res.windowWidth }, }) this.getWxCode() // #endif // #ifdef H5 that.screenWidth = document.documentElement.clientWidth this.posterOptions.QrOption.QrUrl = window.location.href this.isGetOk = true // #endif // 判断是否设置了分享海报的图片 let isHaveImg = this.detailObj.posterImg ? true : false if(!isHaveImg){ this.downImg(that.posterOptions.logOption.logUrl, that.posterOptions.logOption, 'logUrl') this.downImg(that.posterOptions.buttonOption.bottomUrl, that.posterOptions.buttonOption, 'bottomUrl') } this.$set(this.posterOptions, 'isHavePosterImg', isHaveImg) this.$set( this.posterOptions.textOption, 'text', this.detailObj.activityName ) // 设置img图片和canvas大小 let posterImg = this.detailObj.posterImg ? this.detailObj.posterImg : this.detailObj.activityDetailHeadImg // 设置大图的url this.downImg(posterImg, this.posterOptions.poster, 'url') uni.getImageInfo({ src: posterImg, success(res) { let imgW = res.width //图片宽度 let imgH = res.height //图片高度 let per = imgW / that.screenWidth that.$set( that.posterOptions.poster, 'canvasWidth', that.screenWidth ) // 是否设置分享海报 let canvasHeight = isHaveImg ? imgH / per : imgH / per + that.posterOptions.buttonOption.h that.$set(that.posterOptions.poster, 'canvasHeight', canvasHeight) // 设置二维码地位 let codeX = isHaveImg ? that.screenWidth - 95 : 5 that.$set(that.posterOptions.QrOption, 'x', codeX) let codeY = isHaveImg ? imgH / per - 95 : canvasHeight - 95 that.$set(that.posterOptions.QrOption, 'y', codeY) if (!isHaveImg) { // 如果没有设置分享海报 that.$set( that.posterOptions.buttonOption, 'w', that.screenWidth ) that.$set(that.posterOptions.buttonOption, 'y', imgH / per) that.$set(that.posterOptions.textOption, 'x', codeX + 120) that.$set(that.posterOptions.textOption, 'y', codeY + 40) } }, }) } }) }, // 将网络图片缓存到本地 downImg(img, obj, name) { let that = this uni.downloadFile({ url: img, success: (res) => { that.$set(obj, name, res.tempFilePath) }, }) }, // 分享办法 fx(val) { let url = window.location.href // 获取usercode let urls let userInfo = this.$utils.getStorageSync('userInfo') let routes = getCurrentPages() // 获取以后关上过的页面路由数组 let curParam = routes[routes.length - 1].options || routes[routes.length - 1].$route.query //获取路由参数 if (userInfo) { curParam.userCode = userInfo.userCode } let a = this.$utils.pageliks(curParam, routes) let b = window.location.origin if (userInfo) { urls = `${b}${a}` } else { urls = url } let data = { url: url, } let shares = { title: this.detailObj.shareTitle || this.$config.serviceProvider, link: urls, desc:this.detailObj.shareContent||'', imgUrl: this.detailObj.shareImg || '', } if (val == 1) { getShopWxConfig(data, shares, val) } else { share(shares) } }, },}</script><style lang="scss" scoped>.width-100 { width: 100%;}.contentBgc { min-height: 100vh; width: 100vw; padding-bottom: 20rpx;}</style>