【仅供内部供应商使用,不提供对外解答和培训】

对比对象

  1. BI.CollectionView

  2. CollectionView的SVG实现

  3. CollectionView的Canvas实现 (大画布)

  4. CollectionView的Canvas实现 (小画布)

     

实现差异

  1. BI.CollectionView
    1. 每个cell用单独的div做框架,可向内填充任意组件 (label,button等)
  2. CollectionView的SVG实现
    1. 每个collection用一个svg实现,其中各cell的边框用path绘制,文字用text绘制
  3. CollectionView的Canvas实现 (大画布)
    1. 每个collection用一个canvas画布实现,画布大小为其中包含的所有cell所占空间大小 (包括不可见区域)
    2. 每个cell边框和背景用BI.Canvas的solid方法绘制,文字和字体样式用自己添加的text和setFont等方法绘制
  4. CollectionView的Canvas实现 (小画布)
    1. 除画布大小缩小为实际可视范围大小外,其余逻辑与3同

性能对比

第一轮比较

  1. BI.CollectionView
    1. 表格规格:22列,62行; 列宽80px,行高15px =====> 打开速度~1-2s, 滚动帧率1-3fps 
    2. 表格规格:27列,92行; 列宽60px,行高10px =====> 打开速度~3s,滚动帧率~1fps
    3. 表格规格:41列,92行; 列宽40px,行高7px   =====> 打开速度~5s,滚动帧率~0fps
  2. CollectionView的SVG实现
    1. 表格规格:22列,62行; 列宽80px,行高15px =====> 打开速度~3s, 滚动帧率1-2fps
    2. 表格规格:27列,92行; 列宽60px,行高10px =====> 打开速度~5s, 滚动帧率0-2fps
    3. 表格规格:41列,92行; 列宽40px,行高7px   =====> 打开速度~8s, 滚动帧率~0fps
  3. CollectionView的Canvas实现 (大画布)
    1. 表格规格:22列,62行; 列宽80px,行高15px =====> 打开速度<1s, 滚动帧率30-50fps
    2. 表格规格:27列,92行; 列宽60px,行高10px =====> 打开速度<1s, 滚动帧率~25fps
    3. 表格规格:41列,92行; 列宽40px,行高7px   =====> 打开速度<1s, 滚动帧率6-20fps
  4. CollectionView的Canvas实现 (小画布)
    1. 表格规格:22列,62行; 列宽80px,行高15px =====> 打开速度<1s, 滚动帧率7-10fps
    2. 表格规格:27列,92行; 列宽60px,行高10px =====> 打开速度<1s, 滚动帧率4-6fps
    3. 表格规格:41列,92行; 列宽40px,行高7px   =====> 打开速度<1s, 滚动帧率2fps

以上比较从性能上看,在展示大量表格时,两种Canvas实现的表格性能明显好于其他实现方法

第二轮比较

第二轮比较只比较不同画布大小的优劣,在比较之前先简述一下高清晰度canvas的绘制原理,参考链接:High DPI Canvas

首先介绍devicePixelRatio,其值等于物理像素 / 设备独立像素, 一般调整显示屏和浏览器的缩放大小时会改变这个值,e.g. 一个在css中设定高2px的text,其在物理像素上的大小应该是2px * devicePixelRatio;

---------11月8日更新---------

由于上文(High DPI Canvas)年代久远,其中提到的BackingStorePixelRatio属性已被弃用,更新后创建Canva的代码如下:

HiDPICanvas
_createHiDPICanvas: function (w, h, ratio) {
        if (!ratio) {
            ratio = window.devicePixelRatio || 
                  window.screen.deviceXDPI / window.screen.logicalXDPI || 1;
        }
        var canvas = document.createElement("canvas");
        if (!document.createElement('canvas').getContext) {
            canvas = window.G_vmlCanvasManager.initElement(canvas);
        }
        canvas.width = w * ratio;
        canvas.height = h * ratio;
        canvas.style.width = w + "px";
        canvas.style.height = h + "px";
        canvas.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0);
        return canvas;
    },

 

其次介绍backingStorePixelRatio,这个值告诉我们在特定的浏览器下canvas的backing store大小与canvas元素本身大小的比例,当我们向canvas的context中进行绘制时,浏览器会将其写入canvas的underlying storage中,这个underlying storage就叫backing store,当浏览器绘制canvas时,其使用的就是backing store里的数据,但绘制出的实际大小是backing store 大小除以backingStorePixelRatio之后的大小,e.g. 对于backingStorePixelRatio为2的浏览器,其绘制一个大小为200px * 200px的canvas时,canvas的实际数据量应该是400px * 400px,如下图

 

于是当canvas最后被绘制到屏幕上,其实际上经过了以下的过程

 

可以看出总的pixelRatio = devicePixelRatio / backingStorePixelRatio,如果由于设备/浏览器缩放和浏览器自身属性的不同导致pixelRatio不等于1的话,则canvas会被强行放大或缩小,导致显示不清晰,为解决这个问题,应该在实际绘制canvas时,将canvas画布宽/高乘上pixelRatio,再将其CSS style属性中的宽/高设为想要显示的实际宽/高,如下:

那么问题就来了,对于用大画布实现的canvas table,canvas本身大小就是其包含的所有的cell(包括不可见的cell)的大小,在pixelRatio > 1的情况下,实际参与计算的canvas大小会大大增加,而且在滚动时,即时处于可视范围之外的canvas也会占用渲染时间,在数据量极大的情况下,整个渲染过程中painting时间会大大增加,如下图,表格规格:22列,62行; 列宽80px,行高15px,与第一轮测试中不同处仅为将pixelRatio从1增加至1.5,fps则从30-50降至3左右:

而对于小画布实现的canvas table,其在表格规格:22列,62行; 列宽80px,行高15px,pixelRatio从1增加到1.5的情况下,fps从7-10上升到16-20,如下图:

 

---------11月8日更新---------

改进的小画布Canvas实现GridView

在原版本的Canvas实现中,每次滚动/滑动都需要清空整个画布并对每个cell进行重绘,这么实现的原因是对Canvas画布的任何位置、形态、风格上的变化都只会影响到后面的绘制,也就是说一旦有图形绘制到了Canvas上,后续的任何translate,transform之类的方法都不会影响之前的绘制内容,所以想要实现动画效果就必须清除画布再重绘下一帧。而重绘每一帧都需要遍历每个可视区域的cell绘图,导致绘制速度较慢,而Canvas本身可以看作是一幅图片,如果可以把前一帧和后一帧重复出现的表格区域当作一整张图片直接绘制到相应位置,则可以提高绘制速度。这里需要用到drawImage方法:

CanvasRenderingContext2D.drawImage() 提供了三种绘制方式:

CanvasRenderingContext2D.drawImage()
void ctx.drawImage(image, dx, dy);
void ctx.drawImage(image, dx, dy, dWidth, dHeight);
void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

参数含义见图:

其中image可以是:CSSImageValue, HTMLImageElement, SVGImageElement, HTMLVideoElement, HTMLCanvasElement, ImageBitmap, OffscreenCanvas.

而Canvas元素自身也可当作image参数,但由于每次在绘制下一帧时需要清空上一帧的Canvas,所以传进来的Canvas会是空白的,为解决这个问题我用了两块重合的Canvas画布,每次绘制下一帧时先将上一帧的Canvas作为这一帧drawImage参数传进来绘制,绘制完后再将上一帧的Canvas清空,绘制过程如下图:

在此过程中需要注意,上文创建High DPI Canvas时给用setTransform方法给Canvas设置了横向和纵向的缩放比ratio,当ratio不为1时drawImage绘制的图像会被缩放,会导致越滚动/滑动图像越模糊,所以在调用drawImage之前需将横纵向缩放比重置为1,drawImage结束后恢复原来的缩放比。采用改进方法绘制的表格在小跨度滑动的情况下均可以保持每秒60帧左右的帧率,但在滑动跨度较大时,即上一帧和下一帧间几乎没有重复部分的情况时,性能表现相当于改进前(因为所有的cell都需要重绘)。

注:目前这种实现方法的表格在Chrome,FireFox,Opera和Edge下都能保持很不错的性能,但在IE(最多支持到9)下性能略有下降

  • No labels