欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Quark-Renderer--------第七篇

程序员文章站 2022-06-11 09:10:16
...

[email protected]

总述

通过前面几篇的学习,对于CanvasPainter.js文件中的一些比较get方法及其调用的方法进行了研究学习,而这次我们接着学习剩下的比较关键的方法内容。

方法学习

delLayer方法

该方法时删除层方法,与上次学习的创建createLayer()方法具有相反的作用。该方法只需要传入一个参数,该参数就是要删除的层。在该方法中你需要先判端该层画布实例是否存在,即layer.canvasInstance是否存在,然后再将该层画布实例的前几点移除子节点,然后再删除该层画布,否则父节点指向下一层的引用就会为null,这是不行的。最后qlevelList列表调用splice()函数,该函数的作用是从数组中删除元素,如有必要,在其位置插入新元素,并返回已删除的元素。其中,indexOf函数的作用是查询数组中元素的index,便于操作,将indexOf的返回值作为splice() 函数传入的参数。对于delLayer()方法的关键代码如下段代码所示。

if (layer.canvasInstance) {
      layer.canvasInstance.parentNode.removeChild(layer.canvasInstance);
    }
delete layers[qlevel];

qlevelList.splice(dataUtil.indexOf(qlevelList, qlevel), 1);

eachLayer

该函数的作用是,遍历所有画布层,对于画布的绘制等具有重要作用。主要的就是一个for循环,得到qlevelList的长度,然后调用call方法,该方法是调用对象的方法,用另一个对象替换当前对象。以下是eachLayer()方法体的关键代码。

for (i = 0; i < qlevelList.length; i++) {
      z = qlevelList[i];
      cb.call(context, this._layers[z], z);
    }

resize方法

该方法的主要作用就是修改该层的宽高大小。重设画布的尺寸,当外部容器的尺寸发生了变化时需要调用此方法来重新设置画布的大小。首先对该对象的host中的style属性进行判断是否存在,如果不存在,再判断传入的width、height是否weinull,如果是,则直接返回;如果不是,则修改设置该对象_width、_height属性,并且得到该层画布,然后调用自身方法,传入相同的参数。如果该对象的_host下的style属性存在,则将_host的style下的displayer设置为none,接着保存输入的两个参数。
接着判断_height和_width是否相同,如果没有不相同,则优化没有被修改的resize,将host的style下的长宽都进行设置,然后循环遍历所有层,调用每层的hasOwnProperty方法,确定当前层是否具有具有指定名称的属性,如果有,则对该层调用resize方法,并将调用refresh()方法,该方法是是否强制绘制所有元素,设置为true,则进行全面的修改绘制。接着遍历每一层进行resize()方法,重新绘制每一层画布,修改他们的大小尺寸,最后返回该层画布。

  resize(width, height) {
    if (!this._host.style) {
      // Maybe in node or worker or Wechat
      if (width == null || height == null) {
        return;
      }
      this._width = width;
      this._height = height;
      this.getLayer(CANVAS_QLEVEL).resize(width, height);
    } else {
      let domRoot = this._host;
      domRoot.style.display = 'none';

      // Save input w/h
      let options = this.options;
      width != null && (options.width = width);
      height != null && (options.height = height);

      width = this._getSize(0);
      height = this._getSize(1);

      domRoot.style.display = '';

      // 优化没有实际改变的resize
      if (this._width !== width || height !== this._height) {
        domRoot.style.width = width + 'px';
        domRoot.style.height = height + 'px';

        for (let id in this._layers) {
          if (this._layers.hasOwnProperty(id)) {
            this._layers[id].resize(width, height);
          }
        }
        dataUtil.each(this._progressiveLayers, function (layer) {
          layer.resize(width, height);
        });

        this.refresh(true);
      }

      this._width = width;
      this._height = height;
    }
    return this;
  }

getRenderedCanvas方法

该方法的主要作用就是获取已渲染所有内容的画布。但是对于微信小程序,不要使用此方法,因为我们无法获取HtmlElement或canvas实例。在该方法中,首先要进行创建imageLayer新的canvas层,然后初始化,调用clear()方法清空画布,然后进行判断使用options.pixelRatio与该对象的dpr进行比较,如果小于dpr,则进行重绘,然后遍历所有层,使用eachLayer()进行遍历所有层,然后判断__builtin__是否存在,然后进行绘制图像,调用drawImage函数,绘制canvas实例图层。如果不存在,然后判断该层的renderToCanvas是否存在,若存在,则进行保存,同时渲染该层画布。接着一开始的判断如果大于dpr,则进行遍历获取显示队列,并且包含 ignore 的数组。然后遍历绘制图层元素,最后返回该canvasInstance实例。该方法主要是增量渲染、echarts-gl等方式进行绘制图层。

let imageLayer = new CanvasLayer('image', this._width, this._height, options.pixelRatio || this.dpr);
    imageLayer.initContext();
    imageLayer.clear(false, options.backgroundColor || this._backgroundColor);

    if (options.pixelRatio <= this.dpr) {
      this.refresh();
      let width = imageLayer.canvasInstance.width;
      let height = imageLayer.canvasInstance.height;
      let ctx = imageLayer.ctx;
      this.eachLayer(function (layer) {
        if (layer.__builtin__) {
          ctx.drawImage(layer.canvasInstance, 0, 0, width, height);
        } else if (layer.renderToCanvas) {
          imageLayer.ctx.save();
          layer.renderToCanvas(imageLayer.ctx);
          imageLayer.ctx.restore();
        }
      });
    } else {
      // PENDING, echarts-gl and incremental rendering.
      let scope = {};
      let displayList = this.storage.getDisplayQueue(true);
      for (let i = 0; i < displayList.length; i++) {
        let el = displayList[i];
        this._doPaintEl(el, imageLayer, true, scope);
      }
    }
    return imageLayer.canvasInstance;

总结

本次学习了delLayer方法、eachLayer方法、resize方法、getRenderedCanvas方法等,通过本次学习,对画布的一些修改等有了更深刻的理解,再层级调用上有了认知,虽然复杂,但是道理简单。