uniapp使用canvas实现二维码分享

·  阅读 1528

实现使用canvas在小程序H5页面进行二维码分享 如下图效果 可以保存并扫码

1639378852744.jpg

总体思路:使用canvas进行绘制,为了节省时间固定部分采用背景图绘制 只有二维码以及展示图片及标题绘制,绘制完成后调用uni.canvasToTempFilePath将其转为图片展示

1.组件调用,使用ref调用组件内部相应的canvas绘制方法,传入相关参数 包括名称 路由 展示图片等。

 <SharePoster v-if='showposter' ref='poster' @close='close'/>

<script>
  import SharePoster from "@/components/share/shareposter.vue"
  export default {
    components: {
       SharePoster,
    },
   methods:{
       handleShare(item){
          this.showposter=true
          if(this.showvote){
            this.showvote=false
          }
          this.$nextTick(() => {
         this.$refs.poster.drawposter(item.name, `/pagesMore/voluntary/video/player?schoolId=${item.id}`,item.cover)
          })
        },
   }
 </script>
复制代码

2.组件模板放置canvas容器并赋予id以及宽度高度等,使用iscomplete控制是显示canvas还是显示最后调用uni.canvasToTempFilePath生成的图片

<div class="poster-wrapper" @click="closePoster($event)">
      <div class='poster-content'>
          <canvas canvas-id="qrcode"
            v-if="qrShow"
            :style="{opacity: 0, position: 'absolute', top: '-1000px'}"
          ></canvas>
          <canvas
            canvas-id="poster"
            :style="{ width: cansWidth + 'px', height: cansHeight + 'px' ,opacity: 0, }"
            v-if='!iscomplete'
          ></canvas>
          <image
            v-if="iscomplete"
            :style="{ width: cansWidth + 'px', height: cansHeight + 'px' }"
            :src="tempFilePath"
            @longpress="longpress"
          ></image>
      </div>
  </div>
复制代码

3.data内放置相应配置参数

 data() {
      return {
          bgImg:'https://cdn.img.up678.com/ueditor/upload/image/20211130/1638258070231028289.png', //画布背景图片
          cansWidth:288, // 画布宽度
          cansHeight:410, // 画布高度
          projectImgWidth:223, // 中间展示图片宽度
          projectImgHeight:167, // 中间展示图片高度
          qrShow:true, // 二维码canvas
          qrData: null, // 二维码数据
          tempFilePath:'',// 生成图路径
          iscomplete:false, // 是否生成图片
      }
    },
复制代码

4.在created生命周期内调用uni.createCanvasContext创建canvas实例 传入模板内canvas容器id

created(){
      this.ctx = uni.createCanvasContext('poster',this)
    },
复制代码

5.调用对应方法,绘制分享作品

   // 绘制分享作品
      async drawposter(name='重庆最美高校景象',url,projectImg){
           uni.showLoading({
             title: "加载中...",
             mask: true
           })
           // 生成二维码
          await this.createQrcode(url)
          // 背景
          await this.drawWebImg({
            url: this.bgImg,
            x: 0, y: 0, width: this.cansWidth, height: this.cansHeight
          })
          // 展示图
          await this.drawWebImg({
            url: projectImg,
            x: 33, y: 90, width: this.projectImgWidth, height: this.projectImgHeight
          })
          await this.drawText({
            text: name,
            x: 15, y: 285, color: '#241D4A', size: 15, bold: true, center: true,
            shadowObj: {x: '0', y: '4', z: '4', color: 'rgba(173,77,0,0.22)'}
          })
          // 绘制二维码
          await this.drawQrcode()
          //转为图片
          this.tempFilePath = await this.saveCans()
          this.iscomplete = true
          uni.hideLoading()
      },
复制代码

6.绘制图片方法,注意 this.ctx.drawImage方法第一个参数不能放网络图片 必须执行下载后绘制

  drawWebImg(conf) {
        return new Promise((resolve, reject) => {
          uni.downloadFile({
            url: conf.url,
            success: (res) => {
              this.ctx.drawImage(res.tempFilePath, conf.x, conf.y, conf.width?conf.width:"", conf.height?conf.height:"")
              this.ctx.draw(true, () => {
                resolve()
              })
            },
            fail: err => {
              reject(err)
            }
          })
        })
      },
复制代码

7.绘制文本标题

 drawText(conf) {
        return new Promise((resolve, reject) => {
          this.ctx.restore()
          this.ctx.setFillStyle(conf.color)
          if(conf.bold) this.ctx.font = `normal bold ${conf.size}px sans-serif`
          this.ctx.setFontSize(conf.size)
          if(conf.shadowObj) {
            // this.ctx.shadowOffsetX = conf.shadowObj.x
            // this.ctx.shadowOffsetY = conf.shadowObj.y
            // this.ctx.shadowOffsetZ = conf.shadowObj.z
            // this.ctx.shadowColor = conf.shadowObj.color
          }
          let x = conf.x
          conf.text=this.fittingString(this.ctx,conf.text,280)
          if(conf.center) {
            let len = this.ctx.measureText(conf.text)
            x = this.cansWidth / 2 - len.width / 2 + 2
          }

          this.ctx.fillText(conf.text, x, conf.y)
          this.ctx.draw(true, () => {
            this.ctx.save()
            resolve()
          })
        })
      },
// 文本标题溢出隐藏处理
fittingString(_ctx, str, maxWidth) {
            let strWidth = _ctx.measureText(str).width;
            const ellipsis = '…';
            const ellipsisWidth = _ctx.measureText(ellipsis).width;
            if (strWidth <= maxWidth || maxWidth <= ellipsisWidth) {
              return str;
            } else {
              var len = str.length;
              while (strWidth >= maxWidth - ellipsisWidth && len-- > 0) {
                str = str.slice(0, len);
                strWidth = _ctx.measureText(str).width;
              }
              return str + ellipsis;
            }
          },
复制代码

8.生成二维码

      createQrcode(qrcodeUrl) {
        // console.log(window.location.origin)
        const config={host:window.location.origin}
        return new Promise((resolve, reject) => {
          let url = `${config.host}${qrcodeUrl}`
          // if(url.indexOf('?') === -1) url = url + '?sh=1'
          // else url = url + '&sh=1'
          try{
            new qrCode({
              canvasId: 'qrcode',
              usingComponents: true,
              context: this,
              // correctLevel: 3,
              text: url,
              size: 130,
              cbResult: (res) => {
                this.qrShow = false
                this.qrData = res
                resolve()
              }
            })
          } catch (err) {
            reject(err)
          }
        })
      },
复制代码

9.画二维码,this.qrData为生成的二维码资源

  drawQrcode(conf = { x: 185, y: 335, width: 100, height: 50}) {
        return new Promise((resolve, reject) => {
          this.ctx.drawImage(this.qrData, conf.x, conf.y, conf.width, conf.height)
          this.ctx.draw(true, () => {
            resolve()
          })
        })
      },
复制代码

10.将canvas绘制内容转为图片并显示,在H5平台下,tempFilePath 为 base64

// canvs => images
      saveCans() {
        return new Promise((resolve, reject) => {
          uni.canvasToTempFilePath({
            x:0,
            y:0,
            canvasId: 'poster',
            success: (res) => {
              resolve(res.tempFilePath)
            },
            fail: (err) => {
              uni.hideLoading()
              reject(err)
            }
          }, this)
        })
          },
复制代码

11.组件全部代码

<template>
  <div class="poster-wrapper" @click="closePoster($event)">
      <div class='poster-content'>
          <canvas canvas-id="qrcode"
            v-if="qrShow"
            :style="{opacity: 0, position: 'absolute', top: '-1000px'}"
          ></canvas>
          <canvas
            canvas-id="poster"
            :style="{ width: cansWidth + 'px', height: cansHeight + 'px' ,opacity: 0, }"
            v-if='!iscomplete'
          ></canvas>
          <image
            v-if="iscomplete"
            :style="{ width: cansWidth + 'px', height: cansHeight + 'px' }"
            :src="tempFilePath"
            @longpress="longpress"
          ></image>
      </div>
  </div>
</template>

<script>
  import qrCode from '@/utils/wxqrcode.js'
  export default {
    data() {
      return {
          bgImg:'https://cdn.img.up678.com/ueditor/upload/image/20211130/1638258070231028289.png', //画布背景图片
          cansWidth:288, // 画布宽度
          cansHeight:410, // 画布高度
          projectImgWidth:223, // 中间展示图片宽度
          projectImgHeight:167, // 中间展示图片高度
          schoolImgWidth:110, // 中间展示高校宽度
          schoolImgHeight:110, // 中间展示高校高度
          qrShow:true, // 二维码canvas
          qrData: null, // 二维码数据
          tempFilePath:'',// 生成图路径
          iscomplete:false, // 是否生成图片
      }
    },
    created(){
      this.ctx = uni.createCanvasContext('poster',this)
    },
    methods:{
      closePoster(e) {
        if(e.target.id === e.currentTarget.id) {
          // 关闭
          this.$emit('close')
        }
      },
      // 绘制分享作品
      async drawposter(name='重庆最美高校景象',url,projectImg){
           uni.showLoading({
             title: "加载中...",
             mask: true
           })
          await this.createQrcode(url)
          // 背景
          await this.drawWebImg({
            url: this.bgImg,
            x: 0, y: 0, width: this.cansWidth, height: this.cansHeight
          })
          // 展示图
          await this.drawWebImg({
            url: projectImg,
            x: 33, y: 90, width: this.projectImgWidth, height: this.projectImgHeight
          })
          await this.drawText({
            text: name,
            x: 15, y: 285, color: '#241D4A', size: 15, bold: true, center: true,
            shadowObj: {x: '0', y: '4', z: '4', color: 'rgba(173,77,0,0.22)'}
          })
          // 二维码
          await this.drawQrcode()
          this.tempFilePath = await this.saveCans()
          this.iscomplete = true
          uni.hideLoading()
      },
      // 绘制分享学校
      async drawposterschool(name,url,schoolImg){
           uni.showLoading({
             title: "加载中...",
             mask: true
           })
          await this.createQrcode(url)
          // 背景
          await this.drawWebImg({
            url: this.bgImg,
            x: 0, y: 0, width: this.cansWidth, height: this.cansHeight
          })
          // 展示图
          await this.drawSchoolImg({
            url: schoolImg,
            x: 90, y: 120, width: this.schoolImgWidth, height: this.schoolImgHeight
          })
          await this.drawText({
            text: name,
            x: 15, y: 285, color: '#241D4A', size: 15, bold: true, center: true,
            shadowObj: {x: '0', y: '4', z: '4', color: 'rgba(173,77,0,0.22)'}
          })
          // 二维码
          await this.drawQrcode()
          this.tempFilePath = await this.saveCans()
          this.iscomplete = true
          uni.hideLoading()
      },
      drawWebImg(conf) {
        return new Promise((resolve, reject) => {
          uni.downloadFile({
            url: conf.url,
            success: (res) => {
              this.ctx.drawImage(res.tempFilePath, conf.x, conf.y, conf.width?conf.width:"", conf.height?conf.height:"")
              this.ctx.draw(true, () => {
                resolve()
              })
            },
            fail: err => {
              reject(err)
            }
          })
        })
      },
      drawSchoolImg(conf) {
        return new Promise((resolve, reject) => {
          uni.downloadFile({
            url: conf.url,
            success: (res) => {
              this.ctx.save()
              this.ctx.beginPath()
              this.ctx.arc(135, 170, 70, 0, 2 * Math.PI)
              // this.ctx.setFillStyle('blue')
              // this.ctx.fill()
              this.ctx.clip();
               this.ctx.drawImage(res.tempFilePath, conf.x, conf.y, conf.width, conf.height)
               this.ctx.restore()
              this.ctx.draw(true, () => {
                resolve()
              })
            },
            fail: err => {
              reject(err)
            }
          })
        })
      },
      drawText(conf) {
        return new Promise((resolve, reject) => {
          this.ctx.restore()
          this.ctx.setFillStyle(conf.color)
          if(conf.bold) this.ctx.font = `normal bold ${conf.size}px sans-serif`
          this.ctx.setFontSize(conf.size)
          if(conf.shadowObj) {
            // this.ctx.shadowOffsetX = conf.shadowObj.x
            // this.ctx.shadowOffsetY = conf.shadowObj.y
            // this.ctx.shadowOffsetZ = conf.shadowObj.z
            // this.ctx.shadowColor = conf.shadowObj.color
          }
          let x = conf.x
          conf.text=this.fittingString(this.ctx,conf.text,280)
          if(conf.center) {
            let len = this.ctx.measureText(conf.text)
            x = this.cansWidth / 2 - len.width / 2 + 2
          }

          this.ctx.fillText(conf.text, x, conf.y)
          this.ctx.draw(true, () => {
            this.ctx.save()
            resolve()
          })
        })
      },
       fittingString(_ctx, str, maxWidth) {
            let strWidth = _ctx.measureText(str).width;
            const ellipsis = '…';
            const ellipsisWidth = _ctx.measureText(ellipsis).width;
            if (strWidth <= maxWidth || maxWidth <= ellipsisWidth) {
              return str;
            } else {
              var len = str.length;
              while (strWidth >= maxWidth - ellipsisWidth && len-- > 0) {
                str = str.slice(0, len);
                strWidth = _ctx.measureText(str).width;
              }
              return str + ellipsis;
            }
          },
      // 画二维码
      drawQrcode(conf = { x: 185, y: 335, width: 100, height: 50}) {
        return new Promise((resolve, reject) => {
          this.ctx.drawImage(this.qrData, conf.x, conf.y, conf.width, conf.height)
          this.ctx.draw(true, () => {
            resolve()
          })
        })
      },
      // 生成二维码
      createQrcode(qrcodeUrl) {
        // console.log(window.location.origin)
        const config={host:window.location.origin}
        return new Promise((resolve, reject) => {
          let url = `${config.host}${qrcodeUrl}`
          // if(url.indexOf('?') === -1) url = url + '?sh=1'
          // else url = url + '&sh=1'
          try{
            new qrCode({
              canvasId: 'qrcode',
              usingComponents: true,
              context: this,
              // correctLevel: 3,
              text: url,
              size: 130,
              cbResult: (res) => {
                this.qrShow = false
                this.qrData = res
                resolve()
              }
            })
          } catch (err) {
            reject(err)
          }
        })
      },
      // canvs => images
      saveCans() {
        return new Promise((resolve, reject) => {
          uni.canvasToTempFilePath({
            x:0,
            y:0,
            canvasId: 'poster',
            success: (res) => {
              resolve(res.tempFilePath)
            },
            fail: (err) => {
              uni.hideLoading()
              reject(err)
            }
          }, this)
        })
      },
    }
  }
</script>

<style lang="stylus" scoped>
  .poster-wrapper{
    width 100vw
    height 100vh
    position fixed
    background rgba(0, 0, 0, .4)
    left 0
    top 0
    text-align center
    z-index 99999
    .poster-content{
      position absolute
      top 50%
      left 50%
      transform translate(-50%, -50%)
      }
  }

</style>

复制代码
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改