package.src.graphic.Group.ts Maven / Gradle / Ivy
/**
* Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上
* @module zrender/graphic/Group
* @example
* const Group = require('zrender/graphic/Group');
* const Circle = require('zrender/graphic/shape/Circle');
* const g = new Group();
* g.position[0] = 100;
* g.position[1] = 100;
* g.add(new Circle({
* style: {
* x: 100,
* y: 100,
* r: 20,
* }
* }));
* zr.add(g);
*/
import * as zrUtil from '../core/util';
import Element, { ElementProps } from '../Element';
import BoundingRect from '../core/BoundingRect';
import { MatrixArray } from '../core/matrix';
import Displayable from './Displayable';
import { ZRenderType } from '../zrender';
export interface GroupProps extends ElementProps {
}
class Group extends Element {
readonly isGroup = true
private _children: Element[] = []
constructor(opts?: GroupProps) {
super();
this.attr(opts);
}
/**
* Get children reference.
*/
childrenRef() {
return this._children;
}
/**
* Get children copy.
*/
children() {
return this._children.slice();
}
/**
* 获取指定 index 的儿子节点
*/
childAt(idx: number): Element {
return this._children[idx];
}
/**
* 获取指定名字的儿子节点
*/
childOfName(name: string): Element {
const children = this._children;
for (let i = 0; i < children.length; i++) {
if (children[i].name === name) {
return children[i];
}
}
}
childCount(): number {
return this._children.length;
}
/**
* 添加子节点到最后
*/
add(child: Element): Group {
if (child) {
if (child !== this && child.parent !== this) {
this._children.push(child);
this._doAdd(child);
}
if (process.env.NODE_ENV !== 'production') {
if (child.__hostTarget) {
throw 'This elemenet has been used as an attachment';
}
}
}
return this;
}
/**
* 添加子节点在 nextSibling 之前
*/
addBefore(child: Element, nextSibling: Element) {
if (child && child !== this && child.parent !== this
&& nextSibling && nextSibling.parent === this) {
const children = this._children;
const idx = children.indexOf(nextSibling);
if (idx >= 0) {
children.splice(idx, 0, child);
this._doAdd(child);
}
}
return this;
}
replace(oldChild: Element, newChild: Element) {
const idx = zrUtil.indexOf(this._children, oldChild);
if (idx >= 0) {
this.replaceAt(newChild, idx);
}
return this;
}
replaceAt(child: Element, index: number) {
const children = this._children;
const old = children[index];
if (child && child !== this && child.parent !== this && child !== old) {
children[index] = child;
old.parent = null;
const zr = this.__zr;
if (zr) {
old.removeSelfFromZr(zr);
}
this._doAdd(child);
}
return this;
}
_doAdd(child: Element) {
if (child.parent) {
// Parent must be a group
(child.parent as Group).remove(child);
}
child.parent = this;
const zr = this.__zr;
if (zr && zr !== (child as Group).__zr) { // Only group has __storage
child.addSelfToZr(zr);
}
zr && zr.refresh();
}
/**
* Remove child
* @param child
*/
remove(child: Element) {
const zr = this.__zr;
const children = this._children;
const idx = zrUtil.indexOf(children, child);
if (idx < 0) {
return this;
}
children.splice(idx, 1);
child.parent = null;
if (zr) {
child.removeSelfFromZr(zr);
}
zr && zr.refresh();
return this;
}
/**
* Remove all children
*/
removeAll() {
const children = this._children;
const zr = this.__zr;
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (zr) {
child.removeSelfFromZr(zr);
}
child.parent = null;
}
children.length = 0;
return this;
}
/**
* 遍历所有子节点
*/
eachChild(
cb: (this: Context, el: Element, index?: number) => void,
context?: Context
) {
const children = this._children;
for (let i = 0; i < children.length; i++) {
const child = children[i];
cb.call(context, child, i);
}
return this;
}
/**
* Visit all descendants.
* Return false in callback to stop visit descendants of current node
*/
// TODO Group itself should also invoke the callback.
traverse(
cb: (this: T, el: Element) => boolean | void,
context?: T
) {
for (let i = 0; i < this._children.length; i++) {
const child = this._children[i];
const stopped = cb.call(context, child);
if (child.isGroup && !stopped) {
child.traverse(cb, context);
}
}
return this;
}
addSelfToZr(zr: ZRenderType) {
super.addSelfToZr(zr);
for (let i = 0; i < this._children.length; i++) {
const child = this._children[i];
child.addSelfToZr(zr);
}
}
removeSelfFromZr(zr: ZRenderType) {
super.removeSelfFromZr(zr);
for (let i = 0; i < this._children.length; i++) {
const child = this._children[i];
child.removeSelfFromZr(zr);
}
}
getBoundingRect(includeChildren?: Element[]): BoundingRect {
// TODO Caching
const tmpRect = new BoundingRect(0, 0, 0, 0);
const children = includeChildren || this._children;
const tmpMat: MatrixArray = [];
let rect = null;
for (let i = 0; i < children.length; i++) {
const child = children[i];
// TODO invisible?
if (child.ignore || (child as Displayable).invisible) {
continue;
}
const childRect = child.getBoundingRect();
const transform = child.getLocalTransform(tmpMat);
// TODO
// The boundingRect cacluated by transforming original
// rect may be bigger than the actual bundingRect when rotation
// is used. (Consider a circle rotated aginst its center, where
// the actual boundingRect should be the same as that not be
// rotated.) But we can not find better approach to calculate
// actual boundingRect yet, considering performance.
if (transform) {
BoundingRect.applyTransform(tmpRect, childRect, transform);
rect = rect || tmpRect.clone();
rect.union(tmpRect);
}
else {
rect = rect || childRect.clone();
rect.union(childRect);
}
}
return rect || tmpRect;
}
}
Group.prototype.type = 'group';
// Storage will use childrenRef to get children to render.
export interface GroupLike extends Element {
childrenRef(): Element[]
}
export default Group;
© 2015 - 2025 Weber Informatics LLC | Privacy Policy