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

正文

小程序分享海报想必大家都做过,受微信的限制,无法直接分享小程序到朋友圈(虽然微信开发者工具基础库从2.11.3开始支持分享小程序到朋友圈,但目前仍处于Beta中),所以生成海报仍然还是主流方式,通常是将设计稿通过canvas绘制成图片,然后保存到用户相册,用户通过图片分享小程序

但是,如果不是对canvas很熟悉的话,每次都要去学习canvas的Api,挺麻烦的。我只想“无脑”的完成海报的绘制,就像是把每一个元素固定定位一样,告诉它你要插入的是图片、还是文字,然后再传入坐标、宽高就能把在canvas绘制出内容。

怎么做呢?接着往下看(注:本文是基于uniapp Vue3搭建的小程序实现的海报功能)

uniapp封装canvas组件无脑绘制保存小程序分享海报_第1张图片

配置项

属性 说明 可选值
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

一、使用




二、封装m-canvas组件




三、声明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的资料请关注脚本之家其它相关文章!

你可能感兴趣的:(uniapp封装canvas组件无脑绘制保存小程序分享海报)