小程序canvas制图,(圆弧角,画圆,图片短边展示,多行省略,不影响布局)

224 阅读4分钟

先上效果图:   

image.png

wxml:

//生成图片时显示,其余时间隐藏起来,根据css一起使用
<view class='canvas-box'>
  <canvas canvas-id='canvas' style='width:{{windowW}}px;height:{{windowH}}px;' wx:if="{{hideCanvas}}"></canvas>
</view>

wxss:

/*再外侧包一个view,然后给view定位出去,就达到了隐藏的效果*/
.canvas-box{
  position: fixed;
  left: 10000px;
}

js: 

const { roundRect, roundImage, drawTextOverflow } = require("../../utils/util.js");
let rpx;  //宽度%比   适配手机宽度
let xh;  //高宽比     适配手机高度
page({
    data: {
    windowW: '',   //设备宽高
    windowH: '',  
    canvasQr: '',   //二维码图片
    canvasDetail: '', //详情项目图片地址
    clip_left: '',//左偏移值,
    clip_top: '', //上偏移值
    clip_width: '',//截取宽度,截取高度
    clip_height: '',
    hideCanvas:false,  //canvas的隐藏
    detailData: {},   //我的数据,自行改动
}

_downImg:function(e){
    wx.getSystemInfo({
      success: function(res) {
        rpx = res.windowWidth / 375;
        xh = res.windowHeight / res.windowWidth
        _this.setData({
          windowW: res.windowWidth,
          windowH: res.windowHeight,
          scrollerHeight: res.windowHeight - (res.windowWidth / 750) * 120
        })
      }
    });
    console.log(e)
    //这里是我调的接口得到的动态图片,你们根据自己要求去改
    this.wxFileImg(e.detail.codeUrl);
    wx.showLoading({
      title: '正在生成图片中',
      mask: true
    })
    this.setData({
      hideCanvas:true,
    })
  },
  //网络图片转临时 所有的网络图都要转一下,要不然有的真机不显示网络图片。  画布图片太多,就会很卡
  wxFileImg: function (codeUrl) {
    let _this = this;
    wx.downloadFile({
      url: codeUrl,
      success: function (resQrFile) {
        _this.setData({
          canvasQr: resQrFile.tempFilePath
        })
        wx.downloadFile({
          url: _this.data.detailData.logo,
          success: function (resDetailFile) {
            wx.getImageInfo({
              src: resDetailFile.tempFilePath,
              success(resDetailInfo) {
                let img_width = resDetailInfo.width,
                  img_height = resDetailInfo.height;
                //左偏移值,上偏移值,截取宽度,截取高度
                let clip_left, clip_top, clip_width, clip_height;

                clip_height = img_width ;
                if (clip_height > img_height) {
                  clip_height = img_height;
                  clip_width = clip_height;
                  clip_left = (img_width - clip_width) / 2;
                  clip_top = 0;
                } else {
                  clip_left = 0;
                  clip_top = (img_height - clip_height) / 2;
                  clip_width = img_width;
                }
                _this.setData({
                  canvasDetail: resDetailFile.tempFilePath,
                  clip_left,
                  clip_top,
                  clip_width,
                  clip_height
                })
                //画布id
                let canvas = wx.createCanvasContext('canvas');
                _this.canvasdraw(canvas);
              }
            })
          }
        })
      }
    })
  },
  canvasdraw: function (canvas) {
    let _this = this;
    let windowW = _this.data.windowW;
    let windowH = _this.data.windowH;
    let canvasQr = _this.data.canvasQr;
    let canvasDetail = _this.data.canvasDetail;
    let clip_left = _this.data.clip_left;
    let clip_top = _this.data.clip_top;
    let clip_width = _this.data.clip_width;
    let clip_height = _this.data.clip_height;
    let detailData = _this.data.detailData;

    // 整画布 背景
    canvas.setFillStyle('#EFEFEF');
    canvas.fillRect(0, 0, windowW, windowH);
    canvas.draw(true);
    // 主画布
    console.log(xh)
    // 由于手机的高宽比不同,适配主画布的高度,可以根据自己的画布要求自行调整
    if (xh > 1.9) {
      roundRect(canvas, 15 * rpx, 100 * rpx, windowW * 0.92, windowH * 0.65, 10 * rpx, '#FFFFFF');
    } else if (xh > 1.8)  {
      roundRect(canvas, 15 * rpx, 80 * rpx, windowW * 0.92, windowH * 0.75, 10 * rpx, '#FFFFFF');
    }else{
      roundRect(canvas, 15 * rpx, 80 * rpx, windowW * 0.92, windowH * 0.8, 10 * rpx, '#FFFFFF');
    }

    //详情图片
    roundImage(canvas, 10 * rpx, canvasDetail, clip_left, clip_top, clip_width, clip_height, 15 * rpx, 30 * rpx, 345 * rpx, 245 * rpx, '#ffffff');
    //遮挡图片圆角
    roundRect(canvas, 15 * rpx, 210 * rpx, windowW * 0.92, 90 * rpx, 10 * rpx, '#FFFFFF');
    // 项目名称 2行展示
    drawTextOverflow(canvas, detailData.title, 305 * rpx, 2, 24, "#333", 28, 35 * rpx, 260 * rpx);

    // 项目价格
    canvas.setFontSize(20);
    canvas.fillStyle = "#DD2534";
    canvas.setTextAlign('left');
    //文字加粗  这里是利用了两个文字重叠实现的加粗效果 坐标偏移了半个像素的位置
    canvas.fillText(detailData.priceText, 35 * rpx, 319.5 * rpx);
    canvas.fillText(detailData.priceText, 34.5 * rpx, 320 * rpx);
    // 项目区域
    canvas.setFontSize(16);
    canvas.fillStyle = "#666666";
    canvas.setTextAlign('left');
    canvas.fillText(detailData.district, 35 * rpx, 350 * rpx);

    // 项目面积
    canvas.setFontSize(16);
    canvas.fillStyle = "#666666";
    canvas.setTextAlign('left');
    canvas.fillText(detailData.landAreaText || detailData.houseSizeText, 35 * rpx, 380 * rpx);

    //二维码
    canvas.drawImage(canvasQr, 35 * rpx, 410 * rpx, 100 * rpx, 100 * rpx);

    //名字
    canvas.setFontSize(16);
    canvas.fillStyle = "#333333";
    canvas.setTextAlign('left');
    canvas.fillText(detailData.userName, 155 * rpx, 445 * rpx);
    // 线
    canvas.beginPath();
    // 这里要是设置为1  线的颜色就很深而且线比较粗
    canvas.setLineWidth(0.1);
    canvas.setFillStyle('#EFEFEF');
    canvas.moveTo(155 * rpx, 460 * rpx);
    canvas.lineTo(325 * rpx, 460 * rpx);
    canvas.stroke();

    // 提示
    canvas.setFontSize(16);
    canvas.fillStyle = "#999999";
    canvas.setTextAlign('left');
    canvas.fillText('微信长按或扫一扫打开', 155 * rpx, 490 * rpx);
    canvas.draw(true, setTimeout(function () {
      _this.daochu()
    }, 1000));
  },

  //整个画布图片
  daochu: function () {
    let _this = this;
    let windowW = _this.data.windowW;
    let windowH = _this.data.windowH;

    //这里没有处理生产图片时间过长的操作,我还没试着做,产品决定直接就不用画布了,
    //毕竟太耗性能,等待时间也不可控,有懂得大佬能告知一下吗?

    wx.canvasToTempFilePath({
      x: 0,
      y: 0,
      width: windowW,
      height: windowH,
      destWidth: windowW * 3,  //3倍像素  更清晰
      destHeight: windowH * 3,
      canvasId: 'canvas',
      success: function (res) {
        // console.log(res)
        wx.hideLoading();
        wx.saveImageToPhotosAlbum({
          filePath: res.tempFilePath,
          success(res) {
          }
        })
        wx.previewImage({
          urls: [res.tempFilePath],
        })
        _this.setData({
          hideCanvas: false
        })
      },
      fail:function(err){
        wx.hideLoading();
        // console.log(err)
      }
    })
  },
})

util.js:

/**
 * canvas绘制圆角矩形
 * @param {Object} context - canvas组件的绘图上下文
 * @param {Number} x - 矩形的x坐标
 * @param {Number} y - 矩形的y坐标
 * @param {Number} w - 矩形的宽度
 * @param {Number} h - 矩形的高度
 * @param {Number} r - 矩形的圆角半径
 * @param {String} [c = 'transparent'] - 矩形的填充色
 */
const roundRect = (context, x, y, w, h, r, c = 'transparent') => {
  if (w < 2 * r) {
    r = w / 2;
  }
  if (h < 2 * r) {
    r = h / 2;
  }
  context.beginPath();
  context.fillStyle = c;

  context.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
  context.moveTo(x + r, y);
  context.lineTo(x + w - r, y);
  context.lineTo(x + w, y + r);

  context.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
  context.lineTo(x + w, y + h - r);
  context.lineTo(x + w - r, y + h);

  context.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
  context.lineTo(x + r, y + h);
  context.lineTo(x, y + h - r);

  context.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
  context.lineTo(x, y + r);
  context.lineTo(x + r, y);

  context.fill();
  context.closePath();
};


/**
 * canvas绘制圆角图片
 * @param {Object} context - canvas组件的绘图上下文
 * @param {Number} r - 圆角半径
 * @param {String} path - 图片地址
 * @param {Number} sx - 源图像的矩形选择框的左上角 x 坐标
 * @param {Number} sy - 源图像的矩形选择框的左上角 y 坐标
 * @param {Number} sWidth - 源图像的矩形选择框的宽度
 * @param {Number} sHeight - 源图像的矩形选择框的高度
 * @param {Number} dx - 图像的左上角在目标 canvas 上 x 轴的位置
 * @param {Number} dy - 图像的左上角在目标 canvas 上 y 轴的位置
 * @param {Number} dWidth - 在目标画布上绘制图像的宽度,允许对绘制的图像进行缩放
 * @param {Number} dHeight - 在目标画布上绘制图像的高度,允许对绘制的图像进行缩放
 * @param {String} c - 矩形的填充色
 */
const roundImage = (context, r, path, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight, c) => {
  context.save();
  roundRect(context, dx, dy, dWidth, dHeight, r, c);
  context.fill();
  context.clip();
  context.drawImage(path, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
  context.restore();
};
/**
* 绘制圆形图片
* @param {Object} context - canvas组件的绘图上下文
* @param {String} path - 图片地址
* @param {Number} x - 图片的x坐标
* @param {Number} y - 图片的y坐标
* @param {Number} r - 图片的半径
*/
const circleImage = (context, path, x, y, r) => {
  let d = 2 * r;
  let cx = x + r;
  let cy = y + r;

  context.save();
  context.beginPath();
  context.arc(cx, cy, r, 0, 2 * Math.PI);
  context.fill();
  context.clip();
  context.drawImage(path, x, y, d, d);
  context.restore();
};
/**
 * canvas多行文本溢出
 * @param {Object} context - canvas组件的绘图上下文
 * @param {String} text - 文本内容
 * @param {Number} maxWidth - 文本最大宽度
 * @param {Number} maxRow - 文本最多显示行数
 * @param {Number} fontSize - 字体样式
 * @param {String} color - 文本颜色
 * @param {Number} lineHeight - 文本行高
 * @param {Number} x - 文本的x坐标
 * @param {Number} y - 文本的y坐标
 */
const drawTextOverflow = (context, text, maxWidth, maxRow, fontSize, color, lineHeight, x, y) => {
  let arr = [];
  let temp = '';
  let row = [];

  text = text.replace(/[\r\n]/g, ''); // 去除回车换行符
  arr = text.split('');
  context.setFontSize(fontSize);  // 注意:一定要先设置字号,否则会出现文本变形
  context.fillStyle = color;

  if (context.measureText(text).width <= maxWidth) {
    row.push(text);
  } else {
    for (let i = 0; i < arr.length; i++) {
      // 超出最大行数且字符有剩余,添加...
      if (row.length == maxRow && i < arr.length - 1) {
        row[row.length - 1] += '...';
        break;
      }

      // 字符换行计算
      if (context.measureText(temp).width < maxWidth) {
        temp += arr[i];
        // 遍历到最后一位字符
        if (i === arr.length - 1) {
          row.push(temp);
        }
      } else {
        i--;  // 防止字符丢失
        row.push(temp);
        temp = '';
      }
    }
  }
  // 绘制文本
  for (let i = 0; i < row.length; i++) {
    context.fillText(row[i], x, y + i * lineHeight, maxWidth);
  }
  return row.length * lineHeight;  // 返回文本高度
};

util转载于www.jianshu.com/p/6c600e740…