uniapp如何封装canvas组件无脑绘制保存小程序分享海报

2023-06-07,

这篇“uniapp如何封装canvas组件无脑绘制保存小程序分享海报”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“uniapp如何封装canvas组件无脑绘制保存小程序分享海报”文章吧。

配置项

属性 说明 可选值
type 元素类型 image、text、border、block(一般用于设置背景色块)
left 元素距离canvas左侧的距离 数字或者center,center表示水平居中,比如10、'center'
right 元素距离canvas右侧的距离 数字,比如10
top 元素距离canvas顶部的距离 数字,比如10
bottom 元素距离canvas底部的距离 数字,比如10
width 元素宽度 数字,比如20
height 元素高度 数字,比如20
url type为image时的图片地址 字符串
color type为text、border、block时的颜色 字符串,比如#333333
content type为text时的文本内容 字符串
fontSize type为text时的字体大小 数字,比如16
radius type为image、block时圆角,200表示圆形 数字,比如10
maxLine type为text时限制最大行数,超出以…结尾 数字,比如2
lineHeight type为text时的行高,倍数 数字,比如1.5,默认1.3

一、使用

<template>
  <m-canvas ref="myCanvasRef" :width="470" :height="690" />
  <button @click="createPoster">生成海报</button>
</template>
<script setup>
  import { ref } from 'vue'
  const myCanvasRef = ref()
  function createPoster() {
    // 配置项
    const options = [
      // 背景图
      {
        type: 'image',
        url: '自行替换',
        left: 0,
        top: 0,
        width: 470,
        height: 690
      },
      // 长按扫码 > 浏览臻品 > 获取权益
      {
        type: 'text',
        content: '长按扫码 > 浏览臻品 > 获取权益',
        color: '#333',
        fontSize: 20,
        left: 'center',
        top: 240
      },
      // 小程序码白色背景
      {
        type: 'block',
        color: '#fff',
        radius: 30,
        left: 'center',
        top: 275,
        width: 245,
        height: 245
      },
      // 小程序码
      {
        type: 'image',
        url: '自行替换',
        left: 'center',
        top: 310,
        width: 180,
        height: 180
      },
      // 头像
      {
        type: 'image',
        url: '自行替换',
        radius: '50%',
        left: 'center',
        top: 545,
        width: 50,
        height: 50
      },
      // 昵称
      {
        type: 'text',
        content: 'Jerry',
        color: '#333',
        fontSize: 20,
        left: 'center',
        top: 625
      }
    ]
    // 调用myCanvas的onDraw方法,绘制并保存
    myCanvasRef.value.onDraw(options, url => {
      console.log(url)
    })
  }
</script>
<style lang="scss" scoped></style>

二、封装m-canvas组件

<template>
  <canvas class="myCanvas" canvas-id="myCanvas" />
</template>
<script setup>
  import { getCurrentInstance } from 'vue'
  // 引入canvas方法
  import { createPoster } from './canvas'
  const { proxy } = getCurrentInstance()
  // 宽高需要传哦~
  const props = defineProps({
    width: {
      type: Number,
      required: true
    },
    height: {
      type: Number,
      required: true
    }
  })
  // 导出方法给父组件用
  defineExpose({
    onDraw(options, callback) {
      createPoster.call(
        // 当前上下文
        proxy,
        // canvas相关信息
        {
          id: 'myCanvas',
          width: props.width,
          height: props.height
        },
        // 元素集合
        options,
        // 回调函数
        callback
      )
    }
  })
</script>
<style lang="scss" scoped>
  // 隐藏canvas
  .myCanvas {
    left: -9999px;
    bottom: -9999px;
    position: fixed;
    // canvas宽度
    width: calc(1px * v-bind(width));
    // canvas高度
    height: calc(1px * v-bind(height));
  }
</style>

三、声明canvas.js,封装方法

/** @生成海报 **/
export function createPoster(canvasInfo, options, callback) {
  uni.showLoading({
    title: '海报生成中…',
    mask: true
  })
  const myCanvas = uni.createCanvasContext(canvasInfo.id, this)
  var index = 0
  drawCanvas(myCanvas, canvasInfo, options, index, () => {
    myCanvas.draw(true, () => {
      // 延迟,等canvas画完
      const timer = setTimeout(() => {
        savePoster.call(this, canvasInfo.id, callback)
        clearTimeout(timer)
      }, 1000)
    })
  })
}
// 绘制中
async function drawCanvas(myCanvas, canvasInfo, options, index, drawComplete) {
  let item = options[index]
  // 最大行数:maxLine  字体大小:fontSize  行高:lineHeight
  //    类型    颜色  left  right  top  bottom   宽      高     圆角    图片  文本内容
  let { type, color, left, right, top, bottom, width, height, radius, url, content, fontSize } = item
  radius = radius || 0
  const { width: canvasWidth, height: canvasHeight } = canvasInfo
  switch (type) {
    /** @文本 **/
    case 'text':
      if (!content) break
      // 根据字体大小计算出宽度
      myCanvas.setFontSize(fontSize)
      // 内容宽度:传了宽度就去宽度,否则取字体本身宽度
      item.width = width || myCanvas.measureText(content).width
      console.log(myCanvas.measureText(content))
      // left位置
      if (right !== undefined) {
        item.left = canvasWidth - right - item.width
      } else if (left === 'center') {
        item.left = canvasWidth / 2 - item.width / 2
      }
      // top位置
      if (bottom !== undefined) {
        item.top = canvasHeight - bottom - fontSize
      }
      drawText(myCanvas, item)
      break
    /** @图片 **/
    case 'image':
      if (!url) break
      var imageTempPath = await getImageTempPath(url)
      // left位置
      if (right !== undefined) {
        left = canvasWidth - right - width
      } else if (left === 'center') {
        left = canvasWidth / 2 - width / 2
      }
      // top位置
      if (bottom !== undefined) {
        top = canvasHeight - bottom - height
      }
      // 带圆角
      if (radius) {
        myCanvas.save()
        myCanvas.beginPath()
        // 圆形图片
        if (radius === '50%') {
          myCanvas.arc(left + width / 2, top + height / 2, width / 2, 0, Math.PI * 2, false)
        } else {
          if (width < 2 * radius) radius = width / 2
          if (height < 2 * radius) radius = height / 2
          myCanvas.beginPath()
          myCanvas.moveTo(left + radius, top)
          myCanvas.arcTo(left + width, top, left + width, top + height, radius)
          myCanvas.arcTo(left + width, top + height, left, top + height, radius)
          myCanvas.arcTo(left, top + height, left, top, radius)
          myCanvas.arcTo(left, top, left + width, top, radius)
          myCanvas.closePath()
        }
        myCanvas.clip()
      }
      myCanvas.drawImage(imageTempPath, left, top, width, height)
      myCanvas.restore()
      break
    /** @盒子 **/
    case 'block':
      // left位置
      if (right !== undefined) {
        left = canvasWidth - right - width
      } else if (left === 'center') {
        left = canvasWidth / 2 - width / 2
      }
      // top位置
      if (bottom !== undefined) {
        top = canvasHeight - bottom - height
      }
      if (width < 2 * radius) {
        radius = width / 2
      }
      if (height < 2 * radius) {
        radius = height / 2
      }
      myCanvas.beginPath()
      myCanvas.fillStyle = color
      myCanvas.strokeStyle = color
      myCanvas.moveTo(left + radius, top)
      myCanvas.arcTo(left + width, top, left + width, top + height, radius)
      myCanvas.arcTo(left + width, top + height, left, top + height, radius)
      myCanvas.arcTo(left, top + height, left, top, radius)
      myCanvas.arcTo(left, top, left + width, top, radius)
      myCanvas.stroke()
      myCanvas.fill()
      myCanvas.closePath()
      break
    /** @边框 **/
    case 'border':
      // left位置
      if (right !== undefined) {
        left = canvasWidth - right - width
      }
      // top位置
      if (bottom !== undefined) {
        top = canvasHeight - bottom - height
      }
      myCanvas.beginPath()
      myCanvas.moveTo(left, top)
      myCanvas.lineTo(left + width, top + height)
      myCanvas.strokeStyle = color
      myCanvas.lineWidth = width
      myCanvas.stroke()
      break
  }
  // 递归边解析图片边画
  if (index === options.length - 1) {
    drawComplete()
  } else {
    index++
    drawCanvas(myCanvas, canvasInfo, options, index, drawComplete)
  }
}
// 下载并保存
function savePoster(canvasId, callback) {
  uni.showLoading({
    title: '保存中…',
    mask: true
  })
  uni.canvasToTempFilePath(
    {
      canvasId,
      success(res) {
        callback && callback(res.tempFilePath)
        uni.saveImageToPhotosAlbum({
          filePath: res.tempFilePath,
          success() {
            uni.showToast({
              icon: 'success',
              title: '保存成功!'
            })
          },
          fail() {
            uni.showToast({
              icon: 'none',
              title: '保存失败,请稍后再试~'
            })
          },
          complete() {
            uni.hideLoading()
          }
        })
      },
      fail(res) {
        console.log('图片保存失败:', res.errMsg)
        uni.showToast({
          icon: 'none',
          title: '保存失败,请稍后再试~'
        })
      }
    },
    this
  )
}
// 绘制文字(带换行超出省略…功能)
function drawText(ctx, item) {
  let { content, width, maxLine, left, top, lineHeight, color, fontSize } = item
  content = String(content)
  lineHeight = (lineHeight || 1.3) * fontSize
  // 字体
  ctx.setFontSize(fontSize)
  // 颜色
  ctx.setFillStyle(color)
  // 文本处理
  let strArr = content.split('')
  let row = []
  let temp = ''
  for (let i = 0; i < strArr.length; i++) {
    if (ctx.measureText(temp).width < width) {
      temp += strArr[i]
    } else {
      i-- //这里添加了i-- 是为了防止字符丢失,效果图中有对比
      row.push(temp)
      temp = ''
    }
  }
  row.push(temp) // row有多少项则就有多少行
  //如果数组长度大于2,现在只需要显示两行则只截取前两项,把第二行结尾设置成'...'
  if (row.length > maxLine) {
    let rowCut = row.slice(0, maxLine)
    let rowPart = rowCut[1]
    let text = ''
    let empty = []
    for (let i = 0; i < rowPart.length; i++) {
      if (ctx.measureText(text).width < width) {
        text += rowPart[i]
      } else {
        break
      }
    }
    empty.push(text)
    let group = empty[0] + '...' //这里只显示两行,超出的用...表示
    rowCut.splice(1, 1, group)
    row = rowCut
  }
  // 把文本绘制到画布中
  for (let i = 0; i < row.length; i++) {
    // 一次渲染一行
    ctx.fillText(row[i], left, top + i * lineHeight, width)
  }
}
// 获取图片信息
function getImageTempPath(url) {
  return new Promise((resolve) => {
    if (url.includes('http')) {
      uni.downloadFile({
        url,
        success: (res) => {
          uni.getImageInfo({
            src: res.tempFilePath,
            success: (res) => {
              resolve(res.path)
            }
          })
        },
        fail: (res) => {
          console.log('图片下载失败:', res.errMsg)
        }
      })
    } else {
      resolve(url)
    }
  })
}

以上就是关于“uniapp如何封装canvas组件无脑绘制保存小程序分享海报”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注本站行业资讯频道。

《uniapp如何封装canvas组件无脑绘制保存小程序分享海报.doc》

下载本文的Word格式文档,以方便收藏与打印。