小程序canvas绘制海报图,并保存至系统相册案例

前言:公司新项目需要做小程序,一步一步摸着坑,走到上线,感觉收获颇多,其中一处就是用原生组件canvas 绘制海报图,并保存到系统相册的案例,现在将案例分享出来,和大家一同探讨。

预览效果图

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

逐步分析瓦解

分层

  1. (最底层)上边背景图绘制,下边背景颜色绘制
  2. (第二层)头像(圆形图片)和头像边框(圆环)、下边白色圆角矩形
  3. (第三步)奖品图片,小程序二维码
  4. 昵称,居中文字,“发起一个抽奖”文字居中
  5. 奖品名称(一行文字不同字体颜色)
  6. 时间,两条分割线
  7. 最后的 长按识别文字

分析

  1. 难点1 圆形图片问题
  2. 难点2 文字居中问题
  3. 难点3 圆角矩形问题
  4. 难点4 一行文字不同颜色问题,一行文字不同大小类似
  5. 难点5 文字换行显示(一行显示不了,需另起一行,上图尚未体现)
    针对以上是哪个难点,逐一击破

揭晓

针对以上问题,可以先进行思索,是否脑中有方法可以解决。那我就不卖关子,列位看官,拿好水杯,姿势摆好,开始秀操作。

  1. 圆形图片
function drawCircleImg(ctx, src, x, y, width, heigth) {
  ctx.save(); // 先保存状态 已便于画完圆再用
  ctx.beginPath(); //开始绘制
  //先画个圆
  ctx.strokeStyle = "rgba(255,255,255,0)";
  ctx.arc(x + width / 2, y + heigth / 2, width / 2, 0, Math.PI * 2, false);
  ctx.stroke();
  ctx.clip(); // 这步不能丢
  ctx.drawImage(src, x, y, width, heigth);
  ctx.restore(); // 恢复画布不能忘记
}
  1. 文字居中问题
/**
 * 获取文本折行
 * @param {Object} obj
 * @return {Array} arrTr
 */
function getTextLine(obj, ctx) {
  const { size, text, width } = obj;
  const arrText = text.split("");
  const arrTr = [];
  let line = "";
  ctx.setFontSize(size);
  for (let i = 0; i < arrText.length; i++) {
    const testLine = line + arrText[i];
    const metrics = ctx.measureText(testLine);
    const textWidth = metrics.width;
    if (textWidth > width && i > 0) {
      arrTr.push(line);
      line = arrText[i];
    } else {
      line = testLine;
    }
    if (i == arrText.length - 1) {
      arrTr.push(line);
    }
  }
  return arrTr;
}
/**
 * 多余文字省略,居中显示文本
 * @param {Object} obj
 * @param {Object} ctx 画布
 * @param {Number} W 整体宽度
 * @param {Number} width 限定文本长度,超出 显示省略号
 */
function ellipsisText(obj, ctx, W, width = 120) {
  const { text, size } = obj;
  const tr = getTextLine({ text, size, width }, ctx);
  let txt = tr[0];
  if (tr.length > 1) {
    txt += '...'
  }
  ctx.setFontSize(size);
  const metrics = ctx.measureText(txt).width;
  obj.x = Math.floor((W - metrics) / 2);
  obj.text = txt;
  drawText(obj, ctx)
}
/**
 * 绘制单行文本
 * @param {Object} obj
 */
function drawText(obj, ctx) {
  const { color, size, align, baseline, text, x, y, bold } = obj;
  ctx.save();
  ctx.fillStyle = color;
  ctx.setFontSize(size);
  ctx.setTextAlign(align);
  ctx.setTextBaseline(baseline);
  ctx.fillText(text, x, y);
  if (bold) {
    ctx.fillText(text, x, y - 0.5);
    ctx.fillText(text, x - 0.5, y);
  }
  ctx.fillText(text, x, y);
  if (bold) {
    ctx.fillText(text, x, y + 0.5);
    ctx.fillText(text, x + 0.5, y);
  }
  ctx.restore();
}
  1. 圆角矩形问题
    先贴2张图便于理解
    圆弧 圆角矩形
function roundRect(ctx, x, y, w, h, r) {
  ctx.beginPath()
  ctx.fillStyle = '#fff';
  ctx.lineJoin = 'round';
  // 左上角圆弧
  ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5)
  ctx.moveTo(x + r, y)
  ctx.lineTo(x + w - r, y)
  ctx.lineTo(x + w, y + r)
  // 右上角圆弧
  ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2)
  ctx.lineTo(x + w, y + h - r)
  ctx.lineTo(x + w - r, y + h)
  // 右下角圆弧
  ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5)
  ctx.lineTo(x + r, y + h)
  ctx.lineTo(x, y + h - r)
  // 左下角圆弧
  ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI)
  ctx.lineTo(x, y + r)
  ctx.lineTo(x + r, y)
  ctx.fill()
  ctx.closePath()
}
  1. 一行文字不同颜色问题,一行文字不同大小类似
// 一行文本不同颜色
function diffColor(title, pxToRpx, ctx) {
  textWrap(title, ctx);
  const metrics = ctx.measureText(title.text);
  const w = metrics.width;
  const fix = ctx.measureText('一等奖:');
  const sub = ctx.measureText(title.stock);
  if (w + 10 + sub.width > title.width) {
    drawText({
      x: title.x + fix.width,
      y: title.y + 4 * pxToRpx + title.height,
      color: '#B0B0B0',
      size: 22 * pxToRpx,
      align: "left",
      baseline: "top",
      text: title.stock,
      bold: false,
    }, ctx);
    return 14 * pxToRpx + title.height + 22 * pxToRpx;
  } else {
    drawText({
      x: title.x + w + 10,
      y: title.y + 4 * pxToRpx,
      color: '#B0B0B0',
      size: 22 * pxToRpx,
      align: "left",
      baseline: "top",
      text: title.stock,
      bold: false,
    }, ctx);
    return title.height + 4 * pxToRpx;
  }
}
  1. 文字换行显示
/**
 * 文本换行
 * @param {Object} obj
 */
function textWrap(obj, ctx) {
  const { x, y, height, color, size, align, baseline, bold, maxRow } = obj;
  const tr = getTextLine(obj, ctx);
  const row = tr.length;
  const minRow = Math.min(row, maxRow);
  // const txtHeightArr = [];
  // txtHeightArr.push(minRow * height);
  for (let i = 0; i < minRow; i++) {
    if (i < minRow) {
      let txt = {
        x,
        y: y + i * height,
        color,
        size,
        align,
        baseline,
        text: tr[i],
        bold
      };
      if (i == minRow - 1 && row > maxRow) {
        txt.text = txt.text.substring(0, txt.text.length - 2) + "......";
      }
      drawText(txt, ctx);
    }
  }
}

素材

准备3张网络图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

  1. 以上难点在于对于基本api的理解,搜索对应的挨批均能搜索,所以我没有体现出来
  2. 针对canvas 需要一些计算。对坐标的理解

题外话

已上源码我已上传至github地址
若感兴趣可关注公众号 ruanjianxiaoyu 后台留言,可相互探讨学习。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值