18395d9227a533ab0cdb65441aa0a415f96096961e5311a994aa1cfa4857f2e4e2185d7b8037e8f4691b5a5458f53236eb970ce51628e85e203cd44653d6e4 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /**
  2. * Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上
  3. * @module zrender/graphic/Group
  4. * @example
  5. * const Group = require('zrender/graphic/Group');
  6. * const Circle = require('zrender/graphic/shape/Circle');
  7. * const g = new Group();
  8. * g.position[0] = 100;
  9. * g.position[1] = 100;
  10. * g.add(new Circle({
  11. * style: {
  12. * x: 100,
  13. * y: 100,
  14. * r: 20,
  15. * }
  16. * }));
  17. * zr.add(g);
  18. */
  19. import * as zrUtil from '../core/util';
  20. import Element, { ElementProps } from '../Element';
  21. import BoundingRect from '../core/BoundingRect';
  22. import { MatrixArray } from '../core/matrix';
  23. import Displayable from './Displayable';
  24. import { ZRenderType } from '../zrender';
  25. export interface GroupProps extends ElementProps {
  26. }
  27. class Group extends Element<GroupProps> {
  28. readonly isGroup = true
  29. private _children: Element[] = []
  30. constructor(opts?: GroupProps) {
  31. super();
  32. this.attr(opts);
  33. }
  34. /**
  35. * Get children reference.
  36. */
  37. childrenRef() {
  38. return this._children;
  39. }
  40. /**
  41. * Get children copy.
  42. */
  43. children() {
  44. return this._children.slice();
  45. }
  46. /**
  47. * 获取指定 index 的儿子节点
  48. */
  49. childAt(idx: number): Element {
  50. return this._children[idx];
  51. }
  52. /**
  53. * 获取指定名字的儿子节点
  54. */
  55. childOfName(name: string): Element {
  56. const children = this._children;
  57. for (let i = 0; i < children.length; i++) {
  58. if (children[i].name === name) {
  59. return children[i];
  60. }
  61. }
  62. }
  63. childCount(): number {
  64. return this._children.length;
  65. }
  66. /**
  67. * 添加子节点到最后
  68. */
  69. add(child: Element): Group {
  70. if (child) {
  71. if (child !== this && child.parent !== this) {
  72. this._children.push(child);
  73. this._doAdd(child);
  74. }
  75. if (process.env.NODE_ENV !== 'production') {
  76. if (child.__hostTarget) {
  77. throw 'This elemenet has been used as an attachment';
  78. }
  79. }
  80. }
  81. return this;
  82. }
  83. /**
  84. * 添加子节点在 nextSibling 之前
  85. */
  86. addBefore(child: Element, nextSibling: Element) {
  87. if (child && child !== this && child.parent !== this
  88. && nextSibling && nextSibling.parent === this) {
  89. const children = this._children;
  90. const idx = children.indexOf(nextSibling);
  91. if (idx >= 0) {
  92. children.splice(idx, 0, child);
  93. this._doAdd(child);
  94. }
  95. }
  96. return this;
  97. }
  98. replace(oldChild: Element, newChild: Element) {
  99. const idx = zrUtil.indexOf(this._children, oldChild);
  100. if (idx >= 0) {
  101. this.replaceAt(newChild, idx);
  102. }
  103. return this;
  104. }
  105. replaceAt(child: Element, index: number) {
  106. const children = this._children;
  107. const old = children[index];
  108. if (child && child !== this && child.parent !== this && child !== old) {
  109. children[index] = child;
  110. old.parent = null;
  111. const zr = this.__zr;
  112. if (zr) {
  113. old.removeSelfFromZr(zr);
  114. }
  115. this._doAdd(child);
  116. }
  117. return this;
  118. }
  119. _doAdd(child: Element) {
  120. if (child.parent) {
  121. // Parent must be a group
  122. (child.parent as Group).remove(child);
  123. }
  124. child.parent = this;
  125. const zr = this.__zr;
  126. if (zr && zr !== (child as Group).__zr) { // Only group has __storage
  127. child.addSelfToZr(zr);
  128. }
  129. zr && zr.refresh();
  130. }
  131. /**
  132. * Remove child
  133. * @param child
  134. */
  135. remove(child: Element) {
  136. const zr = this.__zr;
  137. const children = this._children;
  138. const idx = zrUtil.indexOf(children, child);
  139. if (idx < 0) {
  140. return this;
  141. }
  142. children.splice(idx, 1);
  143. child.parent = null;
  144. if (zr) {
  145. child.removeSelfFromZr(zr);
  146. }
  147. zr && zr.refresh();
  148. return this;
  149. }
  150. /**
  151. * Remove all children
  152. */
  153. removeAll() {
  154. const children = this._children;
  155. const zr = this.__zr;
  156. for (let i = 0; i < children.length; i++) {
  157. const child = children[i];
  158. if (zr) {
  159. child.removeSelfFromZr(zr);
  160. }
  161. child.parent = null;
  162. }
  163. children.length = 0;
  164. return this;
  165. }
  166. /**
  167. * 遍历所有子节点
  168. */
  169. eachChild<Context>(
  170. cb: (this: Context, el: Element, index?: number) => void,
  171. context?: Context
  172. ) {
  173. const children = this._children;
  174. for (let i = 0; i < children.length; i++) {
  175. const child = children[i];
  176. cb.call(context, child, i);
  177. }
  178. return this;
  179. }
  180. /**
  181. * Visit all descendants.
  182. * Return false in callback to stop visit descendants of current node
  183. */
  184. // TODO Group itself should also invoke the callback.
  185. traverse<T>(
  186. cb: (this: T, el: Element) => boolean | void,
  187. context?: T
  188. ) {
  189. for (let i = 0; i < this._children.length; i++) {
  190. const child = this._children[i];
  191. const stopped = cb.call(context, child);
  192. if (child.isGroup && !stopped) {
  193. child.traverse(cb, context);
  194. }
  195. }
  196. return this;
  197. }
  198. addSelfToZr(zr: ZRenderType) {
  199. super.addSelfToZr(zr);
  200. for (let i = 0; i < this._children.length; i++) {
  201. const child = this._children[i];
  202. child.addSelfToZr(zr);
  203. }
  204. }
  205. removeSelfFromZr(zr: ZRenderType) {
  206. super.removeSelfFromZr(zr);
  207. for (let i = 0; i < this._children.length; i++) {
  208. const child = this._children[i];
  209. child.removeSelfFromZr(zr);
  210. }
  211. }
  212. getBoundingRect(includeChildren?: Element[]): BoundingRect {
  213. // TODO Caching
  214. const tmpRect = new BoundingRect(0, 0, 0, 0);
  215. const children = includeChildren || this._children;
  216. const tmpMat: MatrixArray = [];
  217. let rect = null;
  218. for (let i = 0; i < children.length; i++) {
  219. const child = children[i];
  220. // TODO invisible?
  221. if (child.ignore || (child as Displayable).invisible) {
  222. continue;
  223. }
  224. const childRect = child.getBoundingRect();
  225. const transform = child.getLocalTransform(tmpMat);
  226. // TODO
  227. // The boundingRect cacluated by transforming original
  228. // rect may be bigger than the actual bundingRect when rotation
  229. // is used. (Consider a circle rotated aginst its center, where
  230. // the actual boundingRect should be the same as that not be
  231. // rotated.) But we can not find better approach to calculate
  232. // actual boundingRect yet, considering performance.
  233. if (transform) {
  234. BoundingRect.applyTransform(tmpRect, childRect, transform);
  235. rect = rect || tmpRect.clone();
  236. rect.union(tmpRect);
  237. }
  238. else {
  239. rect = rect || childRect.clone();
  240. rect.union(childRect);
  241. }
  242. }
  243. return rect || tmpRect;
  244. }
  245. }
  246. Group.prototype.type = 'group';
  247. // Storage will use childrenRef to get children to render.
  248. export interface GroupLike extends Element {
  249. childrenRef(): Element[]
  250. }
  251. export default Group;