HT for Web 矢量手册

索引


概述

矢量

矢量HT for Web中是矢量图形的简称,常见的pngjpg这类的栅格位图, 通过存储每个像素的颜色信息来描述图形,这种方式的图片在拉伸放大或缩小时会出现图形模糊,线条变粗出现锯齿等问题。 而矢量图片通过点、线和多边形来描述图形,因此在无限放大和缩小图片的情况下依然能保持一致的精确度。

用途

HT for Web中所有能用栅格位图的地方都可用矢量图形替代,例如GraphView组件上的图元图片,TreeViewTableView上的图标等, 甚至整个HT框架做出来的系统界面可以实现全矢量化,这样GraphView组件上的图元缩放都不会失真,并且不再需要为Retina显示屏提供不同尺寸的图片, 在devicePixelRatio多样化的移动时代, 要实现完美的跨平台,矢量可能是的最低成本的解决方案。

特点


格式

整体属性

矢量采用JSON格式描述,使用方式和普通的栅格位图一致,通过ht.Default.setImage('hightopo', jsonObject)进行注册, 使用是将相应图片注册名设置到数据模型即可,如node.setImage('hightopo')node.setIcon('hightopo')等。

矢量json描述必需包含widthheightcomps参数信息:

同时可设置以下可选参数信息:

以下例子定义了一个名为sunrise的矢量图形,宽度220,高度150comps定义了三个type: shape类型组件。

ht.Default.setImage('sunrise', {
    width: 220,
    height: 150,
    comps: [
        {
            type: 'shape',
            points: [10, 110, 10, 10, 210, 10, 210, 110],
            segments: [1, 4],
            background: 'yellow',
            gradient: 'linear.north'
        },
        {
            type: 'shape',
            shadow: true,
            points: [30, 10, 30, 110, 30, 60, 90, 60, 90, 10,
                90, 110, 130, 10, 190, 10, 160, 10, 160, 110
            ],
            segments: [
                1, 2, 1, 2, 1, 2, 1, 2, 1, 2
            ],
            borderWidth: 10,
            borderColor: '#1ABC9C',
            borderCap: 'round'
        },
        {
            type: 'shape',
            points: [10, 130, 35, 120, 60, 130, 85, 140,
                110, 130, 135, 120, 160, 130, 185, 140, 210, 130
            ],
            segments: [
                1, 3, 3, 3, 3
            ],
            borderWidth: 2,
            borderColor: '#3498DB'
        }
    ]
});

var node = new ht.Node();
node.setPosition(160, 110);
node.setImage('sunrise');
dataModel.add(node);

以下的代码片段展示的是嵌套矢量图形的用法,在定义group-sunrise矢量时,通过type: 'image'的图片类型, 指向了name: 'sunrise'的以定义矢量图形,该例子分别定义了四个嵌套的sunrise矢量,设置了clip: true, 这样右上角的sunrise矢量虽然旋转后超出了宽高设置的区域,但超出的绘制内容会被裁剪掉。

ht.Default.setImage('group-sunrise', {
    width: 240,
    height: 160,
    clip: true,
    color: 'red',
    comps: [
        {
            type: 'image',
            name: 'sunrise',
            rect: [0, 0, 120, 80],
            opacity: 0.3
        },
        {
            type: 'image',
            name: 'sunrise',
            rect: [120, 0, 120, 80],
            rotation: Math.PI / 4
        },
        {
            type: 'image',
            name: 'sunrise',
            rect: [0, 80, 120, 80],
            shadow: true
        },
        {
            type: 'image',
            name: 'sunrise',
            rect: [120, 80, 120, 80]
        }
    ]
});

示例定义了一个cloud的多边形云图效果,接下来定义的cloud-rectcloud-oval分别嵌套复用了cloud的云图。 采用了cliptrue进行矩形裁剪解决了文字内容超出宽高区域的问题,通过自定义clip函数达到裁剪出圆形效果。

ht.Default.setImage('cloud-oval', {
    width: 300,
    height: 300,
    clip: function(g, width, height, data) {
        g.beginPath();
        g.arc(width / 2, height / 2, Math.min(width, height) * 0.42, 0, Math.PI * 2, true);
        g.clip();
    },
    comps: [
        {
            type: 'rect',                            
            rect: [0, 0, 300, 300],
            background: '#3498DB'
        },                
        {
            type: 'image',
            name: 'cloud',
            rect: [0, 0, 300, 300]
        },                         
        {
            type: 'text',
            text: new Date(),
            rect: [0, 120, 300, 100],
            color: '#34495E',
            font: 'bold 18px Arial',
            align: 'center'
        }
    ]
}); 

组件属性

组件的属性一般根据组件类型的不同而设置,而阴影、透明度和旋转等属性是所有类型都具备可设置的参数。

以下例子分别对云图矢量设置了透明度0.5,旋转了Math.PI/4弧度,设置了阴影,以及将三种参数组合的效果。

ht.Default.setImage('cloud-all', {
    width: 300,
    height: 300,
    comps: [
        {
            type: 'shape',
            points: points,
            segments: segments,
            background: '#d6f0fd',
            gradientColor: '#A6f0fd',
            gradient: 'linear.north',
            opacity: 0.5,
            rotation: Math.PI/4,
            shadow: true,
            shadowColor: '#E74C3C',
            shadowBlur: 12,
            shadowOffsetX: 6,
            shadowOffsetY: 6
        }                       
    ]
}); 

除了shape类型外其他类型组件都需要指定rect参数,因为shape自身通过pointssegments信息已经能确定自身组件位置, 但shape类型依然可以设置rect参数,相当于将shape压缩或拉伸到填充入指定的rect区域,以下例子中定义的cloud矢量, 为两个shape构成,第一个shape未指定rect,第二个shape指定的rect将云缩小定位到中心区域,其rect设置时采用了 [17, 0.3, 0.3]的三参数方式,其中17代表中心position位置, 0.3代表宽度为矢量宽和高的0.3倍大小。

组件旋转和缩放的中心点是由锚点anchorXanchorY决定的,与节点锚点不同的是,这里组件绘制区域是由rect决定,锚点不会影响最终的绘制位置。

组件类型

基本类型

矢量的基本类型与style的shape参数是完全一一对应, 只是将style中的名称改成骆驼式命名法去掉了.分隔符。

基本类型:

参数属性:

多边形

shape类型,基本类型的参数也都适用于多边形,多边形通过pointsArray数组指定每个点信息, points[x1, y1, x2, y2, x3, y3, ...]的方式存储点坐标。曲线的多边形可通过segmentsArray数组来描述, segment[1, 2, 1, 3 ...]的方式描述每个线段:

对比闭合多边形除了设置segmennts参数外,还可以设置closePath属性: * closePath获取和设置多边形是否闭合,默认为false,对闭合直线采用这种方式,无需设置segments参数

ht.Default.setImage('shape', {
    width: 100,
    height: 50,
    comps: [
        {
            type: 'shape',
            borderWidth: 2,
            borderColor: '#34495E',
            background: '#40ACFF',
            gradient: 'spread.vertical',
            gradientColor: 'white',
            points: [5, 25, 70, 25, 70, 5, 95, 25, 70, 45],
            segments: [
                1, // moveTo [5, 25]
                2, // lineTo [70, 25]
                1, // moveTo [70, 5]
                2, // lineTo [95, 25]
                2, // lineTo [70, 45]
                5] // closePath to [70, 5]
        }                          
    ]
});                 

边框

border边框类似,用于绘制指定矩形的内边框,该类型的绘制区域不会超出矩形边界

color边框颜色 width边框宽度

文本

text文本类型,用于呈现数值或名称等描述信息。

图片

image图片类型有两种用处,一是可引入传统的栅格位图,达到矢量和传统图片的融合, 二时通过image类型嵌入已定义的矢量,形成无限嵌套效果和可复用的功能, 图片注册详见入门手册

饼图

饼图类型为pieChart

柱状图

柱状图类型为columnChart

以下例子为serie只有一组数据的情况,这种情况下一般会设置colors达到不同column不同颜色。

以下例子为serie有多组数据的情况,这种情况下一般会设置color而不设置colors,以达到不同系列不同颜色效果。

堆栈柱状图

堆栈柱状图类型为stackedColumnChart

百分比柱状图

百分比柱状图类型为percentageColumnChart

曲线图

曲线图类型为lineChart

自定义

除了HT预定义的组件类型外,用户还可以自定义扩展类型,自定义有两种方式:

先注册ht.Default.setCompType的方式有利于数据模型存储序列化,同时也有利于复用。

以下示例自定义了一个时钟,该时钟的矢量由三部分组成:

ht.Default.setCompType('clock-face', function(g, rect, comp, data, view) {
    var cx = rect.x + rect.width / 2;
    var cy = rect.y + rect.height / 2;
    var theta = 0;
    var r = Math.min(rect.width, rect.height)/2 * 0.92;

    g.strokeStyle = "#137";
    for (var i = 0; i < 60; i++) {                        
        g.beginPath();
        g.arc(
            cx + Math.cos(theta) * r, 
            cy + Math.sin(theta) * r, 
            i % 5 === 0 ? 4 : 1, 
            0, Math.PI * 2, true);
        g.closePath();
        g.lineWidth = i % 5 === 0 ? 2 : 1;
        g.stroke();
        theta = theta + (6 * Math.PI / 180);
    }
});

SVGPath

SVGPath类型,基本类型的参数也都适用于SVG路径,通过path指定符合SVG规范的路径信息, path的格式请参考这里 SVGPath类型在绘制时要进行大量的解析工作,所以应该避免大量使用这种类型,尤其是在对性能敏感的场景中。

状态

HT矢量中,如果使用了状态机制,每个组件都可以定义状态state来决定自己在哪个状态下显示,节点状态由data.s('state')决定,data.s('state')为空时,会从整体属性上去获取默认状态值。可以重载gv.getState自定义逻辑。如果组件不设置state属性,则显示隐藏不会受到这一机制的影响,组件状态是一对多的,不同组件可以使用同一个状态值。gv.getState默认逻辑如下

getState: function(data) {
    var state = data.s('state');
    if (state) return state;

    // 从图标上查询获取默认值
    var img = getImage(data.getImage());
    if (!img) return null;
}

一个运用的例子

渲染元素

通过定义HT矢量JSONrenderHTML函数属性,可实现在GraphView拓扑图上,嵌入任意第三方HTML DOM元素。HT的图纸是Canvas实现,renderHTMLDOM一定在Canvas之上,使用renderHTMLDOM与常规Canvas上绘制的图元不可能有层级控制可能性。renderHTML 的基本结构如下:

/**
* renderHTML 在图元属性变化后会被调用到,注意这个函数里面不要再改变 data 属性,更不要在这里起动画逻辑,改变数据要在运行时的外面代码进行
* @param  {ht.Node} data [图元对象]
* @param  {ht.graph.GraphView} gv [当前拓扑图组件]
* @param  {object} cache [缓存对象,用于存在 html 元素和相关信息,避免重复构建]
* @return {HTMLElement} [要显示的 HTML 元素,或 HT 组件对象]
*/
renderHTML: function(data, gv, cache) {
    if (!cache.htmlView) {
        // 创建 HTML
    }
    // 数据驱动
    return html;
}

返回的HTML元素上有以下相关约定:

另外为了方便HTML元素根据图元信息布局的函数,ht.graph.GraphView上增加了layoutHTML方法

/**
* @param  {ht.Node} data 参考图元对象
* @param  {HTMLElement} html 要布局的 HTML 元素,可传入 HT 组件对象
* @param  {boolean} bound 默认为 false,HTML 元素完全重叠甚至被旋转图元,为 true 则根据图元矩形区域摆放
*
*/
graphView.layoutHTML(data, html, bound)

注意bound为默认值false时,图元是通过CSStransform设置,实现和图元对象的完全重合,这种情况下拓扑图缩放时,HTML元素会出现不清晰的问题。如果采用true的布局方式,则图元不设置transform参数,而通过设置top/left/width/height进行布局,这种方式HTML元素会保持清晰,但不具备旋转功能的效果

矢量缓存

Canvas绘制中,每帧绘制次数是影响2D性能的一大因素之一,如果一个矢量有100个基本图形组件,不考虑矢量嵌套的情况下,绘制时相当于这个矢量需要进行100次绘制,然而目前主流的设备中,每帧大于3000次绘制就会感觉性能有所下降。HT矢量中提供了一套缓存机制,可以根据一定逻辑将绘制结果保存在内存中,每次绘制时去找内存中缓存的图形信息,这样就只要进行一次绘制,可以大大提升了绘制性能,特别适用于有复杂矢量,同一个矢量被大量复用的2D拓扑图中。

矢量cacheRule属性,默认为false,即不开启缓存。可以取值:

注意:矢量缓存适用于一个矢量只有一种绘制结果,或者只有几种可枚举的绘制结果,建议在10种以内使用缓存功能。使用缓存后矢量在某些缩放比例下会有轻微的失真,所以缓存一般作为后期性能优化手段。另外,ht.Default上也增加了setImageCacheRule方法,可以让用户在代码中全局注册缓存的逻辑,这个方法更适用于项目已经完成要做后期优化,这样可以不用再去修改矢量内容,复杂的图纸运用,而不会因为定义在矢量上全局受影响,使用示例

/**
 * key 可以是注册的矢量名或者矢量 JSON 地址,取值与 cacheRule 属性取值一致
 */
ht.Default.setImageCacheRule({
    'symbols/test.json': true,
    'symbols/test2.json': function(data, view) {
        return data.a('status');
    },
});
// 也可以是两个参数的形式
ht.Default.setImageCacheRule('symbols/test3.json', true);

可平移或缩放以下两个拓扑图对比缓存效果,左侧是未做缓存,右侧是加了缓存的。

事件处理

整体属性、组件属性中,都可以定义以下几种事件处理,除上下文菜单、悬停、滚动外,其它都可兼容touch 事件。注意:交互是需要额外性能开销,要让矢量内容可以交互,需在矢量整体属性设置 interactive: true,或者对图元设置data.s('interactive', true)

所有事件对应参数列表如下:

数据绑定

详见数据绑定手册


欢迎交流 service@hightopo.com