json配置生成canvas海报

902 阅读3分钟

使用json配置生成canvas海报

tip: 参照掘金文章 juejin.cn/post/684490…

使用方法

   /** params: canvas挂载元素 宽 高 json配置 边界是否需要画  */
  
  const poster = new Poster('demo', 300, 500, this.config, true);
  poster.init();
  poster.listen('progressChange', () => {
    console.log(poster.progress); //海报加载进度
  });

效果图展示

demo.gif


不带边界和带边界

支持文字换行参照coolzjy@v2ex 提供的正则 regexr.com/4f12l ,文字,背景渐变,css布局,进度监听,边界绘制

class Poster {
  constructor(...args) {
     this.parentNode = document.getElementById(args[0]);
     this.width = args[1] || 0;
     this.height = args[2] || 0;
     this.canvas = this.initCanvasContext(this.width, this.height);
     this.defaultConfig = this.setDefault(args[3] || {});
     this.inlineBlockConfig = this.setInlineBlock(this.defaultConfig, 1);
     this.widthConfig = this.setWidth(this.inlineBlockConfig);
     this.heightConfig = this.setHeight(this.widthConfig);
     this.originConfig = this.setOrigin(this.heightConfig);
     this.images = this.getImages(this.originConfig);
     this.progress = 0; //进度
     this.boxShow = args[4] || false; //是否需要画边界
     }
 }
  • 1.初始化canvas(initCanvasContext)

当我们初始化canvas的宽高时,可能出现模糊,所以我们需要获取设备像素比,让canvas画布扩大, 保证能够高清显示

initCanvasContext() {
     var ratio = devicePixelRatio / backingStoreRatio;
     canvas.width = width * ratio;
     canvas.height = height * ratio;
}
  • 2.设置一些基础默认值(setDefault)

我们需要对json配置对象进行一些填补,比如默认颜色,行高,padding等,这个就看个人意愿了

setDefault(config) {
  //do something...
  return config
}
  • 3.改变布局排列(setInlineBlock)

会将连续排列的 inline-block 节点聚合新建一个空白的 div 插入原先的位置然后将这些 inline-block 节点作为 children 插入其中.

setInlineBlock(config) {
  config.children.forEach(item => {
    //do somgthing...
     setInlineBlock(item)
  })
  return config
}

  • 4.再次计算盒模型 width(setWidth),会结合 margin,border 等 css 属性再次计算各种盒模型宽。
setWidth(config) {
  //文字可能没有宽 ,图片一定要有宽高, 宽度有最大值和继承父的宽度限定边界
  //div含有字节点继承父节点的宽度
  // do something
  return config
}
  • 5.计算盒模型height(setHeight),计算所有节点的高 同宽度 不继承父子节点的高度
setWidth(config) {
  //div含有字节点继承父节点的宽度 行内元素取行内元素里面的最大高度
  // do something
  return config
}
  • 6.setOrigin 计算所有节点的绘图位置x,
  setOrigin(config) {
    //需要利用上一个相邻节点的位置计算下一个的
    // do something
    return config
  }

当我们完成上述操作已经拿到了一个计算完成的json配置,接下来我们就开始绘制吧

  1. 因为加载图片需要时间,所以我们可以等图片加载完再开始绘制,体验更好,而且我们可以监听加载了几张图片,来计算progress进度
  async loadAllImages(images) {
    var that = this;
    let count = 0;
    for (var i = 0; i < images.length; i++) {
      const item = images[i];
      const img = await this.loadImage(item.url);
      count++;
      that.progress = count / images.length;
      item.img = img;
    }
    return images;
  }
  loadImage(url) {
    return new Promise(resolve => {
      const img = new Image();
      img.onload = () => {
        //这里可以使用进度更好体现一点
        setTimeout(() => {
          resolve(img);
        }, 0);
      };
      img.setAttribute('crossOrigin', 'Anonymous');
      img.src = url;
    });
  }
  /** 我们可以在这写一个监听进度的函数 */
  listen(type, callBack) {
    if (type == 'progressChange') {
      Object.defineProperty(this, 'progress', {
        configurable: true, //属性可配置
        set: function(v) {
          this._progress = v;
          console.log('progress发生了改变', v);
          callBack(v);
        },
        get: function() {
          return this._progress;
        },
      });
    }

2.加载图片完成我们可以调用封装的drawCanvas来进行绘制来,进行type区分执行不同的绘制

// 最终绘制方法
  drawCanvas(config) {
    if (config.type == 'text') {
      this.drawParagraph(config);
    }
    if (config.type == 'div') {
      config.custom(this.ctx, config);
    }
    if (config.type == 'image') {
      this.drawImage(config);
    }
    if (config.children) {
      config.children.forEach(item => {
        this.drawCanvas(item);
      });
    }
  }

至于具体这里面绘制的过程,大家可以看我代码,不过大家可以自行先看下canvas的绘制方法,ctx.save();ctx.restore()很关键哦

代码仓库地址放在 github.com/kukudedaxia…