便利贴--42{微信小程序中使用canvas -避坑环节...}

27 篇文章 0 订阅
1 篇文章 0 订阅

便利贴--42{微信小程序中使用canvas -避坑环节...}

html

<canvas type="2d" id="cardCanvas" canvas-id="cardCanvas"></canvas>

最好不要写在组件里,因为获取不到cardCanvas对象,渲染不了,以后再补充这个问题
ps:(最新class有更改,使用模板参照prototype方法里的判断自行设置…)

2022.5.25更新 :1.加入阴影(渐变制作)把画圆角路径分开成函数 2.再次运行anginDraw绘画任务时判断是否完成上次绘画,提供两种逻辑处理方式

class

附链接: 微信小程序canvas class源码以及使用数据等下载
在这里插入图片描述
在这里插入图片描述

使用

直接对类实例化

      new canvasDr({ id: "#cardCanvas", data: inforData }).drawing();

其中data的数据格式大概是这样

let data =  [
    //头像
    {
      value: '',
      type: 'img',
      props: 'heard',
      order: 0,
      size: [140, 10, 150, 163],
    style: {
      radius: 65 / 2,
      border: {
        radius: 65 / 2,
        color: "#15499C",
        lineWidth: 5,
      }
    },
  //头像阴影渐变层
  {
    type: 'gradual',
    order: 1,
    size: [50, 30, 75, 75],
    style: {
      size: [80, 70, 15, 80, 70, 50],
      color: {
        0: '#CFCFCF',
        1: 'white'
      }
    }
  },
     {
      //背景图
      value: "",
      type: 'img',
      props: 'back',
      order: 1,
      size: [10, 10, 170, 163]
    },
    {
      type: 'text',
      order: 3,
      props: 'name',
      value: '',
      style: {
        color: '#000',
        fontFrist: 'normal 600 ',
        fontSize: 16,
        fontLast: 'px PingFang SC'
      },
      size: [20, 55]
    },
    {
      type: 'text',
      order: 3,
      props: 'post',
      value: '',
      style: {
        color: '#15499C',
        fontFrist: 'normal 400 ',
        fontSize: 14,
        fontLast: 'px PingFang SC'
      },
      size: [95, 55]
    },
    {
      type: 'text',
      order: 3,
      props: 'tel',
      value: '',
      style: {
        color: '#333333',
        fontFrist: 'normal 400 ',
        fontSize: 14,
        fontLast: 'px PingFang SC'
      },
      size: [20, 78]
    },
    {
      type: 'text',
      order: 4,
      props: 'companyName',
      value: '',
      style: {
        color: '#333333',
        fontFrist: 'normal 400 ',
        fontSize: 14,
        fontLast: 'px PingFang SC',
      maxLetter: 18
      },
      size: [30, 134]
    },
    {
      type: 'text',
      order: 5,
      props: 'companyAddress',
      value: '',
      style: {
        color: '#333333',
        fontFrist: 'normal 400 ',
        fontSize: 12,
        fontLast: 'px PingFang SC',
      maxLetter: 18
      },
      size: [30, 154]
    },
  ],

class源码

class canvasDr {
  constructor(val) {
    this.id = val.id;
    this.data = this.deepClone(val.data);
    this.canvas = '';
    this.cxt = '';
    this.dpr = '';
    this.begin();
    this.x = '';
    this.y = '';
    this.back = val.backFn;
    this.needSaveImg = val.saveImg || false;
    this.onDraw = false;
    //进行比例转换,以300*240为基准,因为小程序分享最大尺寸是这个
    this.cx = function (x) {
      return ((x / 300 * this.x) * this.dpr)
    }
    this.cy = function (y) {
      return ((y / 240 * this.y) * this.dpr)
    }
    this.notReady = true;
    this.readyTime = 0;
    return this;
  }
  drawing() {
    this.onDraw = true;
    console.log(this.onDraw, '开始绘画')
    //   判断canvas是否初始化完成
    if (this.notReady || !this.data) {
      this.readyTime++
      if (this.readyTime == 10) {
        wx.showToast({
          title: "图片渲染失败",
          icon: "none",
        });
        return
      } else {
        setTimeout(() => {
          this.drawing()
        }, 200);
        return
      }
    }
    //背景填充
    this.ctx.fillStyle = "#E6E6E6";
    this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);

    let data = this.data;
    let leng = data.length;
    //冒泡排序
    for (let i = 0; i < leng; i++) {
      for (let m = i; m < leng - 1; m++) {
        if (data[m].order > data[m + 1].order) {
          [data[m], data[m + 1]] = [data[m + 1], data[m]]
        }
      }
    }
    let list = data;
    //递归
    let k = 0,
      i = 0;
    console.log(list)
    let doitImg = () => {
      //强制停止当前渲染列表
      if (this.over) {
        this.over = false;
        this.onDraw = false;
        // 重新启动新的绘画
        console.log('已经停止渲染')
        this.drawing();
        return
      }
      //递归防错误
      i++;
      if (i > 100) {
        return
      }
      if (list[k]) {
        console.log(k)
        // console.log(list[k].props)
        // console.log(list[k])
        let methods = '',
          mData = [];
        if (list[k].type == 'img') {
          // 图片
          methods = 'useImg';
          mData = [list[k].value, list[k].style, list[k].size]
        } else if (list[k].type == 'rect') {
          // 圆角矩形
          methods = 'drawRoundedRect';
          mData = [list[k].style, list[k].size]
        } else if (list[k].type == 'text') {
          // 文字
          methods = 'drawText';
          mData = [list[k].value, list[k].style, list[k].size]
        } else if (list[k].type == 'fill') {
          // 填充
          methods = 'fillRect';
          mData = [list[k].style, list[k].size]
        } else if (list[k].type == 'gradual') {
          // 渐变
          methods = 'gradualChange';
          mData = [list[k].style, list[k].size]
        }
        if (methods == '') {
          wx.showToast({
            title: '数据格式错误',
            icon: 'none'
          })
          return
        }
        this[methods](...mData).then(() => {
          k++
          // setTimeout(() => {
          doitImg()
          // }, 500);
        })
      } else {
        this.onDraw = false;
        this.saveImg(this.back)
        console.log(this.onDraw, '结束绘画')
      }
    }
    doitImg()
  }
  begin() {
    wx.createSelectorQuery()
      .select('#' + this.id)
      .fields({
        node: true,
        size: true,
      })
      .exec(this.init.bind(this));
    //检查canvas的dom是否可正常读取到
    //   .exec((res) => {
    //     console.log(res, "seerererwr");
    //   });
  }
  init(res) {
    //初始化canvas 设置像素为实际尺寸的倍数,增加清晰度
    this.dpr = wx.getSystemInfoSync().pixelRatio;
    this.x = res[0].width;
    this.y = res[0].height;
    this.canvas = res[0].node;
    this.ctx = this.canvas.getContext("2d");
    this.canvas.width = res[0].width * this.dpr;
    this.canvas.height = res[0].height * this.dpr;
    //定位
    // this.rectangle([0, 1, 300, 239], "red");
    this.notReady = false;
  }
  fillRect(style, size) {
    return new Promise((res, rej) => {
      size = this.doData(size);
      this.ctx.fillStyle = style.color;
      style = this.doData(style)
      //剪切无效,换成画矩形
      // if (style.radius) {
      //   style.radius = this.doData(style.radius)
      //   //开始路径,剪切处理
      //   this.ctx.save();
      //   var width = size[2];
      //   var height = size[3];
      //   var x = size[0],
      //     y = size[1],
      //     radius = style.radius;
      //   //开始路径,剪切处理
      //   this.ctx.save();
      //   this.drawRoundedLine(width,
      //     height,
      //     x,
      //     y,
      //     radius) //圆角路线
      //   this.ctx.clip(); //剪切路径
      //   //恢复状态
      //   this.ctx.restore();
      // }
      this.ctx.fillRect(...size);
      res();
    });
  }
  //绘画文字
  drawText(title, val, size) {
    return new Promise((res, rej) => {
      //手动加省略号
      if (val.maxLetter && title.length >= val.maxLetter) {
        title = title.split("").splice(0, val.maxLetter).join("") + "..."
      }
      size = this.doData(size)
      val = this.doData(val)
      let font = (val.fontFrist ? val.fontFrist : '') +
        val.fontSize +
        (val.fontLast ? val.fontLast : "px Arial");
      this.ctx.font = font;
      //位置样式
      if (val.textAlign) {
        this.ctx.textAlign = val.textAlign;
      }
      this.ctx.fillStyle = val.color;
      this.ctx.fillText(title, ...size);
      res();
    });
  }
  // 生成有圆角的矩形
  drawRoundedRect(val, size, dotBig = '') {
    if (!dotBig) {
      size = this.doData(size)
      val = this.doData(val)
    }
    let x = size[0],
      y = size[1],
      width = size[2],
      height = size[3],
      radius = val.radius,
      color = val.color,
      lineWidth = val.lineWidth;
    return new Promise((res, rej) => {
      this.ctx.strokeStyle = color;
      this.ctx.lineWidth = lineWidth;

      this.drawRoundedLine(width,
        height,
        x,
        y,
        radius) //圆角路线
      this.ctx.closePath();
      if (val.fill) {
        this.ctx.fillStyle = val.fill;
        this.ctx.fill();
      }
      this.ctx.stroke();
      res();
    });
  }
  //画矩形
  rectangle(data, color) {
    this.ctx.strokeStyle = color;
    this.ctx.lineWidth = "4";
    data = this.doData(data)
    this.ctx.rect(...data);
    this.ctx.stroke();
  }
  //话图片
  useImg(url, style, data) {
    return new Promise((res, rej) => {
      var image = this.canvas.createImage();
      //适应偏移图片
      //  根据字段长度动态设置格式: data[n]['style'] = {
      //     widths: d.name.length,
      //     fontSize: 11,//单个文字长度
      //     maxLetter: 18//最大长度范围
      //   };
      if (style) {
        if (style.widths && style.maxLetter && style.fontSize) {
          let num = style.widths > style.maxLetter ?
            style.maxLetter / 2 :
            style.widths / 2;

          data[0] = data[0] - (num * style.fontSize);
          console.log(data, "sdfsd")
        }
      }
      data = this.doData(data)
      image.src = url;
      image.onload = () => {
        if (style) {
          if (style.radius) {
            style = this.doData(style)
            var width = data[2];
            var height = data[3];
            var x = data[0],
              y = data[1];
            var radius = style.radius;
            //开始路径,剪切处理
            this.ctx.save();
            this.drawRoundedLine(width,
              height,
              x,
              y,
              radius) //圆角路线
            this.ctx.clip(); //剪切路径
            this.ctx.drawImage(image, ...data);
            //恢复状态
            this.ctx.restore();
          }
          if (style.border) {
            //加边框
            style.border = this.doData(style.border)
            this.drawRoundedRect(style.border,
              //   [data[0] - r, data[1] - r, data[2] + r, data[3] + r])
              data, 'dotBig')
          }
        } else {
          this.ctx.drawImage(image, ...data);
        }
        res();
      };
    });
  }
  // 阴影 渐变
  gradualChange(style, size) {
    return new Promise((res, rej) => {
      size = this.doData(size)
      style.size = this.doData(style.size)
      let grd = this.ctx.createRadialGradient(...style.size);
      for (let k in style.color) {
        grd.addColorStop(k, style.color[k]);
      }
      this.ctx.fillStyle = grd;
      this.ctx.fillRect(...size);
      res();
    });
  }
  //处理传入数据
  doData(data) {
    for (let k in data) {
      if (typeof data[k] != 'number' || k == 'maxLetter') {
        continue
      }
      if (k % 2 == 0) {
        data[k] = this.cx(data[k]);
      } else {
        data[k] = this.cy(data[k]);
      }
    }
    return data
  }
  //画圆角路线为其他画图服务
  drawRoundedLine(width,
    height,
    x,
    y,
    radius) {
    this.ctx.beginPath();
    this.ctx.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2);
    this.ctx.lineTo(width - radius + x, y);
    this.ctx.arc(
      width - radius + x,
      radius + y,
      radius,
      (Math.PI * 3) / 2,
      Math.PI * 2
    );
    this.ctx.lineTo(width + x, height + y - radius);
    this.ctx.arc(
      width - radius + x,
      height - radius + y,
      radius,
      0,
      (Math.PI * 1) / 2
    );
    this.ctx.lineTo(radius + x, height + y);
    this.ctx.arc(
      radius + x,
      height - radius + y,
      radius,
      (Math.PI * 1) / 2,
      Math.PI
    );
  }
  //再次绘画
  anginDraw(val) {
    this.id = val.id;
    this.data = this.deepClone(val.data);
    this.canvas = '';
    this.cxt = '';
    this.dpr = '';
    this.begin();
    this.x = '';
    this.y = '';
    this.notReady = true;
    this.readyTime = 0;
    //两种解决重新绘画会造成上一个绘画任务继续在跑的问题
    // 1.判断状态让用户点过 //需要判断上次继续绘画操作
    // if (this.onDraw) {
    //   wx.showToast({
    //     title: '正在渲染请重试',
    //     icon: 'none'
    //   })
    // } else {
    //   this.drawing();
    // }
    // 2.判断状态  停止当前渲染动作  //需要判断上次继续绘画操作
    // this.over = false; //强行停止当前渲染
    if (this.onDraw) {
      //  停止当前渲染
      this.over = true;
      // console.log('正在停止渲染')
    } else {
      this.drawing();
    }
  }
  deepClone(obj) {
    if (typeof obj != 'object') return obj;
    return JSON.parse(JSON.stringify(obj))
  }
  //储存为图片
  saveImg(fn) {
    wx.canvasToTempFilePath({
      x: 0,
      y: 0,
      width: 300,
      height: 500,
      canvas: this.canvas,
      canvasId: this.id,
      success: function (res) {
        // console.log(res.tempFilePath, "seerererwr");
        // that.shareImg = res.tempFilePath;
        // wx.setStorageSync("shareImg", res.tempFilePath);
        if (fn) {
          fn(res, 1)
        }
        if (this.needSaveImg) {
          wx.saveImageToPhotosAlbum({
            filePath: res.tempFilePath, //canvasToTempFilePath返回的tempFilePath
            success: (ress) => {
              // console.log(res, "seerererwr");
              if (fn) {
                fn(ress, 2)
              }
            },
            fail: (err) => {
              if (fn) {
                fn(err, 3)
              }
            },
          });
        }
      },
    });
  }
}

export default canvasDr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

轻动琴弦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值