Loading...
DisplayObject 是所有图形的基类,例如 Group Circle Text 等都会继承它。
我们尝试让它尽可能兼容 DOM Element,除了能降低学习成本,还能将自身伪装成 DOM Element 来充分利用已有的 Web 生态,例如:
https://developer.mozilla.org/en-US/docs/Web/API/Element/id
全局唯一的标识,可通过 getElementById 查询。
const circle = new Circle({id: 'my-circle-id',style: {r: 10,},});circle.id; // 'my-circle-id'canvas.getElementById('my-circle-id'); // circle
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByName
图形名称,不要求全局唯一,可通过 getElementsByName 查询。
const circle = new Circle({name: 'my-circle-name',style: {r: 10,},});circle.name; // 'my-circle-name'canvas.getElementsByName('my-circle-name'); // [circle]
https://developer.mozilla.org/en-US/docs/Web/API/Element/className
图形拥有的类名,可通过它获取/设置图形的类名。后续可以使用 getElementsByClassName 查询。
const circle = new Circle({className: 'my-circle-classname',style: {r: 10,},});circle.className; // 'my-circle-classname'canvas.getElementsByClassName('my-circle-classname'); // [circle]
可以使用空格隔开多个类名,随后使用 classList 只读属性获取类名列表:
circle.className = 'c1 c2';circle.classList; // ['c1', 'c2']
未指定类名将返回空字符串:
const group = new Group();group.className; // ''
最后在设置时还可以使用 class
作为别名:
const group = new Group({class: 'my-classname',// className: 'my-classname'});group.setAttribute('class', 'my-classname');// 但不可以使用 class 属性,为保留字group.class;
是否支持响应事件,默认为 true
。在某些不需要支持交互的图形上可以关闭。
例如我们不想让下面这个圆响应鼠标 mouseenter/leave
事件,示例
// 初始化时禁止交互const circle = new Circle({interactive: false,style: {r: 100,},});// 或者后续禁止circle.interactive = false;
推荐使用 pointerEvents 属性,因此上面禁止交互的操作等同于:
circle.style.pointerEvents = 'none';
绘图属性通过 style
设置,通常包含了填充色、透明度等通用属性,不同类型的图形也有自己的额外属性,例如在下面的圆角矩形中,填充色 fill
、描边色 stroke
就是通用属性,而矩形的左上角顶点位置(x, y)
、尺寸 width/height
和圆角半径 radius
则是额外属性:
const rect = new Rect({style: {// 或者使用 attrsx: 200,y: 100,fill: '#1890FF',stroke: '#F04864',lineWidth: 4,width: 300,height: 200,radius: 8,},});
属性名也可以使用连字符形式,因此以下写法完全等同,完整用法详见获取/设置属性值:
const rect = new Rect({'line-width': 4,// lineWidth: 4,});rect.style.lineWidth = 4;rect.style['line-width'] = 4;rect.style.setProperty('lineWidth', 4);rect.style.setProperty('line-width', 4);
图形在局部坐标系下的初始位置,根据图形种类使用不同属性描述,后续也可以通过 setLocalPosition 重新设置。
我们提供了在局部坐标系下进行变换的快捷方式,同时与 CSS Transform 保持一致,支持以下transform-function 变换函数:
由于是在局部坐标系下进行变换,因此以下写法在视觉效果上一致:
// 使用 transform 属性const circle = new Circle({style: {transform: 'translate(100px, 100px)',r: 100,},});// 直接设置 cx/cyconst circle = new Circle({style: {cx: 100,cy: 100,r: 100,},});// 使用变换方法const circle = new Circle({style: {r: 100,},});circle.translateLocal(100, 100);
旋转与缩放中心,也称作变换中心,相对于 Bounds 定义。
和 CSS transform-origin 类似,支持以下字符串写法,其中用空格分隔:
因此以下写法等价:
// r = 100circle.style.transformOrigin = 'left';circle.style.transformOrigin = 'left center'; // 包围盒水平方向左侧边缘,垂直方向中点circle.style.transformOrigin = '0 50%'; // 包围盒水平方向左侧边缘距离为 0,垂直方向距离顶部 50% 高度circle.style.transformOrigin = '0 100px'; // 包围盒水平方向左侧边缘距离为 0,垂直方向距离顶部 100px
⚠️ 暂不支持三个值的写法。
不同图形的默认值也不同:
图形整体透明度,取值范围为 [0, 1]
,支持 number
与 string
两种类型,因此以下两种写法等价:
circle.style.opacity = 0.5;circle.style.opacity = '0.5';
填充色透明度,取值范围为 [0, 1]
,支持 number
与 string
两种类型,因此以下两种写法等价:
circle.style.fillOpacity = 0.5;circle.style.fillOpacity = '0.5';
填充色,支持 string
类型,详见 <paint>:
circle.style.fill = 'red';circle.style.fill = 'rgb(255, 0, 0)';
该属性定义了用来确定一个多边形内部区域的算法,支持以下取值:
'nonzero'
默认值 https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute/fill-rule#nonzero'evenodd'
https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute/fill-rule#evenodd该 示例 依次展示了 'nonzero'
和 'evenodd'
的填充效果:
描边透明度,取值范围为 [0, 1]
,支持 number
与 string
两种类型,因此以下两种写法等价:
circle.style.strokeOpacity = 0.5;circle.style.strokeOpacity = '0.5';
描边色,支持 string
类型,详见 <paint>:
circle.style.stroke = 'red';circle.style.stroke = 'rgb(255, 0, 0)';
描边宽度。与我们熟悉的 CSS box model 不同,边框的一半宽度在图形内,一半在图形外。例如下面这个圆的包围盒宽度为:r + lineWidth / 2 = 110
支持 number
和 string
类型,前者默认为以 px
为单位的长度值,以下写法等价:
circle.style.lineWidth = 1;circle.style.lineWidth = '1';circle.style.lineWidth = '1px';
初始值 | 适用元素 | 是否可继承 | 是否支持动画 | 计算值 |
---|---|---|---|---|
'1' | 所有 | 是 | 是 | <percentage> <length> |
端点样式,支持以下取值:
https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/lineCap
连接处样式,支持以下取值:
https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/lineJoin
斜接面限制比例。SVG 和 Canvas2D 的默认值不同,前者为 4 而后者为 10。我们给 Path Polyline Polygon 这三种图形设置为 4,其余图形设置为 10。
https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/miterLimit
使用 number[]
描述交替绘制的线段和间距。可参考:https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/setLineDash
目前仅支持形如:[dash, gap]
的形式,如果数组中仅有一个元素,即 [dash]
等价于 [dash, dash]
。
对它应用动画可以实现笔迹动画效果。
虚线偏移量,number
类型,对它进行变换可以实现蚂蚁线动画
初始值 | 适用元素 | 是否可继承 | 是否支持动画 | 计算值 |
---|---|---|---|---|
'0' | 所有 | 是 | 是 | <percentage> <length> |
在图形底部增加阴影效果,支持配置阴影颜色,模糊半径和水平/垂直偏移距离。示例
阴影不会影响图形的 Geometry Bounds,例如下图中给一个半径为 100 的圆添加阴影后,几何包围盒尺寸不变:
circle.getBounds(); // { halfExtents: [100, 100] }circle.style.shadowBlur = 20;circle.getBounds(); // { halfExtents: [100, 100] }
当然外阴影会使 Render Bounds 增大,内阴影则不会。
最后,阴影会对渲染性能造成非常大影响。
目前我们支持两种阴影:
'outer'
外阴影,也是该属性的默认值。阴影出现在图形填充或者描边的外侧。'inner'
内阴影。顾名思义阴影在图形内部,如下图所示。阴影色,支持 string
类型,例如 '#1890FF'
。不支持渐变或者纹理写法。
阴影效果模糊程度,number
类型,不允许为负数。越大代表越模糊,为 0 时无模糊效果。
水平方向偏移量,支持 number
或 string
类型,例如负数让阴影往左移,正数向右
初始值 | 适用元素 | 是否可继承 | 是否支持动画 | 计算值 |
---|---|---|---|---|
无 | 所有 | 否 | 是 | <percentage> <length> |
垂直方向偏移量,例如负数让阴影往上移,正数向下
初始值 | 适用元素 | 是否可继承 | 是否支持动画 | 计算值 |
---|---|---|---|---|
无 | 所有 | 否 | 是 | <percentage> <length> |
滤镜(Filter)可以对已生成的图像进行一些处理,例如模糊、高亮、提升对比度等。在 Web 端有以下实现:
参考 CSS Filter 语法,我们支持对图形应用一个或多个滤镜效果,示例:
circle.style.filter = 'blur(5px)';circle.style.filter = 'blur(5px) brightness(0.4)'; // 可叠加
目前可以在 g-canvas/svg/webgl 渲染器中使用滤镜,有以下注意事项:
将高斯模糊应用于输入图像。其中 radius 定义了高斯函数的标准偏差值,或者屏幕上有多少像素相互融合,因此较大的值将产生更多的模糊,默认值为 0。该参数可以指定为 CSS 长度,但不接受百分比值。
和阴影一样,模糊同样不会影响图形的包围盒尺寸。
circle.style.filter = 'blur(5px)';
下图依次展示了 2px 4px 和 10px 的模糊效果,示例:
将线性乘法器应用于输入图像,让它变亮或变暗,默认值为 1。值为 0% 将创建全黑图像。值为 100% 会使输入保持不变。其他值是效果的线性乘数。如果值大于 100% 提供更明亮的结果。
circle.style.filter = 'brightness(2)';circle.style.filter = 'brightness(200%)';
下图依次展示了 0 100% 和 200% 的明亮效果,示例:
在图像下展示阴影,可以设置阴影颜色、偏移量与模糊效果,依次传入以下参数:
阴影不会影响图形的包围盒尺寸。
circle.style.filter = 'drop-shadow(16px 16px 10px black)';
下图依次展示了上面配置的效果,示例:
调节图像的对比度。当数值为 0% 时,图像会完全变黑。当数值为 100% 时,图像没有任何变化。
circle.style.filter = 'contrast(2)';circle.style.filter = 'contrast(200%)';
下图依次展示了 0 1 和 10 的对比度效果,示例:
将图像转换成灰色的图片。当值为 100% 时,图像会完全变成灰色。 当值为 0% 时,图像没有任何变化。
circle.style.filter = 'grayscale(1)';circle.style.filter = 'grayscale(100%)';
下图依次展示了 0 50% 和 100% 的灰度效果,示例:
对图像进行饱和度的处理。当值为 0% 时,图像完全不饱和。当值为 100% 时,图像没有任何变化。
circle.style.filter = 'saturate(1)';circle.style.filter = 'saturate(100%)';
下图依次展示了 0 50% 和 100% 的饱和度效果,示例:
对图像进行深褐色处理(怀旧风格)。当值为 100% 时,图像完全变成深褐色。当值为 0% 时,图像没有任何变化。
circle.style.filter = 'sepia(1)';circle.style.filter = 'sepia(100%)';
下图依次展示了 0 50% 和 100% 的处理效果,示例:
在输入图像上应用色相旋转,可设定图像会被调整的色环角度值。值为 0deg 时图像无变化。
circle.style.filter = 'hue-rotate(30deg)';circle.style.filter = 'hue-rotate(180deg)';
下图依次展示了 0 90deg 和 180deg 的处理效果,示例:
反转输入图像的颜色。amount 的值定义转换的比例,100% 代表完全反转,0% 则图像无变化。
circle.style.filter = 'invert(1)';circle.style.filter = 'invert(100%)';
下图依次展示了 0 50% 和 100% 的反转效果,示例:
类似 CSS 的 zIndex
属性,用于控制渲染次序,需要注意:
例如下面的场景图中,由于 li2 在 li1 之后加入画布,因此 li2 默认会展示在 li1 之上。如果希望改变这种展示次序,可以修改 li1 的 zIndex:
// ul1 -> li1// -> li2// ul2 -> li3li1.style.zIndex = 1; // li1 在 li2 之上
再比如尽管 li2 的 zIndex 比 ul2 大很多,但由于 ul1 比 ul2 小,它也只能处于 ul2 之下,示例
为了兼容旧版本,我们也提供了额外的、在上下文中设置的方法:
名称 | 参数 | 返回值 | 备注 |
---|---|---|---|
setZIndex | number | 无 | 设置 zIndex |
toFront | 无 | 无 | 置顶 |
toBack | 无 | 无 | 置底 |
const group = new Group();group.setZIndex(100);// or group.setAttribute('zIndex', 100);// or group.style.zIndex = 100;
控制图形的可见性,可参考:https://developer.mozilla.org/en-US/docs/Web/CSS/visibility
为了兼容旧版本,我们也提供了以下方法:
名称 | 参数 | 返回值 | 备注 |
---|---|---|---|
hide | 无 | 无 | 隐藏节点 |
show | 无 | 无 | 展示节点 |
因此以下写法等价:
const group = new Group();group.style.visibility = 'hidden';// or group.setAttribute('visibility', 'hidden');// or group.hide();group.style.visibility = 'visible';// or group.setAttribute('visibility', 'visible');// or group.show();
初始值 | 适用元素 | 是否可继承 | 是否支持动画 | 计算值 |
---|---|---|---|---|
'visible' | 所有 | 是 | 否 | <keywords> |
关于可见性有两点需要注意:
使用裁剪方式创建元素的可显示区域,区域内的部分显示,区域外的隐藏。可参考 CSS 的 clip-path。该属性值可以是任意图形,例如 Circle、Rect 等等。同一个裁剪区域可以被多个图形共享使用。最后,裁剪区域也会影响图形的拾取区域,示例。
例如我们想创建一个裁剪成圆形的图片,让裁剪区域刚好处于图片中心(尺寸为 200 * 200),此时我们可以设置裁剪区域圆形的世界坐标为 [100, 100]
。示例:
const image = new Image({style: {x: 0,y: 0,width: 200,height: 200,clipPath: new Circle({style: {cx: 100,cy: 100,r: 50,},}),},});
也可以在创建图形之后设置裁剪区域,因此以上写法等价于:
const image = new Image({style: {//... 省略其他属性},});image.style.clipPath = new Circle({style: {cx: 100,cy: 100,r: 50,},});// 或者兼容旧版写法image.setClip(new Circle({style: {cx: 100,cy: 100,r: 50,},}),);
当我们想清除裁剪区域时,可以设置为 null
:
image.style.clipPath = null;// 或者image.setClip(null);
裁剪区域图形本身也是支持修改属性的,受它影响,被裁剪图形会立刻重绘。
配合动画系统我们可以对已经添加到画布中的裁剪区域图形进行变换,实现以下效果,示例:
// 对裁剪区域应用动画clipPathCircle.animate([{ transform: 'scale(1)' }, { transform: 'scale(1.2)' }],{duration: 1500,iterations: Infinity,},);
我们暂不支持复合的裁剪区域,例如自定义图形以及 Group.
在路径动画中,我们可以使用 offsetPath
指定一个图形的运动轨迹,配合动画系统对 offsetDistance
属性应用变换:
const circle = new Circle({style: {offsetPath: new Line({// 创建运动轨迹style: {// 不需要设置其他与轨迹无关的绘图属性x1: 100,y1: 100,x2: 300,y2: 100,},}),r: 10,},});const animation = circle.animate([{ offsetDistance: 0 }, // 变换{ offsetDistance: 1 },],{duration: 3000,easing: 'ease-in-out',iterations: Infinity,},);
指定路径轨迹,目前支持 Line Path 和 Polyline 这三种图形。
从路径起点出发行进的距离,取值范围为 [0-1]
,0 代表路径起点,1 代表终点。
当鼠标悬停在图形上时,我们可以改变它的样式,通过修改容器的 CSS 样式实现。
cursor
属性支持的值可以参考:https://developer.mozilla.org/zh-CN/docs/Web/CSS/cursor
const circle = new Circle({style: {//... 省略其他属性cursor: 'pointer',},});
我们可以设置图形如何响应交互事件,例如命中拾取时展示鼠标样式,或者增大拾取区域。
设置图形如何响应交互事件,可参考:https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
简而言之,fill stroke 和 visibility 都可以独立或组合影响拾取判定行为。目前支持以下关键词:
'auto'
默认值,等同于 'visiblepainted'
。'none'
永远不会成为响应事件的目标。'visiblepainted'
满足以下条件才会响应事件:
'visible'
,即图形为可见的。'none'
的值。或者在图形描边区域触发同时 stroke 取非 'none'
的值。'visiblefill'
满足以下条件才会响应事件:
'visible'
,即图形为可见的。'visiblestroke'
满足以下条件才会响应事件:
'visible'
,即图形为可见的。'visible'
满足以下条件才会响应事件:
'visible'
,即图形为可见的。'painted'
满足以下条件才会响应事件:
'none'
的值。或者在图形描边区域触发同时 stroke 取非 'none'
的值。不受 visibility 取值的影响。'fill'
满足以下条件才会响应事件:
'stroke'
满足以下条件才会响应事件:
'all'
只要进入图形的填充和描边区域就会响应事件。因此不会受 fill stroke visibility 的取值影响。在该 示例 中,我们将该属性设置为 stroke
,因此填充区域不会响应事件:
在该 示例 中,基于继承机制我们能很方便的控制可交互性:
// 整个画布不响应交互事件canvas.document.documentElement.style.pointerEvents = 'none';
初始值 | 适用元素 | 是否可继承 | 是否支持动画 | 计算值 |
---|---|---|---|---|
'auto' | 所有 | 是 | 否 | <keywords> |
当 lineWidth 较小时,可交互区域也随之变小,有时我们想增大这个区域,让“细线”更容易被拾取到。注意该属性并不会影响渲染效果。
在下面的 示例 中,我们设置该属性为 50
,在进行拾取时线宽相当于 50 + 原始线宽
,这样靠近时就更容易拾取到了:
line.style.increasedLineWidthForHitTesting = 50;
另外和 lineWidth 一样,该属性同样会向两侧延展,下图中无填充的 Path 内部拾取区域也变大了:
初始值 | 适用元素 | 是否可继承 | 是否支持动画 | 计算值 |
---|---|---|---|---|
'0' | 所有 | 否 | 否 | <percentage> <length> |
我们提供了一系列变换方法。
对于平移操作,我们提供了局部/世界坐标系下,移动绝对/相对距离的 API:
名称 | 参数 | 返回值 | 备注 |
---|---|---|---|
translate | [number, number] number, number number | 无 | 在 世界坐标系 下,相对当前位置移动 |
translateLocal | [number, number] number, number number | 无 | 在 局部坐标系 下,相对当前位置移动 |
setPosition | [number, number] number, number number | 无 | 设置 世界坐标系 下的位置 |
setLocalPosition | [number, number] number, number number | 无 | 设置 局部坐标系 下的位置 |
getPosition | 无 | [number, number] | 获取 世界坐标系 下的位置 |
getLocalPosition | 无 | [number, number] | 获取 局部坐标系 下的位置 |
其中 translate/translateLocal/setPosition/setLocalPosition 支持以下入参形式,其中如果只想修改 X 轴方向,可以只传一个数字:
circle.translate([100, 0]); // [number, number]circle.translate(100, 0); // number, numbercircle.translate(100); // number
和平移不同,我们无法提供 setScale
这样设置世界坐标系下缩放的方法,因此全局坐标系下缩放是只读的,这在 Unity 中称之为 lossyScale。
名称 | 参数 | 返回值 | 备注 |
---|---|---|---|
scaleLocal | [number, number] number, number number | 无 | 在 局部坐标系 下,相对当前缩放比例继续缩放 |
setLocalScale | [number, number] number, number number | 无 | 设置 局部坐标系 下的缩放比例 |
getScale | 无 | [number, number] | 获取 世界坐标系 下的缩放比例 |
getLocalScale | 无 | [number, number] | 获取 局部坐标系 下的缩放比例 |
其中 scaleLocal/setLocalScale 支持以下入参形式,其中如果水平/垂直方向缩放比例相等时,可以只传一个数字:
circle.scaleLocal([2, 2]); // [number, number]circle.scaleLocal(2, 2); // number, numbercircle.scaleLocal(2); // number
如果想实现沿 X / Y 轴翻转,可以传入负值,例如沿 Y 轴翻转:
circle.setLocalScale(-1, 1);
在 3D 场景中,旋转可以用矩阵、轴角、欧拉角和四元数表示,它们彼此之间可以互相转换。虽然考虑到未来的扩展性,在 G 内部实现中我们使用了四元数。
名称 | 参数 | 返回值 | 备注 |
---|---|---|---|
rotateLocal | number | 无 | 在 局部坐标系 下,旋转一定的欧拉角,顺时针方向为正,单位为 degree |
rotate | number | 无 | 在 世界坐标系 下,旋转一定的欧拉角 |
setEulerAngles | number | 无 | 设置 世界坐标系 下的欧拉角 |
setLocalEulerAngles | number | 无 | 设置 局部坐标系 下的欧拉角 |
setLocalRotation | quat | 无 | 设置 局部坐标系 下的四元数 |
setRotation | quat | 无 | 设置 世界坐标系 下的四元数 |
getEulerAngles | 无 | number | 获取 世界坐标系 下的欧拉角 |
getLocalEulerAngles | 无 | number | 获取 局部坐标系 下的欧拉角 |
getLocalRotation | 无 | quat | 获取 局部坐标系 下的四元数 |
getRotation | 无 | quat | 获取 世界坐标系 下的四元数 |
在 2D 场景中,可以进行拉伸,在一定方向上以一定角度扭曲元素上的每个点。可参考 CSS 同名变换函数。
名称 | 参数 | 返回值 | 备注 |
---|---|---|---|
setLocalSkew | vec2 | 无 | 在 局部坐标系 下,沿着横/纵坐标扭曲元素的角度,单位为 rad |
getLocalSkew | 无 | vec2 | 获取 局部坐标系 下的扭曲角度,单位为 rad |
除了使用 transformOrigin 属性,还可以通过 setOrigin
重新设置变换中心。
名称 | 参数 | 返回值 | 备注 |
---|---|---|---|
setOrigin | [number, number] 或 [number, number, number] 或 number, number 或 number, number, number | 无 | 设置局部坐标系下的缩放和旋转中心 |
getOrigin | [number, number, number] | 无 | 获取局部坐标系下的缩放和旋转中心 |
设置局部坐标系下的缩放和旋转中心,示例
默认值为 [0, 0]
。
在下面的例子中,我们在 [100, 100]
处放置了一个半径为 100 的圆:
const circle = new Circle({style: {cx: 100,cy: 100,r: 100,},});
如果我们想让圆以圆心作为变换中心进行缩放,发生变化的是包围盒:
circle.setOrigin(100, 100);circle.scale(0.5);circle.getBounds(); // { center: [100, 100], halfExtents: [50, 50] }
但假如我们想让这个圆以自身包围盒左上角进行缩放:
circle.setOrigin(0, 0);circle.scale(0.5);circle.getBounds(); // { center: [50, 50], halfExtents: [50, 50] }
在下面的示例中,我们创建了一个矩形,它的默认锚点为局部坐标系下包围盒的左上角。如果我们想让它以包围盒中心进行旋转,就需要设置变换中心相对于锚点偏移长宽各一半,即 [150, 100]
:
const rect = new Rect({id: 'rect',style: {width: 300,height: 200,},});rect.setOrigin(150, 100); // 设置旋转与缩放中心为自身包围盒中心点
例如我们想修改一个圆的变换中心到左上角而非圆心,可以这样做:
const circle = new Circle({style: {cx: 100,cy: 100,r: 100,},});circle.setOrigin(0, 0);// 或者circle.style.transformOrigin = 'left top'; // 包围盒左上角// 或者circle.style.transformOrigin = '0px 0px';// 或者circle.style.transformOrigin = '0% 0%';
基于不同的包围盒定义,我们提供了以下获取方法。
获取基础图形的几何包围盒,除了定义所需的样式属性(例如 Circle 的 r,Rect 的 width/height),它不受其他绘图属性(例如 lineWidth,fitler,shadowBlur 等)影响:
const circle = new Circle({style: {cx: 100, // 局部坐标系下的坐标不会影响 Geometry Boundscy: 100, // 局部坐标系下的坐标不会影响 Geometry Boundsr: 100,lineWidth: 20, // 样式属性不会影响 Geometry BoundsshadowBlur: 10, // 样式属性不会影响 Geometry Bounds},});circle.getGeometryBounds(); // { center: [0, 0], halfExtents: [100, 100] }
Group 由于没有几何定义,因此会返回 null:
const group = new Group();group.getGeometryBounds(); // null
合并自身以及子节点在世界坐标系下的 Geometry Bounds。这应当是最常用的计算方式:
const circle = new Circle({style: {cx: 100, // 应用世界坐标系下的变换cy: 100,r: 100,},});circle.getBounds(); // { center: [100, 100], halfExtents: [100, 100] }
合并自身以及子节点在世界坐标系下的 Render Bounds,在 Geometry Bounds 基础上,受以下样式属性影响: lineWidth,shadowBlur,filter:
const circle = new Circle({style: {cx: 100, // 应用世界坐标系下的变换cy: 100,r: 100,lineWidth: 20, // 考虑样式属性},});// r + lineWidth / 2circle.getRenderBounds(); // { center: [100, 100], halfExtents: [110, 110] }
getBounds 的唯一区别是在父节点的局部坐标系下计算。
兼容 SVG 同名方法,计算方式等同于 getBounds,区别仅在于返回值类型不同,后者返回的是 AABB,而该方法返回一个 DOMRect:
interface DOMRect {top: number;left: number;right: number;bottom: number;width: number;height: number;}
获取浏览器坐标系下的 Geometry Bounds,应用世界坐标系下的变换后,再加上画布相对于浏览器的偏移量。
在场景图中,我们需要构建父子关系,快速获取父子节点,有时还需要在子树中查询某一类型的节点列表。基于继承关系,每个 DisplayObject 都拥有 Node 和 Element 能力。
名称 | 属性/方法 | 返回值 | 备注 |
---|---|---|---|
parentNode | 属性 | DisplayObject | null | 父节点(如有) |
parentElement | 属性 | DisplayObject | null | 父节点(如有) |
childNodes | 属性 | DisplayObject[] | 子节点列表 |
children | 属性 | DisplayObject[] | 子节点列表 |
firstChild | 属性 | DisplayObject | null | 返回子节点列表中第一个节点(如有) |
lastChild | 属性 | DisplayObject | null | 返回子节点列表中最后一个节点(如有) |
nextSibling | 属性 | DisplayObject | null | 返回后一个兄弟节点(如有) |
previousSibling | 属性 | DisplayObject | null | 返回前一个兄弟节点(如有) |
contains | 方法 | boolean | 子树中是否包含某个节点(入参) |
getRootNode | 方法 | Node | 返回当前节点的根节点 |
ownerDocument | 属性 | Document | 返回画布入口 Document |
isConnected | 属性 | boolean | 节点是否被添加到画布中 |
参考 CSS 选择器,我们提供了以下查询方法,查询范围是当前节点的整棵子树,并不仅仅是直接的子节点列表,而是所有子孙节点。
名称 | 参数 | 返回值 | 备注 |
---|---|---|---|
getElementById | (id: string) | DisplayObject | null | 通过 id 查询子节点 |
getElementsByName | (name: string) | DisplayObject[] | 通过 name 查询子节点列表 |
getElementsByClassName | (className: string) | DisplayObject[] | 通过 className 查询子节点列表 |
getElementsByTagName | (tagName: string) | DisplayObject[] | 通过 tagName 查询子节点列表 |
querySelector | (selector: string) | DisplayObject | null | 查询满足条件的第一个子节点 |
querySelectorAll | (selector: string) | DisplayObject[] | 查询满足条件的所有子节点列表 |
find | (filter: Function) | DisplayObject | null | 查询满足条件的第一个子节点 |
findAll | (filter: Function) | DisplayObject[] | 查询满足条件的所有子节点列表 |
下面我们以上面太阳系的例子,演示如何使用这些查询方法。
solarSystem.getElementsByName('sun');// sunsolarSystem.getElementsByTagName('circle');solarSystem.getElementsByTagName(Shape.CIRCLE);// [sun, earth, moon]solarSystem.querySelector('[name=sun]');// sunsolarSystem.querySelectorAll('[r=25]');// [moon]
有时查询条件不好用 CSS 选择器描述,此时可以使用自定义查询方法:find/findAll。它们可以类比成 querySelector/querySelectorAll。不同之处在于前者需要传入一个 filter,例如以下写法等价:
solarSystem.querySelector('[name=sun]');solarSystem.find((element) => element.name === 'sun');solarSystem.querySelectorAll('[r=25]');solarSystem.findAll((element) => element.style.r === 25);
以下添加/删除节点能力来自继承的 Element 基类。
名称 | 参数 | 返回值 | 备注 |
---|---|---|---|
appendChild | child: DisplayObject | DisplayObject | 添加子节点,返回添加的节点 |
insertBefore | child: DisplayObject reference?: DisplayObject | DisplayObject | 添加子节点,在某个子节点之前(如有),返回添加的节点 |
append | ...nodes: DisplayObject[] | 在当前节点的子节点列表末尾批量添加一组节点 | |
prepend | ...nodes: DisplayObject[] | 在当前节点的子节点列表头部批量添加一组节点 | |
after | ...nodes: DisplayObject[] | 在当前节点之后批量添加一些兄弟节点 | |
before | ...nodes: DisplayObject[] | 在当前节点之前批量添加一些兄弟节点 | |
removeChild | child: DisplayObject | DisplayObject | 删除子节点,返回被删除的节点。 |
removeChildren | 删除全部子节点。 | ||
remove | destroy = true | DisplayObject | 从父节点(如有)中移除自身,destroy 表示是否要销毁 |
replaceChild | child: DisplayObject | DisplayObject | 用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点 |
replaceWith | ...nodes: DisplayObject[] | 在父节点的子节点列表中,用传入的节点列表替换该节点 | |
replaceChildren | ...nodes: DisplayObject[] | 替换该节点的所有子节点。不传参数时则会清空该节点的所有子节点 |
从父节点中删除子节点并销毁有以下两种方式:
// parent -> childparent.removeChild(child);// 等价于child.remove();
删除所有子节点有以下三种方式:
parent.removeChildren();// 等价于[...parent.children].forEach((child) => parent.removeChild(child));[...parent.children].forEach((child) => child.remove());// 等价于parent.replaceChildren();
在添加/删除节点时有以下注意点:
remove(false)
方法签名为 cloneNode(deep?: boolean): this
,可选参数为是否需要深拷贝,返回克隆得到的新节点。
在下面的例子中,我们创建了一个圆,设置了它的半径与位置。拷贝得到的新节点拥有同样的样式属性与位置:
circle.style.r = 20;circle.setPosition(10, 20);const clonedCircle = circle.cloneNode();clonedCircle instanceof Circle; // trueclonedCircle.style.r; // 20clonedCircle.getPosition(); // [10, 20]
注意事项:
appendChild
将其加入画布才会被渲染在这个示例中,我们展示了以上特性:
名称 | 参数 | 返回值 | 备注 |
---|---|---|---|
getAttribute | (name: string) | null | any | 根据属性名获取属性值 |
setAttribute | (name: string, value: any) | 无 | 设置属性值 |
⚠️ 兼容旧版 attr(name: string, value?: any)
,获取以及设置属性值。
⚠️ 兼容 HTMLElement Style,因此可以使用以下方法:
以下用法等价:
const circle = new Circle({style: {// 或者使用 attrsr: 10,fill: 'red',},});// 获取属性值circle.getAttribute('fill'); // redcircle.attr('fill'); // redcircle.style.fill; // redcircle.style.getPropertyValue('fill');// 设置属性值circle.setAttribute('r', 20);circle.attr('r', 20);circle.style.r = 20;circle.style.setProperty('r', 20);
部分属性例如 Rect 的 width / height 是支持单位的,如果想获取计算后的值,可以使用 parsedStyle
:
rect.style.width = '100px';rect.parsedStyle.width; // CSSUnitValue { unit: 'px', value: 100 }
需要注意的是,目前在使用动画时,我们也会将待插值的属性值进行转换,因此如果想获取以 px 为单位的绝对值,需要使用 parsedStyle
示例:
animation.onframe = () => {rect.style.width; // '100px'rect.parsedStyle.width; // CSSUnitValue { unit: 'px', value: 100 }};
调用 destroy()
将销毁节点。被销毁的节点将无法被再次加入画布渲染。通过 destroyed 属性可以判断一个节点是否已经被销毁。
circle.destroy();
在调用用该方法时,会依次执行以下操作:
remove()
将自身从场景图中移除,因此会触发 Removed 和 ChildRemoved 事件通过以下属性可以判断图形当前的状态,例如是否被加入到画布中,是否已经被销毁等。
用于判断一个图形是否已经被加入到画布中。
https://developer.mozilla.org/zh-CN/docs/Web/API/Node/isConnected
circle.isConnected; // falsecanvas.appendChild(circle); // add to canvascircle.isConnected; // true
指向画布的入口 Document。如果还未加入到画布中,返回 null。
https://developer.mozilla.org/en-US/docs/Web/API/Node/ownerDocument
circle.ownerDocument; // nullcanvas.appendChild(circle); // add to canvascircle.ownerDocument; // canvas.document
用于判断一个图形是否已经被销毁。
通过调用 destroy()
主动销毁自身,或者父节点通过 destroyChildren()
主动移除并销毁所有子节点等:
circle.destroyed; // falsecircle.destroy();circle.destroyed; // true
在事件系统中,我们可以使用类似 DOM Event API 的方式给添加到画布中的节点增加事件监听器。
除了例如 click、mouseenter 这样的交互事件,我们还提供了一系列内置的节点生命周期事件,例如可以监听节点的添加和删除事件,这些事件同样有完整的传播路径(冒泡、捕获),示例:
import { ElementEvent, MutationEvent } from '@antv/g';child.on(ElementEvent.INSERTED, (e: MutationEvent) => {e.target; // childe.relatedNode; // parent});child.on(ElementEvent.REMOVED, (e) => {e.target; // childe.relatedNode; // parent});child.on(ElementEvent.ATTR_MODIFIED, (e) => {e.target; // childe.attrName; // 属性名e.prevValue; // 旧值e.newValue; // 新值});parent.appendChild(child);
目前我们支持如下场景图相关事件:
参考 Web Animations API,可以使用 animate 完成 keyframe 动画,下面是一个 ScaleIn 动画效果:
circle.animate([{transform: 'scale(0)',},{transform: 'scale(1)',},],{duration: 500,easing: 'cubic-bezier(0.250, 0.460, 0.450, 0.940)',iterations: Infinity,},);
更多用法详见动画系统
https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Howto/Use_data_attributes
提供 data-*
属性用于存储额外信息。
group.dataset.type = 'a';group.getAttribute('data-type'); // 'a'
需要注意的是,data-
前缀之后的部分通过 dataset
访问时需要使用驼峰形式:
group.setAttribute('data-a-b-c');group.dataset.aBC;// Wronggroup.dataset.abc;group.dataset.abC;