微信小程序-海报制作(canvas)

Posted yw00yw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微信小程序-海报制作(canvas)相关的知识,希望对你有一定的参考价值。

文档

wxml

<view class="canvas-container">
  <canvas
    canvas-id="myCanvas"
    class="myCanvas"
    style="width: {{canvasWidth}}px; height: {{canvasHeight}}px;">
  </canvas>
  <view class="btn-save" bindtap="savePoster">保存到相册</view>
</view>

wxss

.myCanvas {
  display: block;
  margin: 0 auto;
}
.btn-save {
  margin: 20rpx;
  padding: 10rpx;
  border: 1rpx solid #eee;
  text-align: center;
  font-size: 24rpx;
  border-radius: 4rpx;
}

js

// pages/canvas/canvas.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    canvasWidth: 296, // 绘图区域宽度
    canvasHeight: 426, // 绘图区域高度
    bgUrl: 'https://img-crs.vchangyi.com/standard/new_buy_poster_bg2.png',
    goodsUlr: 'https://img-crs.vchangyi.com/goods16233105466440.jpg',
    card_url: '' // 生成图片的id
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    const { canvasWidth, canvasHeight, bgUrl, goodsUlr } = this.data;
    // 定义画布对象
    const ctx = wx.createCanvasContext("myCanvas", this);

    // 获取图片
    Promise.all([
      this.getCode(bgUrl),
      this.getCode(goodsUlr)
    ]).then(res => {
      // 背景图
      ctx.drawImage(res[0].tempFilePath, 0, 0, canvasWidth, canvasHeight);

      // 白背景
      this.roundedRect(
        ctx,
        10,
        56,
        275,
        360,
        5,
        5,
        5,
        5
      );
      ctx.fillStyle = "#ffffff";
      ctx.fill();

      // 商品图
      ctx.drawImage(res[1].tempFilePath, 20, 66, 255, 255);
      
      // 标题
      this.drawText(ctx, {
        x: 20,
        y: 255 + 66 + 20,
        color: "#333333",
        size: 14,
        // isCenter: true,
        text: '哈哈哈,大傻瓜,哈哈哈,大傻瓜哈哈哈',
        width: "",
        bold: true
      });

      this.drawText(ctx, {
        x: 20,
        y: 255 + 66 + 40,
        color: "#333333",
        size: 14,
        isCenter: true,
        text: '哈哈哈,大傻瓜,哈哈哈',
        width: 255,
        bold: true
      });

      // 价格
      this.drawText(ctx, {
        x: 20,
        y: 390,
        color: "#FA4046",
        size: 16,
        isCenter: true,
        text: "¥",
        width: "",
        bold: true
      });

      // 元
      this.drawText(ctx, {
        x: 20 + this.measureText('¥'),
        y: 378,
        color: "#FA4046",
        size: 32,
        isCenter: true,
        text: 2,
        width: "",
        bold: true
      });

      // 角分
      this.drawText(ctx, {
        x: 20 + 3 + this.measureText('¥') + this.measureText('2', 32),
        y: 390,
        color: "#FA4046",
        size: 16,
        isCenter: true,
        text: "." + '02',
        width: "",
        bold: true
      });

      // 划线价
      this.drawText(ctx, {
        x: 20 + 3 + 10 + this.measureText('¥') + this.measureText('2', 32) + this.measureText('.02', 16),
        y: 394,
        color: "#a6a6a6",
        size: 12,
        isCenter: true,
        text: '¥7.99',
        width: "",
        bold: true
      });

      // 划线
      this.lineation(ctx, {
        sx: 20 + 3 + 10 + this.measureText('¥') + this.measureText('2', 32) + this.measureText('.02', 16),
        sy: 399,
        ex: 20 + 3 + 5 + 10 + this.measureText('¥') + this.measureText('2', 32) + this.measureText('.02', 16) + this.measureText('¥7.99', 12),
        ey: 399,
        lineWidth: "",
        color: "#a6a6a6"
      });

      ctx.draw();

      setTimeout(() => {
        console.log('生成', '....')
        this.exportImg("myCanvas", 296, 426);
      }, 1000);
    })
  },
  //把网路图片下载成本地图片
  getCode(img) {
    return new Promise((resolve, reject) => {
      wx.downloadFile({
        url: img,
        success: (res) => {
          resolve(res)
        }
      })
    })
  },
  // 生成指定的图片
  exportImg(idn, w, h) {
    console.log(idn, w, h)
    let that = this;
    wx.canvasToTempFilePath(
      {
        x: 0,
        y: 0,
        width: w,
        height: h,
        destWidth: w,
        destHeight: h,
        canvasId: idn,
        success: function(res) {
          console.log(res)
          wx.getImageInfo({
            src: res.tempFilePath,
            success: function(res) {
              console.log(that, '.......');
              // 生成的canvas图片路径
              that.setData({
                card_url: res.path
              })
            }
          });
        },
        fail(err) {
          console.error(err);
        },
        complete() {}
      },
      this
    );
  },
  // 保存海报
  savePoster() {
    // 将图片保存到相册
    wx.saveImageToPhotosAlbum(
      {
        filePath: this.data.card_url,
        success(res) {
          wx.showToast({
            title: '成功',
            icon: 'success',
            duration: 2000
          })
        },
        fail() {
          // 判断是否获得了用户保存相册授权
          wx.getSetting({
            success: auth => {
              if (!auth.authSetting["scope.writePhotosAlbum"]) {
                wx.showToast({
                  title: '请开启相册授权',
                  icon: 'error',
                  duration: 2000
                })
              }
            }
          });
        }
      }
    );
  },
  /**
     * 工具  圆角矩形
     * x, y 坐标
     * width, height 宽高
     * 圆角: 左下, 右下, 右上, 左上
     */
  roundedRect(
    ctx,
    x,
    y,
    width,
    height,
    RTradius,
    RBradius,
    LBradius,
    LTradius
  ) {
    // ctx.strokeStyle = "#fffbff";
    ctx.beginPath();
    // 右上
    ctx.moveTo(x, y + RTradius);
    ctx.lineTo(x, y + height - RTradius);
    ctx.quadraticCurveTo(x, y + height, x + RTradius, y + height);
    // 右下
    ctx.lineTo(x + width - RBradius, y + height);
    ctx.quadraticCurveTo(
      x + width,
      y + height,
      x + width,
      y + height - RBradius
    );
    // 左下
    ctx.lineTo(x + width, y + LBradius);
    ctx.quadraticCurveTo(x + width, y, x + width - LBradius, y);
    // 左上
    ctx.lineTo(x + LTradius, y);
    ctx.quadraticCurveTo(x, y, x, y + LTradius);
    // ctx.stroke();
    ctx.closePath();
  },
  /**
   * 文本填充方法
   * @param {String} ctx 画布实例对象名
   * @param {Object} obj 文本属性
   * obj = {
      x: 绘制文本的左上角 x 坐标位置,如果这个文本要基于一个盒子水平居中,此值应该写盒子的左上角 x 坐标位置,
      y: 绘制文本的左上角 y 坐标位置,
      color: 绘制文本的颜色,
      size: 绘制文本的字体大小,
      isCenter: 文字的水平对齐方式(true,false),
      text: 在画布上输出的文本内容,
      width: 展示当前文本盒子的宽度,在文本需要水平居中是要加这个参数,
      bold: 字体是否需要加粗(true、false)
    }
  */
  drawText(ctx, obj) {
    ctx.save();
    ctx.fillStyle = obj.color;
    ctx.setTextBaseline("top");
    if (obj.bold) {
      ctx.font = `normal bold ${obj.size}px PingFangSC-Regular`;
    } else {
      ctx.font = `normal 100 ${obj.size}px PingFangSC-Regular`;
    }
    if (obj.isCenter && obj.width) {
      ctx.fillText(
        obj.text,
        obj.x + (obj.width - ctx.measureText(obj.text).width) / 2,
        obj.y
      );
    } else {
      ctx.fillText(obj.text, obj.x, obj.y);
    }
    ctx.restore();
    ctx.closePath();
  },
  // 文本宽度计算
  measureText(text, fontSize = 10) {
    let width = 0;
    String(text)
      .split("")
      .forEach(item => {
        if (/[a-zA-Z]/.test(item)) {
          width += 7;
        } else if (/[0-9]/.test(item)) {
          width += 5.5;
        } else if (/\\./.test(item)) {
          width += 2.7;
        } else if (/-/.test(item)) {
          width += 3.25;
        } else if (/[\\u4e00-\\u9fa5]/.test(item)) {
          // 中文匹配
          width += 10;
        } else if (/\\(|\\)/.test(item)) {
          width += 3.73;
        } else if (/\\s/.test(item)) {
          width += 2.5;
        } else if (/%/.test(item)) {
          width += 8;
        } else {
          width += 10;
        }
      });
    return (width * fontSize) / 10;
  },
  /**
   * 文本划线
   * @param {String} ctx 画布实例对象名
   * @param {Object} obj
   * obj = {
      sx: 划线的开始位置的 x 坐标位置,
      sy: 划线的开始位置的 y 坐标位置,
      ex: 划线的结束位置的 x 坐标位置,
      ey: 划线的结束位置的 y 坐标位置,
      lineWidth: 线条的宽度,
      color: 画线的颜色
    }
  */
  lineation(ctx, obj) {
    // 开始一个新的绘制路径
    ctx.beginPath();
    // 设置线条的宽度
    ctx.setLineWidth(1);
    // 定义直线的起点坐标为
    ctx.moveTo(obj.sx, obj.sy);
    // 定义直线的终点坐标为
    ctx.lineTo(obj.ex, obj.ey);
    // 设置画线的颜色
    ctx.strokeStyle = obj.color;
    // 沿着坐标点顺序的路径绘制直线
    ctx.stroke();
    // 关闭当前的绘制路径
    ctx.closePath();
  }
})

效果图

在这里插入图片描述

以上是关于微信小程序-海报制作(canvas)的主要内容,如果未能解决你的问题,请参考以下文章

微信小程序-海报制作(canvas)

微信小程序-海报制作(canvas)

uniapp 手写canvas海报(兼容android/ios/h5/微信小程序)

微信小程序canvas画图,保存页面为海报

用canvas绘制微信小程序海报页面并保存相册-适用微信原生

利用微信小程序中Canvas API来合成海报生成组件封装