微信小程序canvas绘制表格

效果图
在这里插入图片描述

在这里插入图片描述

html

<button catchtap="draw">打印</button
<button bindtap="saveImg">保存本地</button>
<image src="{{imgUrl}}" style="height: 500rpx;width: 100%;" mode="aspectFit"></image>
<canvas style="width: 100%; height:{{rectH}}px;" type="2d" id="myCanvas"></canvas>

js

// index/index.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    rectH: 500, //canvas默认高度}
    imgUrl: '',
    list: {
      consignee: '阿三大阿三大阿三大阿三大阿三大',
      dataVos: [{
          name: '哒哒人脸',
          type: '人脸设备',
          num: 1,
          unit: '台'
        },
      ]
    },
    canvas: null
  },
  rpx2px(arg) {
    const info = wx.getSystemInfoSync()
    const width = info.screenWidth
    return arg * width / 750
  },
  // 获取图片对象
  async getImage(url) {
    const off = wx.createOffscreenCanvas({
      type: '2d'
    })
    const image = off.createImage()
    await new Promise((resolve, reject) => {
      image.onload = resolve // 绘制图片逻辑
      image.src = url
    })
    return image
  },
  saveImg() {
    wx.saveImageToPhotosAlbum({
      filePath: this.data.imgUrl,
      success: function (res) {
        wx.showToast({
          title: '保存成功',
          duration: 2000
        })
      },
      fail: function (err) {
        wx.showToast({
          title: '保存失败',
          icon: 'error',
          duration: 2000
        })
      }
    })
  },
  draw() {
    const $ = wx.createSelectorQuery()
    let that = this
    wx.showLoading({
      title: '正在生成图片',
    })
    $.select('#myCanvas')
      .fields({
        node: true,
        size: true
      })
      .exec((res) => {
        // Canvas 对象
        const canvas = res[0].node
        // Canvas 画布的实际绘制宽高
        const windowWidth = wx.getSystemInfoSync().windowWidth; // 屏幕的宽度
        const db = that.data.list; //定义后台数据
        let row = parseFloat(db.dataVos.length + 8) // 总行数
        let rectW = windowWidth - 30; // 表格宽度
        let rectH = 0; // 表格高度,用来记录当前已绘制的表格高度,初始默认高度
        let gridH = 20 //  单元格宽度,初始值100,即行高
        let gridW = parseInt(rectW / 5); // 单元格宽度,初始值100

        // 创建canvas渲染上下文
        const ctx = canvas.getContext('2d')
        const dpr = wx.getSystemInfoSync().pixelRatio
        console.log('---dpr', dpr)
        // 手动改变canvas的宽和高,先给一个
        canvas.width = rectW * dpr
        canvas.height = gridH * row * dpr
        ctx.scale(dpr, dpr)
        // 以上代码都是基础工作,给canvas写css样式时可以使用rpx单位。

        //绘制线: ctx;startX,stratY:起点坐标;endX,endY:末端坐标;strokeStyle:颜色(默认#000);lineWidth:宽度(默认rpx)
        function line(canvasSum, startX, stratY, endX, endY, strokeStyle = '#000', lineWidth = '2rpx') {
          try {
            canvasSum.beginPath()
            canvasSum.moveTo(startX, stratY);
            canvasSum.lineTo(endX, endY);
            canvasSum.strokeStyle = strokeStyle
            canvasSum.lineWidth = lineWidth
            canvasSum.stroke()
          } catch (err) {
            console.log('绘制线失败:' + err);
          }
        }

        // 填充文字,自动换行,并返回文本高度
        // {string} str         在画布上输出的文本
        // {number} x           绘制文本的左上角 x 坐标位置
        // {number} y           绘制文本的左上角 y 坐标位置
        // {number} maxWidth    最大宽度
        // {number} fontSize    字体大小(默认16)
        // return {number}      文本换行后的总高度
        function text(str, x, y, maxWidth, fontSize = 16) {
          try {
            str = String(str)
            x += 5 // 文字不贴边
            var lineWidth = 0; // 文本宽度
            var lastSubStrIndex = 0; //每次开始截取的字符串的索引
            var titleHeight = y + fontSize;
            rectH = (rectH < y) ? y : rectH
            // y += fontSize; // 文本高度
            for (let i = 0; i < str.length; i++) {
              lineWidth += ctx.measureText(str[i]).width; // 测量文本尺寸信息
              if (lineWidth > (maxWidth - 11)) { // 判断文本宽度是否超出最大宽度 左右不贴边各5,线宽1px 5+5+1
                ctx.fillText(str.substring(lastSubStrIndex, i), x, y); //绘制截取部分
                y += fontSize; //16为字体的高度
                lineWidth = 0; // 重置文本宽度
                lastSubStrIndex = i;
                titleHeight += fontSize;
              }
              if (i == str.length - 1) { //绘制剩余部分
                ctx.fillText(str.substring(lastSubStrIndex, i + 1), x, y);
              }
            }
            rectH = (rectH < y) ? y : rectH
            // console.log( titleHeight > y ? 'titleHeight' : 'y');
          } catch (error) {
            console.log('填充文字失败:' + error);
          }
        }
        // 绘制辅料、备注之前的行列
        function drawTable() {
          for (let i = 0; i <= (8 + db.dataVos.length); i++) {
            //填充文字  竖线
            if (i == 1) {
              text('项目名称', 0, titleH, gridW);
              text(db.consignee, gridW, titleH, rectW - gridW); // 这里的 rectW-gridW-10 是指 表格的宽度 - 上一个单元格宽度 - 左右预留5(5*2)空间免得贴边
            } else if (i == 2) {
              text('材料单次', 0, titleH, gridW);
              text(db.consignee, gridW, titleH, rectW - gridW);
            } else if (i === 3) {
              text('详细清单如下', 0, titleH, gridW);
            } else if (i === 4) {
              text('序号', 0, titleH, gridW);
              text('设备名', gridW, titleH, gridW);
              // 间隔线 -11, 应该-16,但是整体下移5了,所以-11
              line(ctx, gridW * 2,titleH - 11, gridW * 2, titleH + 5)
              text('型号', gridW * 2, titleH, gridW);
              // 间隔线
              line(ctx, gridW * 3,titleH - 11, gridW * 3, titleH + 5)
              text('数量', gridW * 3, titleH, gridW);
              // 间隔线
              line(ctx, gridW * 4,titleH - 11, gridW * 4, titleH + 5)
              text('单位', gridW * 4, titleH, gridW);
            } else if (i > 4 && ((i - 4) <= db.dataVos.length)) {
              text(parseFloat(i - 4), 0, titleH, gridW);
              text(db.dataVos[i - 5].name, gridW, titleH, gridW);
              // 间隔线
              line(ctx, gridW * 2,titleH - 11, gridW * 2, titleH + 5)
              text(db.dataVos[i - 5].type, gridW * 2, titleH, gridW);
              // 间隔线
              line(ctx, gridW * 3,titleH - 11, gridW * 3, titleH + 5)
              text(db.dataVos[i - 5].num, gridW * 3, titleH, gridW);
              // 间隔线
              line(ctx, gridW * 4,titleH - 11, gridW * 4, titleH + 5)
              text(db.dataVos[i - 5].unit, gridW * 4, titleH, gridW);
            } else if ((row - i) === 3) {
              text('辅料', 0, titleH, gridW);
              text(db.consignee, gridW, titleH, rectW - gridW);
            } else if ((row - i) === 2) {
              text('备注', 0, titleH, gridW);
              text(db.consignee, gridW, titleH, rectW - gridW);
            } else if ((row - i) === 1) {
              text('送货地址、联系人及电话', 0, titleH, gridW);
              text(db.consignee, gridW, titleH, rectW - gridW);
            } else if ((row - i) === 0) {
              console.log(rectH);
              text('项目负责人', 0, titleH, gridW);
              text(db.consignee, gridW, titleH, rectW - gridW);
              console.log(rectH);
            }
            let titleH = rectH + 16; // 文本所在高度, 16为11 + 5
            line(ctx, 0, rectH + 5, rectW, rectH + 5) // 横线
          }
          // 左边的边线
          line(ctx, 0.5, 5, 0.5, rectH + 5)
          // 右边的边线 -0.5是为了能够完全显示线,线差不多1px
          line(ctx, rectW-0.5, 5, rectW-0.5, rectH + 5)
          rectH = rectH + 5
          line(ctx, 0, rectH, rectW, rectH) // 底部横线
          // 左侧名称竖线
          line(ctx, gridW, 5, gridW, rectH + 5)
          let canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
          canvas.height = rectH* dpr
          for (var i = 0; i < canvasData.data.length; i += 4) {
            // 当该像素是透明的,则设置成白色
            if (canvasData.data[i + 3] == 0) {
              canvasData.data[i] = 255;
              canvasData.data[i + 1] = 255;
              canvasData.data[i + 2] = 255;
              canvasData.data[i + 3] = 255;
            }
          }
          ctx.scale(dpr, dpr)
          ctx.putImageData(canvasData, 0, 0)
        }
        drawTable();
        // 最后根据行的高度总和重新计算画布高度
        setTimeout(function(){
          that.setData({
            rectH:rectH * dpr, //所以当获取了数据的总行数后,需要重新计算画布的高度。
            canvas
          });
        })
        wx.canvasToTempFilePath({
          x: 0,
          y: 0,
          canvas, // 画布标识,传入 canvas 组件的 canvas-id
          fileType: 'png',
          quality: 1, // 图片质量 (0, 1]
          success: function (res) {
            console.log("store success: ", res);
            that.setData({
              imgUrl: res.tempFilePath
            })
            wx.hideLoading()
          },
          fail: function (res) {
            console.log("store fail: ", res);
            wx.hideLoading()
          },
        }, that)
      })

  },
})

还有很多缺点没有完善,不太灵活,仅作为个人学习参考

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
微信小程序通过 Canvas 2D 绘制海报可以用于商品展示、广告宣传等场景。下面介绍一下实现流程: 1. 页面结构 在页面中添加一个 Canvas 标签,设置 id 和宽高: ```html <canvas id="poster" style="width: 750rpx; height: 1334rpx;"></canvas> ``` 2. 获取 Canvas 上下文 在页面的 onLoad 函数中获取 Canvas 上下文: ```javascript let ctx = wx.createCanvasContext('poster'); ``` 3. 绘制背景 使用 Canvas 2D 绘制背景,可以使用 fillRect 方法绘制一个填充矩形: ```javascript ctx.setFillStyle('#ffffff'); ctx.fillRect(0, 0, 750, 1334); ``` 4. 绘制图片 使用 drawImage 方法绘制图片,需要先将图片下载到本地: ```javascript wx.getImageInfo({ src: 'https://example.com/image.png', success: function(res) { ctx.drawImage(res.path, 0, 0, 750, 500); } }); ``` 5. 绘制文本 使用 fillText 或者 strokeText 方法绘制文本,需要设置字体样式和对齐方式: ```javascript ctx.setFontSize(32); ctx.setTextAlign('center'); ctx.fillText('这是一段文本', 375, 600); ``` 6. 保存海报 使用 Canvas 2D 的 toTempFilePath 方法将绘制的海报保存到本地: ```javascript ctx.draw(false, function() { wx.canvasToTempFilePath({ x: 0, y: 0, width: 750, height: 1334, canvasId: 'poster', success: function(res) { console.log(res.tempFilePath); } }); }); ``` 完整代码: ```javascript Page({ onLoad: function() { let ctx = wx.createCanvasContext('poster'); ctx.setFillStyle('#ffffff'); ctx.fillRect(0, 0, 750, 1334); wx.getImageInfo({ src: 'https://example.com/image.png', success: function(res) { ctx.drawImage(res.path, 0, 0, 750, 500); } }); ctx.setFontSize(32); ctx.setTextAlign('center'); ctx.fillText('这是一段文本', 375, 600); ctx.draw(false, function() { wx.canvasToTempFilePath({ x: 0, y: 0, width: 750, height: 1334, canvasId: 'poster', success: function(res) { console.log(res.tempFilePath); } }); }); } }); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值