评论

canvas 快速生成心得 1

致力于帮大家解决生成 canvas 的难题。

  1. 前言
    由于接到需求需要使用 canvas 生成一些图片,然后每次改动的都不是特别多,但是真的很烦。网上也没有现成的demo,我决定自己new一个。(本期完成图片大小 位置 圆角的设置,字体大小 颜色 加粗 换行 省略 居中的功能)

  2. 代码

  • index.wxml 代码
<canvas class="canvas" canvas-id="shareCanvas"></canvas>
  • index.wxss
.canvas {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: calc(750px / 2 );
  height: calc(1050px / 2 );
  /* transform: translateY(-200%); */
  z-index: 1;
  background: #98c379;
}
  • index.js
const app = getApp()
const ctx = wx.createCanvasContext('shareCanvas');
const keyValue = {
  'image': 0,
  'text': 1
}
Page({
  data: {
    canvasList:[
      {
        type:keyValue.image,
        width:750,
        height:1050,
        x:0,
        y:0,
        url:'https://mg-shop-test.oss-cn-hangzhou.aliyuncs.com/public/xcx_images/index-share-list-001.png',
        borderRadius:0,
      },
      {
        type: keyValue.image,
        width: 124,
        height: 124,
        x: 313,
        y: 48,     
        url:'https://wx.qlogo.cn/mmopen/vi_32/UFBlX5BwfmflsuyEhGFSNCksLX33yicawcSx4rYRB25uTC7HkWgSiclKjWPkJor2BPPdXSG3FQuI0WDt5EAHQmsg/132',
        borderRadius: 62,
      },
      {
        type:keyValue.text,
        text:'Hello World 的简介',
        width:550,
        lineHeight:40,
        fontSize: 32,
        fontWeight: 'bold',
        color: '#FFFFFF',
        lineNumber: '1',
        x: 100,
        y: 200,
        textAlign: 'center'
      },
      {
        type:keyValue.text,
        text:'        Hello World 中文意思是『你好,世界』。因为《The C Programming Language》中使用它做为第一个演示程序,非常著名,所以后来的程序员在学习编程或进行设备调试时延续了这一习惯。',
        width:550,
        lineHeight:40,
        fontSize:28,
        fontWeight:'normal',
        color:'#87CEEB',
        lineNumber:'max',
        x:100,
        y:300,
        textAlign:'right'
      },{
        type: keyValue.text,
        text: '“Hello, world"程序是指在计算机屏幕上输出“Hello,world”这行字符串的计算机程序,“hello, world”的中文意思是“你好,世界。”。这个例程在Brian Kernighan 和Dennis M. Ritchie合著的《The C Programme Language》使用而广泛流行',
        width: 550,
        lineHeight: 36,
        fontSize: 26,
        fontWeight: 'normal',
        color: '#b3e98f',
        lineNumber: 3,
        x: 100,
        y: 550,
        textAlign: 'center'
      }
    ]
  },
  onLoad: function () {
    this.downFile();
  },
  // 下载所需所需文件
  downFile(index = 0){
    let item = this.data.canvasList[index];
    if(item.type == keyValue.image){ // 验证它的类型是否为 image
      wx.downloadFile({
        url:item.url,
        complete:res=>{
          if (res.errMsg == 'downloadFile:ok'){
            item.tempFilePath = res.tempFilePath;
            item.x = item.x / 2;
            item.y = item.y / 2;
            item.width = item.width / 2;
            item.height = item.height / 2;
            item.borderRadius = item.borderRadius / 2;
            if (++index < this.data.canvasList.length) { // 验证是否为canvasList的最后一个
              this.downFile(index);
            }
            else{
              this.drawCanvas();
            }
          }
          else{
            console.error(res.errMsg);
          }
        }
      })
    }
    else if (item.type == keyValue.text) { // 验证它的类型是否为 string
      item.x = item.x / 2;
      item.y = item.y / 2;
      item.width = item.width / 2;
      item.lineHeight = item.lineHeight / 2;
      item.fontSize = item.fontSize / 2;
      if (++index < this.data.canvasList.length) {
        this.downFile(index); // 验证是否为canvasList的最后一个
      }
      else {
        this.drawCanvas();
      }
    }
  },
  // 绘制图片
  drawCanvas(index = 0){
    let item = this.data.canvasList[index];
    if (item.type == keyValue.image){
      if(item.borderRadius == 0){ // 验证是否需要绘制圆角
        ctx.drawImage(item.tempFilePath, item.x, item.y, item.width,item.height);
      }
      else{
        // 怎么使用官方文档说的明明白白,我这不过多结束
        // https://developers.weixin.qq.com/miniprogram/dev/api/CanvasContext.clip.html
        ctx.beginPath()
        let borderRadius = Math.sqrt(Math.pow((item.height / 2), 2) + Math.pow((item.width / 2) - item.borderRadius, 2)); // 计算圆角的半径(原理下图1)
        ctx.arc(item.width / 2 + item.x, item.height / 2 + item.y, borderRadius , 0, 2 * Math.PI)
        ctx.clip()
        console.log(item.tempFilePath, item.x, item.y, item.width, item.height);
        ctx.drawImage(item.tempFilePath, item.x, item.y,item.width,item.height);
        ctx.restore()
      }
      ctx.save();
    }
    else if(item.type == keyValue.text){
      ctx.setFillStyle(item.color); // 设置字体颜色
      ctx.setFontSize(item.fontSize); // 设置字体大小
      let tempTextList = []; // 存储分割字符串使用
      let tempIndexNumber = 0; 
      for (let i = 1; i < item.text.length ; i++){
        if (ctx.measureText(item.text.substring(tempIndexNumber, i)).width > item.width){
          i--;
          tempTextList.push(item.text.substring(tempIndexNumber, i));
          tempIndexNumber = i;
        }
        else if(i == item.text.length - 1){
          tempTextList.push(item.text.substring(tempIndexNumber));
        }
      }
      
      let tempLength = item.lineNumber == 'max' ? tempTextList.length : item.lineNumber;
      for(let i = 0 ; i < tempLength ; i++){
        if(item.fontWeight == 'bold'){// 是否加粗字体
          if (i == tempLength - 1 && tempLength < tempTextList.length){ // 验证是否超出了所需的行数限制
            ctx.fillText(tempTextList[i].substring(0, tempTextList[i].length - 2) + '...', item.x, item.lineHeight * i + item.y + item.fontSize + 0.5);
            ctx.fillText(tempTextList[i].substring(0, tempTextList[i].length - 2) + '...', item.x + 0.5, item.lineHeight * i + item.y + item.fontSize);
          }
          else if (tempLength == tempTextList.length){
            if(item.textAlign == 'center'){// 文字居中
              let tempWidth = (item.width - ctx.measureText(tempTextList[i]).width) / 2;
              ctx.fillText(tempTextList[i], tempWidth +  item.x, item.lineHeight * i + item.y + item.fontSize + 0.5);
              ctx.fillText(tempTextList[i], tempWidth + item.x + 0.5, item.lineHeight * i + item.y + item.fontSize);
            }
            else if(item.textAlign == 'right'){// 文字居右
              let tempWidth = item.width - ctx.measureText(tempTextList[i]).width;
              ctx.fillText(tempTextList[i], tempWidth + item.x, item.lineHeight * i + item.y + item.fontSize + 0.5);
              ctx.fillText(tempTextList[i], tempWidth + item.x + 0.5, item.lineHeight * i + item.y + item.fontSize);
            }
            else { // 文字默认居左 不作处理
              ctx.fillText(tempTextList[i], item.x, item.lineHeight * i + item.y + item.fontSize + 0.5);
              ctx.fillText(tempTextList[i], item.x + 0.5, item.lineHeight * i + item.y + item.fontSize);
            }
          }
          else{
            ctx.fillText(tempTextList[i], item.x, item.lineHeight * i + item.y + item.fontSize + 0.5);
            ctx.fillText(tempTextList[i], item.x + 0.5, item.lineHeight * i + item.y + item.fontSize);
          }
        }
        else{
          if (i == tempLength - 1 && tempLength < tempTextList.length){
            ctx.fillText(tempTextList[i].substring(0, tempTextList[i].length - 2) + '...', item.x, item.lineHeight * i + item.y + item.fontSize);
          }
          else if (tempLength == tempTextList.length){
            if (item.textAlign == 'center') {// 文字居中
              let tempWidth = (item.width - ctx.measureText(tempTextList[i]).width) / 2;
              ctx.fillText(tempTextList[i], tempWidth + item.x, item.lineHeight * i + item.y + item.fontSize);
            }
            else if (item.textAlign == 'right') {// 文字居右
              let tempWidth = item.width - ctx.measureText(tempTextList[i]).width;
              ctx.fillText(tempTextList[i], tempWidth + item.x, item.lineHeight * i + item.y + item.fontSize);
            }
            else { // 文字默认居左 不作处理
              ctx.fillText(tempTextList[i], item.x, item.lineHeight * i + item.y + item.fontSize);
            }
            
          }
          else { // 文字默认居左 不作处理
            ctx.fillText(tempTextList[i], item.x, item.lineHeight * i + item.y + item.fontSize);
          }
        }
      }
    }
    if (++index < this.data.canvasList.length) {// 验证是否绘制完成
      this.drawCanvas(index);
    }
    else {
      // 渲染图片
      ctx.draw(true,()=>{
        this.savePhoto()
      });
    }
  },
  // 保存图片到本地
  savePhoto(){
    wx.canvasToTempFilePath({
      destWidth: 750 / 2,
      destHeight: 1050 / 2,
      quality: 1,
      fileType: 'jpg',
      canvasId: 'shareCanvas',
      complete: res => {
        console.log(res);
        if (res.errMsg == 'canvasToTempFilePath:ok'){
          wx.saveImageToPhotosAlbum({
            filePath: res.tempFilePath,
            complete: res => {
              if (res.errMsg == 'saveImageToPhotosAlbum:ok') {
                wx.showToast('保存成功');
              } else {
                wx.showToast('用户取消了保存');
              }
            }
          })
        }
      }
    })
  }
})



效果图

小程序代码片段分享

  1. 结尾
    头一次编写分享文章,如有编写错误或者写不好的地方希望大家多担待,如有不明白的地方请留言,我将对其解答。后期将会再为 canvas 迭代几个版本尽量能将所有样式能再canvas 中实现出来。
    By:Axs
点赞 10
收藏
评论

2 个评论

登录 后发表内容