displayObject.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. import { RAD_TO_DEG, DEG_TO_RAD } from "./const";
  2. import { Bounds } from "./bounds";
  3. import { Point } from "./point";
  4. import { Transform } from "./transform";
  5. import { Rectangle } from "./rectangle";
  6. export class DisplayObject {
  7. tempDisplayObjectParent?:DisplayObject;
  8. transform = new Transform();
  9. visible = true;
  10. parent?:any;
  11. alpha = 1;
  12. /**
  13. * The multiplied alpha of the displayObject.
  14. */
  15. worldAlpha = 1;
  16. /**
  17. * Which index in the children array the display component was before the previous zIndex sort.
  18. * Used by containers to help sort objects with the same zIndex, by using previous array index as the decider.
  19. */
  20. _lastSortedIndex = 0;
  21. /**
  22. * The zIndex of the displayObject.
  23. * A higher value will mean it will be rendered on top of other displayObjects within the same container.
  24. */
  25. _zIndex = 0;
  26. /**
  27. * The bounds object, this is used to calculate and store the bounds of the displayObject.
  28. */
  29. _bounds = new Bounds();
  30. _boundsID = 0;
  31. _lastBoundsID = -1;
  32. _boundsRect?:Rectangle;
  33. _localBoundsRect?:Rectangle;
  34. /**
  35. * If the object has been destroyed via destroy(). If true, it should not be used.
  36. */
  37. _destroyed = false;
  38. get _tempDisplayObjectParent() {
  39. if (!this.tempDisplayObjectParent) {
  40. this.tempDisplayObjectParent = new DisplayObject();
  41. }
  42. return this.tempDisplayObjectParent as DisplayObject;
  43. }
  44. /**
  45. * Updates the object transform for rendering.
  46. * TODO - Optimization pass!
  47. */
  48. updateTransform() {
  49. if (this.parent) {
  50. this.transform.updateTransform(this.parent.transform);
  51. // multiply the alphas..
  52. this.worldAlpha = this.alpha * this.parent.worldAlpha;
  53. } else {
  54. this.transform.updateTransform(this._tempDisplayObjectParent.transform);
  55. this.worldAlpha = this.alpha;
  56. }
  57. this._boundsID++;
  58. }
  59. /**
  60. * Recursively updates transform of all objects from the root to this one
  61. * internal function for toLocal()
  62. */
  63. _recursivePostUpdateTransform() {
  64. if (this.parent) {
  65. this.parent._recursivePostUpdateTransform();
  66. this.transform.updateTransform(this.parent.transform);
  67. } else {
  68. this.transform.updateTransform(this._tempDisplayObjectParent.transform);
  69. }
  70. }
  71. calculateBounds() {
  72. console.log("please implement me")
  73. }
  74. getBounds(skipUpdate:boolean, rect?:Rectangle) {
  75. if (!skipUpdate) {
  76. if (!this.parent) {
  77. this.parent = this._tempDisplayObjectParent;
  78. this.updateTransform();
  79. this.parent = undefined;
  80. } else {
  81. this._recursivePostUpdateTransform();
  82. this.updateTransform();
  83. }
  84. }
  85. if (this._boundsID !== this._lastBoundsID) {
  86. this.calculateBounds();
  87. this._lastBoundsID = this._boundsID;
  88. }
  89. if (!rect) {
  90. if (!this._boundsRect) {
  91. this._boundsRect = new Rectangle();
  92. }
  93. rect = this._boundsRect;
  94. }
  95. return this._bounds.getRectangle(rect);
  96. }
  97. getLocalBounds(rect?:Rectangle) {
  98. const transformRef = this.transform;
  99. const parentRef = this.parent;
  100. this.parent = undefined;
  101. this.transform = this._tempDisplayObjectParent.transform;
  102. if (!rect) {
  103. if (!this._localBoundsRect) {
  104. this._localBoundsRect = new Rectangle();
  105. }
  106. rect = this._localBoundsRect;
  107. }
  108. const bounds = this.getBounds(false, rect);
  109. this.parent = parentRef;
  110. this.transform = transformRef;
  111. return bounds;
  112. }
  113. /**
  114. * Calculates the global position of the display object.
  115. */
  116. toGlobal(position:Point, point:Point, skipUpdate = false) {
  117. if (!skipUpdate) {
  118. this._recursivePostUpdateTransform();
  119. // this parent check is for just in case the item is a root object.
  120. // If it is we need to give it a temporary parent so that displayObjectUpdateTransform works correctly
  121. // this is mainly to avoid a parent check in the main loop. Every little helps for performance :)
  122. if (!this.parent) {
  123. this.parent = this._tempDisplayObjectParent;
  124. this.displayObjectUpdateTransform();
  125. this.parent = undefined;
  126. } else {
  127. this.displayObjectUpdateTransform();
  128. }
  129. }
  130. // don't need to update the lot
  131. return this.worldTransform.apply(position, point);
  132. }
  133. /**
  134. * Calculates the local position of the display object relative to another point.
  135. *
  136. * @param {PIXI.IPoint} position - The world origin to calculate from.
  137. * @param {PIXI.DisplayObject} [from] - The DisplayObject to calculate the global position from.
  138. * @param {PIXI.IPoint} [point] - A Point object in which to store the value, optional
  139. * (otherwise will create a new Point).
  140. * @param {boolean} [skipUpdate=false] - Should we skip the update transform
  141. * @return {PIXI.IPoint} A point object representing the position of this object
  142. */
  143. toLocal(position:Point, from:DisplayObject, point:Point, skipUpdate:boolean) {
  144. if (from) {
  145. position = from.toGlobal(position, point, skipUpdate);
  146. }
  147. if (!skipUpdate) {
  148. this._recursivePostUpdateTransform();
  149. // this parent check is for just in case the item is a root object.
  150. // If it is we need to give it a temporary parent so that displayObjectUpdateTransform works correctly
  151. // this is mainly to avoid a parent check in the main loop. Every little helps for performance :)
  152. if (!this.parent) {
  153. this.parent = this._tempDisplayObjectParent;
  154. this.displayObjectUpdateTransform();
  155. this.parent = undefined;
  156. } else {
  157. this.displayObjectUpdateTransform();
  158. }
  159. }
  160. // simply apply the matrix..
  161. return this.worldTransform.applyInverse(position, point);
  162. }
  163. setParent(container:any) {
  164. if (!container || !container.addChild) {
  165. throw new Error("setParent: Argument must be a Container");
  166. }
  167. container.addChild(this);
  168. return container;
  169. }
  170. setTransform(
  171. x = 0,
  172. y = 0,
  173. scaleX = 1,
  174. scaleY = 1,
  175. rotation = 0,
  176. skewX = 0,
  177. skewY = 0,
  178. pivotX = 0,
  179. pivotY = 0
  180. ) {
  181. this.position.x = x;
  182. this.position.y = y;
  183. this.scale.x = !scaleX ? 1 : scaleX;
  184. this.scale.y = !scaleY ? 1 : scaleY;
  185. this.rotation = rotation;
  186. this.skew.x = skewX;
  187. this.skew.y = skewY;
  188. this.pivot.x = pivotX;
  189. this.pivot.y = pivotY;
  190. return this;
  191. }
  192. /**
  193. * Base destroy method for generic display objects. This will automatically
  194. * remove the display object from its parent Container as well as remove
  195. * all current event listeners and internal references. Do not use a DisplayObject
  196. * after calling `destroy()`.
  197. *
  198. */
  199. sortDirty = false;
  200. destroy() {
  201. if (this.parent) {
  202. this.parent.removeChild(this);
  203. }
  204. this.parent = undefined;
  205. this._destroyed = true;
  206. }
  207. /**
  208. * The position of the displayObject on the x axis relative to the local coordinates of the parent.
  209. * An alias to position.x
  210. */
  211. get x() {
  212. return this.position.x;
  213. }
  214. set x( value // eslint-disable-line require-jsdoc
  215. ) {
  216. this.transform.position.x = value;
  217. }
  218. /**
  219. * The position of the displayObject on the y axis relative to the local coordinates of the parent.
  220. * An alias to position.y
  221. *
  222. * @member {number}
  223. */
  224. get y() {
  225. return this.position.y;
  226. }
  227. set y(
  228. value // eslint-disable-line require-jsdoc
  229. ) {
  230. this.transform.position.y = value;
  231. }
  232. /**
  233. * Current transform of the object based on world (parent) factors.
  234. *
  235. * @member {PIXI.Matrix}
  236. * @readonly
  237. */
  238. get worldTransform() {
  239. return this.transform.worldTransform;
  240. }
  241. get localTransform() {
  242. return this.transform.localTransform;
  243. }
  244. /**
  245. * The coordinate of the object relative to the local coordinates of the parent.
  246. * Assignment by value since pixi-v4.
  247. *
  248. * @member {PIXI.IPoint}
  249. */
  250. get position() {
  251. return this.transform.position;
  252. }
  253. set position(
  254. value // eslint-disable-line require-jsdoc
  255. ) {
  256. this.transform.position.copyFrom(value);
  257. }
  258. /**
  259. * The scale factor of the object.
  260. * Assignment by value since pixi-v4.
  261. *
  262. * @member {PIXI.IPoint}
  263. */
  264. get scale() {
  265. return this.transform.scale;
  266. }
  267. set scale(
  268. value // eslint-disable-line require-jsdoc
  269. ) {
  270. this.transform.scale.copyFrom(value);
  271. }
  272. /**
  273. * The pivot point of the displayObject that it rotates around.
  274. * Assignment by value since pixi-v4.
  275. *
  276. * @member {PIXI.IPoint}
  277. */
  278. get pivot() {
  279. return this.transform.pivot;
  280. }
  281. set pivot( value ) {
  282. this.transform.pivot.copyFrom(value);
  283. }
  284. //保持当前对象位置不变,修改pivot
  285. setPivotWorldNoChange(x:number, y:number) {
  286. const targetPivot = { x, y };
  287. const point = { x: targetPivot.x, y: targetPivot.y };
  288. this.worldTransform.apply(point as any, point as any);
  289. this.pivot = targetPivot as any;
  290. this.position.x = point.x;
  291. this.position.y = point.y;
  292. this.updateTransform();
  293. }
  294. /**
  295. * The skew factor for the object in radians.
  296. * Assignment by value since pixi-v4.
  297. */
  298. get skew() {
  299. return this.transform.skew;
  300. }
  301. set skew(
  302. value // eslint-disable-line require-jsdoc
  303. ) {
  304. this.transform.skew.copyFrom(value);
  305. }
  306. /**
  307. * The rotation of the object in radians.
  308. * 'rotation' and 'angle' have the same effect on a display object; rotation is in radians, angle is in degrees.
  309. */
  310. get rotation() {
  311. return this.transform.rotation;
  312. }
  313. set rotation(
  314. value // eslint-disable-line require-jsdoc
  315. ) {
  316. this.transform.rotation = value;
  317. }
  318. /**
  319. * The angle of the object in degrees.
  320. * 'rotation' and 'angle' have the same effect on a display object; rotation is in radians, angle is in degrees.
  321. *
  322. * @member {number}
  323. */
  324. get angle() {
  325. return this.transform.rotation * RAD_TO_DEG;
  326. }
  327. set angle(
  328. value // eslint-disable-line require-jsdoc
  329. ) {
  330. this.transform.rotation = value * DEG_TO_RAD;
  331. }
  332. /**
  333. * The zIndex of the displayObject.
  334. * If a container has the sortableChildren property set to true, children will be automatically
  335. * sorted by zIndex value; a higher value will mean it will be moved towards the end of the array,
  336. * and thus rendered on top of other displayObjects within the same container.
  337. *
  338. * @member {number}
  339. */
  340. get zIndex() {
  341. return this._zIndex;
  342. }
  343. set zIndex(
  344. value // eslint-disable-line require-jsdoc
  345. ) {
  346. this._zIndex = value;
  347. if (this.parent) {
  348. this.parent.sortDirty = true;
  349. }
  350. }
  351. get worldVisible() {
  352. let item:any = this;
  353. do {
  354. if (!item.visible) {
  355. return false;
  356. }
  357. item = item.parent;
  358. } while (item);
  359. return true;
  360. }
  361. getGlobalPosition( point = new Point(),skipUpdate = false ) {
  362. if (this.parent) {
  363. this.parent.toGlobal(this.position, point, skipUpdate);
  364. } else {
  365. point.x = this.position.x;
  366. point.y = this.position.y;
  367. }
  368. return point;
  369. }
  370. displayObjectUpdateTransform = this.updateTransform;
  371. }