21/5/27 canvas

小程序canvas总结

遇到的问题:

  1. 无法获取canvas实例
  2. canvas 接收的px要做成rpx
  3. 当把canvas当组件调用时,图片不显示
  4. canvas绘画线上图片失败
  5. canvas绘画文字不能换行
  6. 当把canvas当做组件调用时,在父组件会高度失效

解决:

1、无法获取canvas实例

小程序不能用document属性,wx.createOffscreenCanvas这个也已经废除了

微信上给的例子是这样的

Page({
  onReady() {
    const query = wx.createSelectorQuery()
    query.select('#myCanvas')
      .fields({ node: true, size: true })
      .exec((res) => {
        const canvas = res[0].node
        const ctx = canvas.getContext('2d')

        const dpr = wx.getSystemInfoSync().pixelRatio
        canvas.width = res[0].width * dpr
        canvas.height = res[0].height * dpr
        ctx.scale(dpr, dpr)

        ctx.fillRect(0, 0, 100, 100)
      })
  }
})

因为我用的uni的,从uni上找到了相似的方法,uni.createCanvasContext(canvasId, this),坑倒是没有主要是之前找方法的时候想用创建离线的canvas结果失败了。

2、rpx 转px

canvas只接受px传过去之前要换算一下,先用getSystemInfoSync.screenWidth获取设备宽度,然后根据宽度与设计稿的宽度算出比例,忘记时看的哪位的方法了,下次我见到会把连接放进来

 /** 获取设备信息 */
  // eslint-disable-next-line no-undef
  private system = uni.getSystemInfoSync();
  private screenWidth = this.system.screenWidth;

  private factor = this.screenWidth / 750; // 获取比例
  private topx(rpx: number) {
    return Math.round(rpx * this.factor);
  }

3、当把canvas当组件调用时,图片不显示

canvas 获取实例时,有两个参数,第一个是类名,第二个是代表现在的this要传。

  // eslint-disable-next-line @typescript-eslint/no-this-alias
    const that = this;
    // eslint-disable-next-line no-undef
    const ctx = uni.createCanvasContext('myCanvas', that);

4、线上地址绘画失败

从线上拿来的地址直接用,不显示图片,先用getImageInfo下载到本地,然后再用拿到的临时路径绘画

// 获取图片方法  
private getImgPath(src: any) {
    return new Promise((resolve, reject) => {
      // eslint-disable-next-line no-undef
      uni.getImageInfo({
        src: src,
        success: (res: any) => {
          resolve(res);
        },
        fail: (res: any) => {
          reject(res);
        },
      });
    });
  }

/** 把线上地址下载到本地才能绘画 */
      this.getImgPath(sliderImage[0]).then((resolve: any) => {
        this._img(resolve.path);
      });

5、绘画文字不能换行

canvas绘画文字没有warp这个属性,自己可以定义个方法让她逐字绘画,然后算宽度,超过就让绘画的高度加一点,模拟换行

  /**
   * ctx canvas 对象
   * str 文本
   * leftWidth 距离左侧的距离
   * initHeight 距离顶部的距离
   * titleHeight 文本的高度
   * canvasWidth 文本的宽度
   *
   */
  private drawText(
    ctx: any,
    str: string,
    leftWidth: any,
    initHeight: any,
    canvasWidth: any
  ) {
    let lineWidth = 0;
    for (let i = 0; i < str.length; i++) {
      ctx.setFontSize(this.topx(30));
      ctx.setFillStyle('#232323');
      ctx.fillText(str.substring(i, i + 1), leftWidth + lineWidth, initHeight); // 一个字一个字绘制
      lineWidth += ctx.measureText(str[i]).width;// 字体宽度累计相加
      
      // 如果宽度超过了canvas的宽度,就换行渲染
      if (leftWidth + lineWidth > canvasWidth) {
        initHeight += 20;
        lineWidth = 0;
      }
    }
  }

6、当把canvas当做组件调用时,在父组件会高度失效

当用组件调用canvas时,在父组件设置绘画的高度,不成功,最后我也没解决,把组件里面的代码都复制到了本页面,没有用组件。

总的代码

 /** 获取设备信息 */
  // eslint-disable-next-line no-undef
  private system = uni.getSystemInfoSync();
  private screenWidth = this.system.screenWidth;

  private factor = this.screenWidth / 750; // 获取比例
  private topx(rpx: number) {
    return Math.round(rpx * this.factor);
  }


  private _img(img: any) {
    /** 设置分享的标题,价格 */
    const { otPrice, price, title } = this.shopDetailsArr;

    // eslint-disable-next-line @typescript-eslint/no-this-alias
    const that = this;
    // eslint-disable-next-line no-undef
    const ctx = uni.createCanvasContext('myCanvas', that);
    /** 设置背景 */
    ctx.rect(0, 0, this.topx(600), this.topx(1000));
    ctx.setFillStyle('#fff');
    ctx.fill();

    /** 画图片 */
    ctx.drawImage(
      img,
      this.topx(20),
      this.topx(20),
      this.topx(560),
      this.topx(600)
    );
    /** 画文字 */
    /** ¥ */
    ctx.font = 'normal 20px STXingkai'; // 字体
    ctx.setFillStyle('#E53B64');
    ctx.setFontSize(this.topx(28));
    ctx.fillText('¥', this.topx(40), this.topx(690)); //文字内容、x坐标,y坐标
    /** 1299 */
    ctx.setFontSize(this.topx(56));
    ctx.fillText(price, this.topx(66), this.topx(690));
    ctx.setFontSize(this.topx(28));
    ctx.setFillStyle('#B0B8C9');
    ctx.fillText('¥', this.topx(200), this.topx(685)); //文字内容、x坐标,y坐标
    ctx.setFontSize(this.topx(28));
    ctx.setFillStyle('#B0B8C9');
    ctx.fillText(otPrice, this.topx(230), this.topx(685));
    /** 线 */
    ctx.beginPath();
    ctx.setLineWidth(0.5);
    ctx.moveTo(this.topx(200), this.topx(678));
    ctx.lineTo(
      ctx.measureText('¥ 1299').width + this.topx(200),
      this.topx(678)
    );
    ctx.stroke();

    /** title */
    ctx.setFontSize(this.topx(30));
    ctx.setFillStyle('#242C42');
    this.drawText(ctx, title, this.topx(40), this.topx(740), this.topx(560));

    /** 线 */
    // ctx.beginPath();
    // ctx.setLineWidth(this.topx(1));
    // ctx.moveTo(this.topx(20), this.topx(710));
    // ctx.lineTo(this.topx(20), this.topx(980));
    // ctx.lineTo(this.topx(980), this.topx(780));
    // ctx.lineTo(this.topx(980), this.topx(700));
    // ctx.stroke();
    // ctx.lineTo(
    //   ctx.measureText('¥ 1299').width + this.topx(200),
    //   this.topx(630)
    // );

    /** 二维码 */
    ctx.drawImage(
      this.ar,
      this.topx(240),
      this.topx(820),
      this.topx(112),
      this.topx(112)
    );
    ctx.restore();

    /** 长按识别 */
    ctx.setFillStyle('#242C42');
    ctx.setFontSize(10);
    ctx.globalAlpha = 0.5;
    ctx.fillText('长按识别,查看商品', this.topx(210), this.topx(960));

    ctx.draw(false, () => {
      // eslint-disable-next-line no-undef
      uni.canvasToTempFilePath(
        {
          x: 0,
          y: 0,
          width: this.topx(600),
          height: this.topx(1000),
          destWidth: this.topx(600) * this.system.pixelRatio,
          destHeight: this.topx(1000) * this.system.pixelRatio,
          fileType: 'png',
          canvasId: 'myCanvas',
          quality: 1,
          success: (res: any) => {
            this.getImgPath(res.tempFilePath).then((resolve: any) => {
              this.temporaryPath = resolve.path;
            });
          },
        },
        that
      );
    });
  }
  /** 获取图片临时路径 */
  private temporaryPath = '';
  private getImgPath(src: any) {
    return new Promise((resolve, reject) => {
      // eslint-disable-next-line no-undef
      uni.getImageInfo({
        src: src,
        success: (res: any) => {
          resolve(res);
        },
        fail: (res: any) => {
          reject(res);
        },
      });
    });
  }
  /**
   * ctx canvas 对象
   * str 文本
   * leftWidth 距离左侧的距离
   * initHeight 距离顶部的距离
   * titleHeight 文本的高度
   * canvasWidth 文本的宽度
   *
   */
  private drawText(
    ctx: any,
    str: string,
    leftWidth: any,
    initHeight: any,
    canvasWidth: any
  ) {
    let lineWidth = 0;
    for (let i = 0; i < str.length; i++) {
      ctx.setFontSize(this.topx(30));
      ctx.setFillStyle('#232323');
      ctx.fillText(str.substring(i, i + 1), leftWidth + lineWidth, initHeight); // 一个字一个字绘制
      lineWidth += ctx.measureText(str[i]).width;

      if (leftWidth + lineWidth > canvasWidth) {
        initHeight += 20;
        lineWidth = 0;
      }
    }
  }

 

上一篇:js 对象与string之间的转换


下一篇:C++ std::any、std::variant和std::optional的原位构造(In-Place Construction)