interaction.mjs 85 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836
  1. /*!
  2. * @pixi/interaction - v6.5.10
  3. * Compiled Thu, 06 Jul 2023 15:25:11 UTC
  4. *
  5. * @pixi/interaction is licensed under the MIT License.
  6. * http://www.opensource.org/licenses/mit-license
  7. */
  8. import { Point } from '@pixi/math';
  9. import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker';
  10. import { DisplayObject, TemporaryDisplayObject } from '@pixi/display';
  11. import { EventEmitter } from '@pixi/utils';
  12. import { ExtensionType } from '@pixi/core';
  13. /**
  14. * Holds all information related to an Interaction event
  15. * @memberof PIXI
  16. */
  17. var InteractionData = /** @class */ (function () {
  18. function InteractionData() {
  19. /**
  20. * Pressure applied by the pointing device during the event. A Touch's force property
  21. * will be represented by this value.
  22. * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
  23. */
  24. this.pressure = 0;
  25. /**
  26. * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
  27. * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
  28. */
  29. this.rotationAngle = 0;
  30. /**
  31. * Twist of a stylus pointer.
  32. * @see https://w3c.github.io/pointerevents/#pointerevent-interface
  33. */
  34. this.twist = 0;
  35. /**
  36. * Barrel pressure on a stylus pointer.
  37. * @see https://w3c.github.io/pointerevents/#pointerevent-interface
  38. */
  39. this.tangentialPressure = 0;
  40. this.global = new Point();
  41. this.target = null;
  42. this.originalEvent = null;
  43. this.identifier = null;
  44. this.isPrimary = false;
  45. this.button = 0;
  46. this.buttons = 0;
  47. this.width = 0;
  48. this.height = 0;
  49. this.tiltX = 0;
  50. this.tiltY = 0;
  51. this.pointerType = null;
  52. this.pressure = 0;
  53. this.rotationAngle = 0;
  54. this.twist = 0;
  55. this.tangentialPressure = 0;
  56. }
  57. Object.defineProperty(InteractionData.prototype, "pointerId", {
  58. /**
  59. * The unique identifier of the pointer. It will be the same as `identifier`.
  60. * @readonly
  61. * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
  62. */
  63. get: function () {
  64. return this.identifier;
  65. },
  66. enumerable: false,
  67. configurable: true
  68. });
  69. /**
  70. * This will return the local coordinates of the specified displayObject for this InteractionData
  71. * @param displayObject - The DisplayObject that you would like the local
  72. * coords off
  73. * @param point - A Point object in which to store the value, optional (otherwise
  74. * will create a new point)
  75. * @param globalPos - A Point object containing your custom global coords, optional
  76. * (otherwise will use the current global coords)
  77. * @returns - A point containing the coordinates of the InteractionData position relative
  78. * to the DisplayObject
  79. */
  80. InteractionData.prototype.getLocalPosition = function (displayObject, point, globalPos) {
  81. return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
  82. };
  83. /**
  84. * Copies properties from normalized event data.
  85. * @param {Touch|MouseEvent|PointerEvent} event - The normalized event data
  86. */
  87. InteractionData.prototype.copyEvent = function (event) {
  88. // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
  89. // it with "false" on later events when our shim for it on touch events might not be
  90. // accurate
  91. if ('isPrimary' in event && event.isPrimary) {
  92. this.isPrimary = true;
  93. }
  94. this.button = 'button' in event && event.button;
  95. // event.buttons is not available in all browsers (ie. Safari), but it does have a non-standard
  96. // event.which property instead, which conveys the same information.
  97. var buttons = 'buttons' in event && event.buttons;
  98. this.buttons = Number.isInteger(buttons) ? buttons : 'which' in event && event.which;
  99. this.width = 'width' in event && event.width;
  100. this.height = 'height' in event && event.height;
  101. this.tiltX = 'tiltX' in event && event.tiltX;
  102. this.tiltY = 'tiltY' in event && event.tiltY;
  103. this.pointerType = 'pointerType' in event && event.pointerType;
  104. this.pressure = 'pressure' in event && event.pressure;
  105. this.rotationAngle = 'rotationAngle' in event && event.rotationAngle;
  106. this.twist = ('twist' in event && event.twist) || 0;
  107. this.tangentialPressure = ('tangentialPressure' in event && event.tangentialPressure) || 0;
  108. };
  109. /** Resets the data for pooling. */
  110. InteractionData.prototype.reset = function () {
  111. // isPrimary is the only property that we really need to reset - everything else is
  112. // guaranteed to be overwritten
  113. this.isPrimary = false;
  114. };
  115. return InteractionData;
  116. }());
  117. /*! *****************************************************************************
  118. Copyright (c) Microsoft Corporation.
  119. Permission to use, copy, modify, and/or distribute this software for any
  120. purpose with or without fee is hereby granted.
  121. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  122. REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  123. AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  124. INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  125. LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  126. OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  127. PERFORMANCE OF THIS SOFTWARE.
  128. ***************************************************************************** */
  129. /* global Reflect, Promise */
  130. var extendStatics = function(d, b) {
  131. extendStatics = Object.setPrototypeOf ||
  132. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  133. function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
  134. return extendStatics(d, b);
  135. };
  136. function __extends(d, b) {
  137. extendStatics(d, b);
  138. function __() { this.constructor = d; }
  139. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  140. }
  141. /**
  142. * Event class that mimics native DOM events.
  143. * @memberof PIXI
  144. */
  145. var InteractionEvent = /** @class */ (function () {
  146. function InteractionEvent() {
  147. this.stopped = false;
  148. this.stopsPropagatingAt = null;
  149. this.stopPropagationHint = false;
  150. this.target = null;
  151. this.currentTarget = null;
  152. this.type = null;
  153. this.data = null;
  154. }
  155. /** Prevents event from reaching any objects other than the current object. */
  156. InteractionEvent.prototype.stopPropagation = function () {
  157. this.stopped = true;
  158. this.stopPropagationHint = true;
  159. this.stopsPropagatingAt = this.currentTarget;
  160. };
  161. /** Resets the event. */
  162. InteractionEvent.prototype.reset = function () {
  163. this.stopped = false;
  164. this.stopsPropagatingAt = null;
  165. this.stopPropagationHint = false;
  166. this.currentTarget = null;
  167. this.target = null;
  168. };
  169. return InteractionEvent;
  170. }());
  171. /**
  172. * DisplayObjects with the {@link PIXI.interactiveTarget} mixin use this class to track interactions
  173. * @class
  174. * @private
  175. * @memberof PIXI
  176. */
  177. var InteractionTrackingData = /** @class */ (function () {
  178. /**
  179. * @param {number} pointerId - Unique pointer id of the event
  180. * @private
  181. */
  182. function InteractionTrackingData(pointerId) {
  183. this._pointerId = pointerId;
  184. this._flags = InteractionTrackingData.FLAGS.NONE;
  185. }
  186. /**
  187. *
  188. * @private
  189. * @param {number} flag - The interaction flag to set
  190. * @param {boolean} yn - Should the flag be set or unset
  191. */
  192. InteractionTrackingData.prototype._doSet = function (flag, yn) {
  193. if (yn) {
  194. this._flags = this._flags | flag;
  195. }
  196. else {
  197. this._flags = this._flags & (~flag);
  198. }
  199. };
  200. Object.defineProperty(InteractionTrackingData.prototype, "pointerId", {
  201. /**
  202. * Unique pointer id of the event
  203. * @readonly
  204. * @private
  205. * @member {number}
  206. */
  207. get: function () {
  208. return this._pointerId;
  209. },
  210. enumerable: false,
  211. configurable: true
  212. });
  213. Object.defineProperty(InteractionTrackingData.prototype, "flags", {
  214. /**
  215. * State of the tracking data, expressed as bit flags
  216. * @private
  217. * @member {number}
  218. */
  219. get: function () {
  220. return this._flags;
  221. },
  222. set: function (flags) {
  223. this._flags = flags;
  224. },
  225. enumerable: false,
  226. configurable: true
  227. });
  228. Object.defineProperty(InteractionTrackingData.prototype, "none", {
  229. /**
  230. * Is the tracked event inactive (not over or down)?
  231. * @private
  232. * @member {number}
  233. */
  234. get: function () {
  235. return this._flags === InteractionTrackingData.FLAGS.NONE;
  236. },
  237. enumerable: false,
  238. configurable: true
  239. });
  240. Object.defineProperty(InteractionTrackingData.prototype, "over", {
  241. /**
  242. * Is the tracked event over the DisplayObject?
  243. * @private
  244. * @member {boolean}
  245. */
  246. get: function () {
  247. return (this._flags & InteractionTrackingData.FLAGS.OVER) !== 0;
  248. },
  249. set: function (yn) {
  250. this._doSet(InteractionTrackingData.FLAGS.OVER, yn);
  251. },
  252. enumerable: false,
  253. configurable: true
  254. });
  255. Object.defineProperty(InteractionTrackingData.prototype, "rightDown", {
  256. /**
  257. * Did the right mouse button come down in the DisplayObject?
  258. * @private
  259. * @member {boolean}
  260. */
  261. get: function () {
  262. return (this._flags & InteractionTrackingData.FLAGS.RIGHT_DOWN) !== 0;
  263. },
  264. set: function (yn) {
  265. this._doSet(InteractionTrackingData.FLAGS.RIGHT_DOWN, yn);
  266. },
  267. enumerable: false,
  268. configurable: true
  269. });
  270. Object.defineProperty(InteractionTrackingData.prototype, "leftDown", {
  271. /**
  272. * Did the left mouse button come down in the DisplayObject?
  273. * @private
  274. * @member {boolean}
  275. */
  276. get: function () {
  277. return (this._flags & InteractionTrackingData.FLAGS.LEFT_DOWN) !== 0;
  278. },
  279. set: function (yn) {
  280. this._doSet(InteractionTrackingData.FLAGS.LEFT_DOWN, yn);
  281. },
  282. enumerable: false,
  283. configurable: true
  284. });
  285. InteractionTrackingData.FLAGS = Object.freeze({
  286. NONE: 0,
  287. OVER: 1 << 0,
  288. LEFT_DOWN: 1 << 1,
  289. RIGHT_DOWN: 1 << 2,
  290. });
  291. return InteractionTrackingData;
  292. }());
  293. /**
  294. * Strategy how to search through stage tree for interactive objects
  295. * @memberof PIXI
  296. */
  297. var TreeSearch = /** @class */ (function () {
  298. function TreeSearch() {
  299. this._tempPoint = new Point();
  300. }
  301. /**
  302. * Recursive implementation for findHit
  303. * @private
  304. * @param interactionEvent - event containing the point that
  305. * is tested for collision
  306. * @param displayObject - the displayObject
  307. * that will be hit test (recursively crawls its children)
  308. * @param func - the function that will be called on each interactive object. The
  309. * interactionEvent, displayObject and hit will be passed to the function
  310. * @param hitTest - this indicates if the objects inside should be hit test against the point
  311. * @param interactive - Whether the displayObject is interactive
  312. * @returns - Returns true if the displayObject hit the point
  313. */
  314. TreeSearch.prototype.recursiveFindHit = function (interactionEvent, displayObject, func, hitTest, interactive) {
  315. var _a;
  316. if (!displayObject || !displayObject.visible) {
  317. return false;
  318. }
  319. var point = interactionEvent.data.global;
  320. // Took a little while to rework this function correctly! But now it is done and nice and optimized! ^_^
  321. //
  322. // This function will now loop through all objects and then only hit test the objects it HAS
  323. // to, not all of them. MUCH faster..
  324. // An object will be hit test if the following is true:
  325. //
  326. // 1: It is interactive.
  327. // 2: It belongs to a parent that is interactive AND one of the parents children have not already been hit.
  328. //
  329. // As another little optimization once an interactive object has been hit we can carry on
  330. // through the scenegraph, but we know that there will be no more hits! So we can avoid extra hit tests
  331. // A final optimization is that an object is not hit test directly if a child has already been hit.
  332. interactive = displayObject.interactive || interactive;
  333. var hit = false;
  334. var interactiveParent = interactive;
  335. // Flag here can set to false if the event is outside the parents hitArea or mask
  336. var hitTestChildren = true;
  337. // If there is a hitArea, no need to test against anything else if the pointer is not within the hitArea
  338. // There is also no longer a need to hitTest children.
  339. if (displayObject.hitArea) {
  340. if (hitTest) {
  341. displayObject.worldTransform.applyInverse(point, this._tempPoint);
  342. if (!displayObject.hitArea.contains(this._tempPoint.x, this._tempPoint.y)) {
  343. hitTest = false;
  344. hitTestChildren = false;
  345. }
  346. else {
  347. hit = true;
  348. }
  349. }
  350. interactiveParent = false;
  351. }
  352. // If there is a mask, no need to hitTest against anything else if the pointer is not within the mask.
  353. // We still want to hitTestChildren, however, to ensure a mouseout can still be generated.
  354. // https://github.com/pixijs/pixi.js/issues/5135
  355. else if (displayObject._mask) {
  356. if (hitTest) {
  357. var maskObject = (displayObject._mask.isMaskData
  358. ? displayObject._mask.maskObject : displayObject._mask);
  359. if (maskObject && !((_a = maskObject.containsPoint) === null || _a === void 0 ? void 0 : _a.call(maskObject, point))) {
  360. hitTest = false;
  361. }
  362. }
  363. }
  364. // ** FREE TIP **! If an object is not interactive or has no buttons in it
  365. // (such as a game scene!) set interactiveChildren to false for that displayObject.
  366. // This will allow PixiJS to completely ignore and bypass checking the displayObjects children.
  367. if (hitTestChildren && displayObject.interactiveChildren && displayObject.children) {
  368. var children = displayObject.children;
  369. for (var i = children.length - 1; i >= 0; i--) {
  370. var child = children[i];
  371. // time to get recursive.. if this function will return if something is hit..
  372. var childHit = this.recursiveFindHit(interactionEvent, child, func, hitTest, interactiveParent);
  373. if (childHit) {
  374. // its a good idea to check if a child has lost its parent.
  375. // this means it has been removed whilst looping so its best
  376. if (!child.parent) {
  377. continue;
  378. }
  379. // we no longer need to hit test any more objects in this container as we we
  380. // now know the parent has been hit
  381. interactiveParent = false;
  382. // If the child is interactive , that means that the object hit was actually
  383. // interactive and not just the child of an interactive object.
  384. // This means we no longer need to hit test anything else. We still need to run
  385. // through all objects, but we don't need to perform any hit tests.
  386. if (childHit) {
  387. if (interactionEvent.target) {
  388. hitTest = false;
  389. }
  390. hit = true;
  391. }
  392. }
  393. }
  394. }
  395. // no point running this if the item is not interactive or does not have an interactive parent.
  396. if (interactive) {
  397. // if we are hit testing (as in we have no hit any objects yet)
  398. // We also don't need to worry about hit testing if once of the displayObjects children
  399. // has already been hit - but only if it was interactive, otherwise we need to keep
  400. // looking for an interactive child, just in case we hit one
  401. if (hitTest && !interactionEvent.target) {
  402. // already tested against hitArea if it is defined
  403. if (!displayObject.hitArea && displayObject.containsPoint) {
  404. if (displayObject.containsPoint(point)) {
  405. hit = true;
  406. }
  407. }
  408. }
  409. if (displayObject.interactive) {
  410. if (hit && !interactionEvent.target) {
  411. interactionEvent.target = displayObject;
  412. }
  413. if (func) {
  414. func(interactionEvent, displayObject, !!hit);
  415. }
  416. }
  417. }
  418. return hit;
  419. };
  420. /**
  421. * This function is provides a neat way of crawling through the scene graph and running a
  422. * specified function on all interactive objects it finds. It will also take care of hit
  423. * testing the interactive objects and passes the hit across in the function.
  424. * @private
  425. * @param interactionEvent - event containing the point that
  426. * is tested for collision
  427. * @param displayObject - the displayObject
  428. * that will be hit test (recursively crawls its children)
  429. * @param func - the function that will be called on each interactive object. The
  430. * interactionEvent, displayObject and hit will be passed to the function
  431. * @param hitTest - this indicates if the objects inside should be hit test against the point
  432. * @returns - Returns true if the displayObject hit the point
  433. */
  434. TreeSearch.prototype.findHit = function (interactionEvent, displayObject, func, hitTest) {
  435. this.recursiveFindHit(interactionEvent, displayObject, func, hitTest, false);
  436. };
  437. return TreeSearch;
  438. }());
  439. /**
  440. * Interface for classes that represent a hit area.
  441. *
  442. * It is implemented by the following classes:
  443. * - {@link PIXI.Circle}
  444. * - {@link PIXI.Ellipse}
  445. * - {@link PIXI.Polygon}
  446. * - {@link PIXI.RoundedRectangle}
  447. * @interface IHitArea
  448. * @memberof PIXI
  449. */
  450. /**
  451. * Checks whether the x and y coordinates given are contained within this area
  452. * @method
  453. * @name contains
  454. * @memberof PIXI.IHitArea#
  455. * @param {number} x - The X coordinate of the point to test
  456. * @param {number} y - The Y coordinate of the point to test
  457. * @returns {boolean} Whether the x/y coordinates are within this area
  458. */
  459. /**
  460. * Default property values of interactive objects
  461. * Used by {@link PIXI.InteractionManager} to automatically give all DisplayObjects these properties
  462. * @private
  463. * @name interactiveTarget
  464. * @type {object}
  465. * @memberof PIXI
  466. * @example
  467. * function MyObject() {}
  468. *
  469. * Object.assign(
  470. * DisplayObject.prototype,
  471. * PIXI.interactiveTarget
  472. * );
  473. */
  474. var interactiveTarget = {
  475. interactive: false,
  476. interactiveChildren: true,
  477. hitArea: null,
  478. /**
  479. * If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
  480. * Setting this changes the 'cursor' property to `'pointer'`.
  481. * @example
  482. * const sprite = new PIXI.Sprite(texture);
  483. * sprite.interactive = true;
  484. * sprite.buttonMode = true;
  485. * @member {boolean}
  486. * @memberof PIXI.DisplayObject#
  487. */
  488. get buttonMode() {
  489. return this.cursor === 'pointer';
  490. },
  491. set buttonMode(value) {
  492. if (value) {
  493. this.cursor = 'pointer';
  494. }
  495. else if (this.cursor === 'pointer') {
  496. this.cursor = null;
  497. }
  498. },
  499. /**
  500. * This defines what cursor mode is used when the mouse cursor
  501. * is hovered over the displayObject.
  502. * @example
  503. * const sprite = new PIXI.Sprite(texture);
  504. * sprite.interactive = true;
  505. * sprite.cursor = 'wait';
  506. * @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
  507. * @member {string}
  508. * @memberof PIXI.DisplayObject#
  509. */
  510. cursor: null,
  511. /**
  512. * Internal set of all active pointers, by identifier
  513. * @member {Map<number, InteractionTrackingData>}
  514. * @memberof PIXI.DisplayObject#
  515. * @private
  516. */
  517. get trackedPointers() {
  518. if (this._trackedPointers === undefined)
  519. { this._trackedPointers = {}; }
  520. return this._trackedPointers;
  521. },
  522. /**
  523. * Map of all tracked pointers, by identifier. Use trackedPointers to access.
  524. * @private
  525. * @type {Map<number, InteractionTrackingData>}
  526. */
  527. _trackedPointers: undefined,
  528. };
  529. // Mix interactiveTarget into DisplayObject.prototype
  530. DisplayObject.mixin(interactiveTarget);
  531. var MOUSE_POINTER_ID = 1;
  532. // helpers for hitTest() - only used inside hitTest()
  533. var hitTestEvent = {
  534. target: null,
  535. data: {
  536. global: null,
  537. },
  538. };
  539. /**
  540. * The interaction manager deals with mouse, touch and pointer events.
  541. *
  542. * Any DisplayObject can be interactive if its `interactive` property is set to true.
  543. *
  544. * This manager also supports multitouch.
  545. *
  546. * An instance of this class is automatically created by default, and can be found at `renderer.plugins.interaction`
  547. * @memberof PIXI
  548. */
  549. var InteractionManager = /** @class */ (function (_super) {
  550. __extends(InteractionManager, _super);
  551. /**
  552. * @param {PIXI.CanvasRenderer|PIXI.Renderer} renderer - A reference to the current renderer
  553. * @param options - The options for the manager.
  554. * @param {boolean} [options.autoPreventDefault=true] - Should the manager automatically prevent default browser actions.
  555. * @param {number} [options.interactionFrequency=10] - Maximum frequency (ms) at pointer over/out states will be checked.
  556. * @param {number} [options.useSystemTicker=true] - Whether to add {@link tickerUpdate} to {@link PIXI.Ticker.system}.
  557. */
  558. function InteractionManager(renderer, options) {
  559. var _this = _super.call(this) || this;
  560. options = options || {};
  561. _this.renderer = renderer;
  562. _this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true;
  563. _this.interactionFrequency = options.interactionFrequency || 10;
  564. _this.mouse = new InteractionData();
  565. _this.mouse.identifier = MOUSE_POINTER_ID;
  566. // setting the mouse to start off far off screen will mean that mouse over does
  567. // not get called before we even move the mouse.
  568. _this.mouse.global.set(-999999);
  569. _this.activeInteractionData = {};
  570. _this.activeInteractionData[MOUSE_POINTER_ID] = _this.mouse;
  571. _this.interactionDataPool = [];
  572. _this.eventData = new InteractionEvent();
  573. _this.interactionDOMElement = null;
  574. _this.moveWhenInside = false;
  575. _this.eventsAdded = false;
  576. _this.tickerAdded = false;
  577. _this.mouseOverRenderer = !('PointerEvent' in globalThis);
  578. _this.supportsTouchEvents = 'ontouchstart' in globalThis;
  579. _this.supportsPointerEvents = !!globalThis.PointerEvent;
  580. // this will make it so that you don't have to call bind all the time
  581. _this.onPointerUp = _this.onPointerUp.bind(_this);
  582. _this.processPointerUp = _this.processPointerUp.bind(_this);
  583. _this.onPointerCancel = _this.onPointerCancel.bind(_this);
  584. _this.processPointerCancel = _this.processPointerCancel.bind(_this);
  585. _this.onPointerDown = _this.onPointerDown.bind(_this);
  586. _this.processPointerDown = _this.processPointerDown.bind(_this);
  587. _this.onPointerMove = _this.onPointerMove.bind(_this);
  588. _this.processPointerMove = _this.processPointerMove.bind(_this);
  589. _this.onPointerOut = _this.onPointerOut.bind(_this);
  590. _this.processPointerOverOut = _this.processPointerOverOut.bind(_this);
  591. _this.onPointerOver = _this.onPointerOver.bind(_this);
  592. _this.cursorStyles = {
  593. default: 'inherit',
  594. pointer: 'pointer',
  595. };
  596. _this.currentCursorMode = null;
  597. _this.cursor = null;
  598. _this.resolution = 1;
  599. _this.delayedEvents = [];
  600. _this.search = new TreeSearch();
  601. _this._tempDisplayObject = new TemporaryDisplayObject();
  602. _this._eventListenerOptions = { capture: true, passive: false };
  603. /**
  604. * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
  605. * object.
  606. * @event PIXI.InteractionManager#mousedown
  607. * @param {PIXI.InteractionEvent} event - Interaction event
  608. */
  609. /**
  610. * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
  611. * on the display object.
  612. * @event PIXI.InteractionManager#rightdown
  613. * @param {PIXI.InteractionEvent} event - Interaction event
  614. */
  615. /**
  616. * Fired when a pointer device button (usually a mouse left-button) is released over the display
  617. * object.
  618. * @event PIXI.InteractionManager#mouseup
  619. * @param {PIXI.InteractionEvent} event - Interaction event
  620. */
  621. /**
  622. * Fired when a pointer device secondary button (usually a mouse right-button) is released
  623. * over the display object.
  624. * @event PIXI.InteractionManager#rightup
  625. * @param {PIXI.InteractionEvent} event - Interaction event
  626. */
  627. /**
  628. * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
  629. * the display object.
  630. * @event PIXI.InteractionManager#click
  631. * @param {PIXI.InteractionEvent} event - Interaction event
  632. */
  633. /**
  634. * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
  635. * and released on the display object.
  636. * @event PIXI.InteractionManager#rightclick
  637. * @param {PIXI.InteractionEvent} event - Interaction event
  638. */
  639. /**
  640. * Fired when a pointer device button (usually a mouse left-button) is released outside the
  641. * display object that initially registered a
  642. * [mousedown]{@link PIXI.InteractionManager#event:mousedown}.
  643. * @event PIXI.InteractionManager#mouseupoutside
  644. * @param {PIXI.InteractionEvent} event - Interaction event
  645. */
  646. /**
  647. * Fired when a pointer device secondary button (usually a mouse right-button) is released
  648. * outside the display object that initially registered a
  649. * [rightdown]{@link PIXI.InteractionManager#event:rightdown}.
  650. * @event PIXI.InteractionManager#rightupoutside
  651. * @param {PIXI.InteractionEvent} event - Interaction event
  652. */
  653. /**
  654. * Fired when a pointer device (usually a mouse) is moved while over the display object
  655. * @event PIXI.InteractionManager#mousemove
  656. * @param {PIXI.InteractionEvent} event - Interaction event
  657. */
  658. /**
  659. * Fired when a pointer device (usually a mouse) is moved onto the display object
  660. * @event PIXI.InteractionManager#mouseover
  661. * @param {PIXI.InteractionEvent} event - Interaction event
  662. */
  663. /**
  664. * Fired when a pointer device (usually a mouse) is moved off the display object
  665. * @event PIXI.InteractionManager#mouseout
  666. * @param {PIXI.InteractionEvent} event - Interaction event
  667. */
  668. /**
  669. * Fired when a pointer device button is pressed on the display object.
  670. * @event PIXI.InteractionManager#pointerdown
  671. * @param {PIXI.InteractionEvent} event - Interaction event
  672. */
  673. /**
  674. * Fired when a pointer device button is released over the display object.
  675. * Not always fired when some buttons are held down while others are released. In those cases,
  676. * use [mousedown]{@link PIXI.InteractionManager#event:mousedown} and
  677. * [mouseup]{@link PIXI.InteractionManager#event:mouseup} instead.
  678. * @event PIXI.InteractionManager#pointerup
  679. * @param {PIXI.InteractionEvent} event - Interaction event
  680. */
  681. /**
  682. * Fired when the operating system cancels a pointer event
  683. * @event PIXI.InteractionManager#pointercancel
  684. * @param {PIXI.InteractionEvent} event - Interaction event
  685. */
  686. /**
  687. * Fired when a pointer device button is pressed and released on the display object.
  688. * @event PIXI.InteractionManager#pointertap
  689. * @param {PIXI.InteractionEvent} event - Interaction event
  690. */
  691. /**
  692. * Fired when a pointer device button is released outside the display object that initially
  693. * registered a [pointerdown]{@link PIXI.InteractionManager#event:pointerdown}.
  694. * @event PIXI.InteractionManager#pointerupoutside
  695. * @param {PIXI.InteractionEvent} event - Interaction event
  696. */
  697. /**
  698. * Fired when a pointer device is moved while over the display object
  699. * @event PIXI.InteractionManager#pointermove
  700. * @param {PIXI.InteractionEvent} event - Interaction event
  701. */
  702. /**
  703. * Fired when a pointer device is moved onto the display object
  704. * @event PIXI.InteractionManager#pointerover
  705. * @param {PIXI.InteractionEvent} event - Interaction event
  706. */
  707. /**
  708. * Fired when a pointer device is moved off the display object
  709. * @event PIXI.InteractionManager#pointerout
  710. * @param {PIXI.InteractionEvent} event - Interaction event
  711. */
  712. /**
  713. * Fired when a touch point is placed on the display object.
  714. * @event PIXI.InteractionManager#touchstart
  715. * @param {PIXI.InteractionEvent} event - Interaction event
  716. */
  717. /**
  718. * Fired when a touch point is removed from the display object.
  719. * @event PIXI.InteractionManager#touchend
  720. * @param {PIXI.InteractionEvent} event - Interaction event
  721. */
  722. /**
  723. * Fired when the operating system cancels a touch
  724. * @event PIXI.InteractionManager#touchcancel
  725. * @param {PIXI.InteractionEvent} event - Interaction event
  726. */
  727. /**
  728. * Fired when a touch point is placed and removed from the display object.
  729. * @event PIXI.InteractionManager#tap
  730. * @param {PIXI.InteractionEvent} event - Interaction event
  731. */
  732. /**
  733. * Fired when a touch point is removed outside of the display object that initially
  734. * registered a [touchstart]{@link PIXI.InteractionManager#event:touchstart}.
  735. * @event PIXI.InteractionManager#touchendoutside
  736. * @param {PIXI.InteractionEvent} event - Interaction event
  737. */
  738. /**
  739. * Fired when a touch point is moved along the display object.
  740. * @event PIXI.InteractionManager#touchmove
  741. * @param {PIXI.InteractionEvent} event - Interaction event
  742. */
  743. /**
  744. * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
  745. * object. DisplayObject's `interactive` property must be set to `true` to fire event.
  746. *
  747. * This comes from the @pixi/interaction package.
  748. * @event PIXI.DisplayObject#mousedown
  749. * @param {PIXI.InteractionEvent} event - Interaction event
  750. */
  751. /**
  752. * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
  753. * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
  754. *
  755. * This comes from the @pixi/interaction package.
  756. * @event PIXI.DisplayObject#rightdown
  757. * @param {PIXI.InteractionEvent} event - Interaction event
  758. */
  759. /**
  760. * Fired when a pointer device button (usually a mouse left-button) is released over the display
  761. * object. DisplayObject's `interactive` property must be set to `true` to fire event.
  762. *
  763. * This comes from the @pixi/interaction package.
  764. * @event PIXI.DisplayObject#mouseup
  765. * @param {PIXI.InteractionEvent} event - Interaction event
  766. */
  767. /**
  768. * Fired when a pointer device secondary button (usually a mouse right-button) is released
  769. * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
  770. *
  771. * This comes from the @pixi/interaction package.
  772. * @event PIXI.DisplayObject#rightup
  773. * @param {PIXI.InteractionEvent} event - Interaction event
  774. */
  775. /**
  776. * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
  777. * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
  778. *
  779. * This comes from the @pixi/interaction package.
  780. * @event PIXI.DisplayObject#click
  781. * @param {PIXI.InteractionEvent} event - Interaction event
  782. */
  783. /**
  784. * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
  785. * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
  786. *
  787. * This comes from the @pixi/interaction package.
  788. * @event PIXI.DisplayObject#rightclick
  789. * @param {PIXI.InteractionEvent} event - Interaction event
  790. */
  791. /**
  792. * Fired when a pointer device button (usually a mouse left-button) is released outside the
  793. * display object that initially registered a
  794. * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
  795. * DisplayObject's `interactive` property must be set to `true` to fire event.
  796. *
  797. * This comes from the @pixi/interaction package.
  798. * @event PIXI.DisplayObject#mouseupoutside
  799. * @param {PIXI.InteractionEvent} event - Interaction event
  800. */
  801. /**
  802. * Fired when a pointer device secondary button (usually a mouse right-button) is released
  803. * outside the display object that initially registered a
  804. * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
  805. * DisplayObject's `interactive` property must be set to `true` to fire event.
  806. *
  807. * This comes from the @pixi/interaction package.
  808. * @event PIXI.DisplayObject#rightupoutside
  809. * @param {PIXI.InteractionEvent} event - Interaction event
  810. */
  811. /**
  812. * Fired when a pointer device (usually a mouse) is moved while over the display object.
  813. * DisplayObject's `interactive` property must be set to `true` to fire event.
  814. *
  815. * This comes from the @pixi/interaction package.
  816. * @event PIXI.DisplayObject#mousemove
  817. * @param {PIXI.InteractionEvent} event - Interaction event
  818. */
  819. /**
  820. * Fired when a pointer device (usually a mouse) is moved onto the display object.
  821. * DisplayObject's `interactive` property must be set to `true` to fire event.
  822. *
  823. * This comes from the @pixi/interaction package.
  824. * @event PIXI.DisplayObject#mouseover
  825. * @param {PIXI.InteractionEvent} event - Interaction event
  826. */
  827. /**
  828. * Fired when a pointer device (usually a mouse) is moved off the display object.
  829. * DisplayObject's `interactive` property must be set to `true` to fire event.
  830. *
  831. * This comes from the @pixi/interaction package.
  832. * @event PIXI.DisplayObject#mouseout
  833. * @param {PIXI.InteractionEvent} event - Interaction event
  834. */
  835. /**
  836. * Fired when a pointer device button is pressed on the display object.
  837. * DisplayObject's `interactive` property must be set to `true` to fire event.
  838. *
  839. * This comes from the @pixi/interaction package.
  840. * @event PIXI.DisplayObject#pointerdown
  841. * @param {PIXI.InteractionEvent} event - Interaction event
  842. */
  843. /**
  844. * Fired when a pointer device button is released over the display object.
  845. * DisplayObject's `interactive` property must be set to `true` to fire event.
  846. *
  847. * This comes from the @pixi/interaction package.
  848. * @event PIXI.DisplayObject#pointerup
  849. * @param {PIXI.InteractionEvent} event - Interaction event
  850. */
  851. /**
  852. * Fired when the operating system cancels a pointer event.
  853. * DisplayObject's `interactive` property must be set to `true` to fire event.
  854. *
  855. * This comes from the @pixi/interaction package.
  856. * @event PIXI.DisplayObject#pointercancel
  857. * @param {PIXI.InteractionEvent} event - Interaction event
  858. */
  859. /**
  860. * Fired when a pointer device button is pressed and released on the display object.
  861. * DisplayObject's `interactive` property must be set to `true` to fire event.
  862. *
  863. * This comes from the @pixi/interaction package.
  864. * @event PIXI.DisplayObject#pointertap
  865. * @param {PIXI.InteractionEvent} event - Interaction event
  866. */
  867. /**
  868. * Fired when a pointer device button is released outside the display object that initially
  869. * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
  870. * DisplayObject's `interactive` property must be set to `true` to fire event.
  871. *
  872. * This comes from the @pixi/interaction package.
  873. * @event PIXI.DisplayObject#pointerupoutside
  874. * @param {PIXI.InteractionEvent} event - Interaction event
  875. */
  876. /**
  877. * Fired when a pointer device is moved while over the display object.
  878. * DisplayObject's `interactive` property must be set to `true` to fire event.
  879. *
  880. * This comes from the @pixi/interaction package.
  881. * @event PIXI.DisplayObject#pointermove
  882. * @param {PIXI.InteractionEvent} event - Interaction event
  883. */
  884. /**
  885. * Fired when a pointer device is moved onto the display object.
  886. * DisplayObject's `interactive` property must be set to `true` to fire event.
  887. *
  888. * This comes from the @pixi/interaction package.
  889. * @event PIXI.DisplayObject#pointerover
  890. * @param {PIXI.InteractionEvent} event - Interaction event
  891. */
  892. /**
  893. * Fired when a pointer device is moved off the display object.
  894. * DisplayObject's `interactive` property must be set to `true` to fire event.
  895. *
  896. * This comes from the @pixi/interaction package.
  897. * @event PIXI.DisplayObject#pointerout
  898. * @param {PIXI.InteractionEvent} event - Interaction event
  899. */
  900. /**
  901. * Fired when a touch point is placed on the display object.
  902. * DisplayObject's `interactive` property must be set to `true` to fire event.
  903. *
  904. * This comes from the @pixi/interaction package.
  905. * @event PIXI.DisplayObject#touchstart
  906. * @param {PIXI.InteractionEvent} event - Interaction event
  907. */
  908. /**
  909. * Fired when a touch point is removed from the display object.
  910. * DisplayObject's `interactive` property must be set to `true` to fire event.
  911. *
  912. * This comes from the @pixi/interaction package.
  913. * @event PIXI.DisplayObject#touchend
  914. * @param {PIXI.InteractionEvent} event - Interaction event
  915. */
  916. /**
  917. * Fired when the operating system cancels a touch.
  918. * DisplayObject's `interactive` property must be set to `true` to fire event.
  919. *
  920. * This comes from the @pixi/interaction package.
  921. * @event PIXI.DisplayObject#touchcancel
  922. * @param {PIXI.InteractionEvent} event - Interaction event
  923. */
  924. /**
  925. * Fired when a touch point is placed and removed from the display object.
  926. * DisplayObject's `interactive` property must be set to `true` to fire event.
  927. *
  928. * This comes from the @pixi/interaction package.
  929. * @event PIXI.DisplayObject#tap
  930. * @param {PIXI.InteractionEvent} event - Interaction event
  931. */
  932. /**
  933. * Fired when a touch point is removed outside of the display object that initially
  934. * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
  935. * DisplayObject's `interactive` property must be set to `true` to fire event.
  936. *
  937. * This comes from the @pixi/interaction package.
  938. * @event PIXI.DisplayObject#touchendoutside
  939. * @param {PIXI.InteractionEvent} event - Interaction event
  940. */
  941. /**
  942. * Fired when a touch point is moved along the display object.
  943. * DisplayObject's `interactive` property must be set to `true` to fire event.
  944. *
  945. * This comes from the @pixi/interaction package.
  946. * @event PIXI.DisplayObject#touchmove
  947. * @param {PIXI.InteractionEvent} event - Interaction event
  948. */
  949. _this._useSystemTicker = options.useSystemTicker !== undefined ? options.useSystemTicker : true;
  950. _this.setTargetElement(_this.renderer.view, _this.renderer.resolution);
  951. return _this;
  952. }
  953. Object.defineProperty(InteractionManager.prototype, "useSystemTicker", {
  954. /**
  955. * Should the InteractionManager automatically add {@link tickerUpdate} to {@link PIXI.Ticker.system}.
  956. * @default true
  957. */
  958. get: function () {
  959. return this._useSystemTicker;
  960. },
  961. set: function (useSystemTicker) {
  962. this._useSystemTicker = useSystemTicker;
  963. if (useSystemTicker) {
  964. this.addTickerListener();
  965. }
  966. else {
  967. this.removeTickerListener();
  968. }
  969. },
  970. enumerable: false,
  971. configurable: true
  972. });
  973. Object.defineProperty(InteractionManager.prototype, "lastObjectRendered", {
  974. /**
  975. * Last rendered object or temp object.
  976. * @readonly
  977. * @protected
  978. */
  979. get: function () {
  980. return this.renderer._lastObjectRendered || this._tempDisplayObject;
  981. },
  982. enumerable: false,
  983. configurable: true
  984. });
  985. /**
  986. * Hit tests a point against the display tree, returning the first interactive object that is hit.
  987. * @param globalPoint - A point to hit test with, in global space.
  988. * @param root - The root display object to start from. If omitted, defaults
  989. * to the last rendered root of the associated renderer.
  990. * @returns - The hit display object, if any.
  991. */
  992. InteractionManager.prototype.hitTest = function (globalPoint, root) {
  993. // clear the target for our hit test
  994. hitTestEvent.target = null;
  995. // assign the global point
  996. hitTestEvent.data.global = globalPoint;
  997. // ensure safety of the root
  998. if (!root) {
  999. root = this.lastObjectRendered;
  1000. }
  1001. // run the hit test
  1002. this.processInteractive(hitTestEvent, root, null, true);
  1003. // return our found object - it'll be null if we didn't hit anything
  1004. return hitTestEvent.target;
  1005. };
  1006. /**
  1007. * Sets the DOM element which will receive mouse/touch events. This is useful for when you have
  1008. * other DOM elements on top of the renderers Canvas element. With this you'll be bale to delegate
  1009. * another DOM element to receive those events.
  1010. * @param element - the DOM element which will receive mouse and touch events.
  1011. * @param resolution - The resolution / device pixel ratio of the new element (relative to the canvas).
  1012. */
  1013. InteractionManager.prototype.setTargetElement = function (element, resolution) {
  1014. if (resolution === void 0) { resolution = 1; }
  1015. this.removeTickerListener();
  1016. this.removeEvents();
  1017. this.interactionDOMElement = element;
  1018. this.resolution = resolution;
  1019. this.addEvents();
  1020. this.addTickerListener();
  1021. };
  1022. /** Adds the ticker listener. */
  1023. InteractionManager.prototype.addTickerListener = function () {
  1024. if (this.tickerAdded || !this.interactionDOMElement || !this._useSystemTicker) {
  1025. return;
  1026. }
  1027. Ticker.system.add(this.tickerUpdate, this, UPDATE_PRIORITY.INTERACTION);
  1028. this.tickerAdded = true;
  1029. };
  1030. /** Removes the ticker listener. */
  1031. InteractionManager.prototype.removeTickerListener = function () {
  1032. if (!this.tickerAdded) {
  1033. return;
  1034. }
  1035. Ticker.system.remove(this.tickerUpdate, this);
  1036. this.tickerAdded = false;
  1037. };
  1038. /** Registers all the DOM events. */
  1039. InteractionManager.prototype.addEvents = function () {
  1040. if (this.eventsAdded || !this.interactionDOMElement) {
  1041. return;
  1042. }
  1043. var style = this.interactionDOMElement.style;
  1044. if (globalThis.navigator.msPointerEnabled) {
  1045. style.msContentZooming = 'none';
  1046. style.msTouchAction = 'none';
  1047. }
  1048. else if (this.supportsPointerEvents) {
  1049. style.touchAction = 'none';
  1050. }
  1051. /*
  1052. * These events are added first, so that if pointer events are normalized, they are fired
  1053. * in the same order as non-normalized events. ie. pointer event 1st, mouse / touch 2nd
  1054. */
  1055. if (this.supportsPointerEvents) {
  1056. globalThis.document.addEventListener('pointermove', this.onPointerMove, this._eventListenerOptions);
  1057. this.interactionDOMElement.addEventListener('pointerdown', this.onPointerDown, this._eventListenerOptions);
  1058. // pointerout is fired in addition to pointerup (for touch events) and pointercancel
  1059. // we already handle those, so for the purposes of what we do in onPointerOut, we only
  1060. // care about the pointerleave event
  1061. this.interactionDOMElement.addEventListener('pointerleave', this.onPointerOut, this._eventListenerOptions);
  1062. this.interactionDOMElement.addEventListener('pointerover', this.onPointerOver, this._eventListenerOptions);
  1063. globalThis.addEventListener('pointercancel', this.onPointerCancel, this._eventListenerOptions);
  1064. globalThis.addEventListener('pointerup', this.onPointerUp, this._eventListenerOptions);
  1065. }
  1066. else {
  1067. globalThis.document.addEventListener('mousemove', this.onPointerMove, this._eventListenerOptions);
  1068. this.interactionDOMElement.addEventListener('mousedown', this.onPointerDown, this._eventListenerOptions);
  1069. this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, this._eventListenerOptions);
  1070. this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, this._eventListenerOptions);
  1071. globalThis.addEventListener('mouseup', this.onPointerUp, this._eventListenerOptions);
  1072. }
  1073. // always look directly for touch events so that we can provide original data
  1074. // In a future version we should change this to being just a fallback and rely solely on
  1075. // PointerEvents whenever available
  1076. if (this.supportsTouchEvents) {
  1077. this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, this._eventListenerOptions);
  1078. this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, this._eventListenerOptions);
  1079. this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, this._eventListenerOptions);
  1080. this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, this._eventListenerOptions);
  1081. }
  1082. this.eventsAdded = true;
  1083. };
  1084. /** Removes all the DOM events that were previously registered. */
  1085. InteractionManager.prototype.removeEvents = function () {
  1086. if (!this.eventsAdded || !this.interactionDOMElement) {
  1087. return;
  1088. }
  1089. var style = this.interactionDOMElement.style;
  1090. if (globalThis.navigator.msPointerEnabled) {
  1091. style.msContentZooming = '';
  1092. style.msTouchAction = '';
  1093. }
  1094. else if (this.supportsPointerEvents) {
  1095. style.touchAction = '';
  1096. }
  1097. if (this.supportsPointerEvents) {
  1098. globalThis.document.removeEventListener('pointermove', this.onPointerMove, this._eventListenerOptions);
  1099. this.interactionDOMElement.removeEventListener('pointerdown', this.onPointerDown, this._eventListenerOptions);
  1100. this.interactionDOMElement.removeEventListener('pointerleave', this.onPointerOut, this._eventListenerOptions);
  1101. this.interactionDOMElement.removeEventListener('pointerover', this.onPointerOver, this._eventListenerOptions);
  1102. globalThis.removeEventListener('pointercancel', this.onPointerCancel, this._eventListenerOptions);
  1103. globalThis.removeEventListener('pointerup', this.onPointerUp, this._eventListenerOptions);
  1104. }
  1105. else {
  1106. globalThis.document.removeEventListener('mousemove', this.onPointerMove, this._eventListenerOptions);
  1107. this.interactionDOMElement.removeEventListener('mousedown', this.onPointerDown, this._eventListenerOptions);
  1108. this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, this._eventListenerOptions);
  1109. this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, this._eventListenerOptions);
  1110. globalThis.removeEventListener('mouseup', this.onPointerUp, this._eventListenerOptions);
  1111. }
  1112. if (this.supportsTouchEvents) {
  1113. this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, this._eventListenerOptions);
  1114. this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, this._eventListenerOptions);
  1115. this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, this._eventListenerOptions);
  1116. this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, this._eventListenerOptions);
  1117. }
  1118. this.interactionDOMElement = null;
  1119. this.eventsAdded = false;
  1120. };
  1121. /**
  1122. * Updates the state of interactive objects if at least {@link interactionFrequency}
  1123. * milliseconds have passed since the last invocation.
  1124. *
  1125. * Invoked by a throttled ticker update from {@link PIXI.Ticker.system}.
  1126. * @param deltaTime - time delta since the last call
  1127. */
  1128. InteractionManager.prototype.tickerUpdate = function (deltaTime) {
  1129. this._deltaTime += deltaTime;
  1130. if (this._deltaTime < this.interactionFrequency) {
  1131. return;
  1132. }
  1133. this._deltaTime = 0;
  1134. this.update();
  1135. };
  1136. /** Updates the state of interactive objects. */
  1137. InteractionManager.prototype.update = function () {
  1138. if (!this.interactionDOMElement) {
  1139. return;
  1140. }
  1141. // if the user move the mouse this check has already been done using the mouse move!
  1142. if (this._didMove) {
  1143. this._didMove = false;
  1144. return;
  1145. }
  1146. this.cursor = null;
  1147. // Resets the flag as set by a stopPropagation call. This flag is usually reset by a user interaction of any kind,
  1148. // but there was a scenario of a display object moving under a static mouse cursor.
  1149. // In this case, mouseover and mouseevents would not pass the flag test in dispatchEvent function
  1150. for (var k in this.activeInteractionData) {
  1151. // eslint-disable-next-line no-prototype-builtins
  1152. if (this.activeInteractionData.hasOwnProperty(k)) {
  1153. var interactionData = this.activeInteractionData[k];
  1154. if (interactionData.originalEvent && interactionData.pointerType !== 'touch') {
  1155. var interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, interactionData.originalEvent, interactionData);
  1156. this.processInteractive(interactionEvent, this.lastObjectRendered, this.processPointerOverOut, true);
  1157. }
  1158. }
  1159. }
  1160. this.setCursorMode(this.cursor);
  1161. };
  1162. /**
  1163. * Sets the current cursor mode, handling any callbacks or CSS style changes.
  1164. * @param mode - cursor mode, a key from the cursorStyles dictionary
  1165. */
  1166. InteractionManager.prototype.setCursorMode = function (mode) {
  1167. mode = mode || 'default';
  1168. var applyStyles = true;
  1169. // offscreen canvas does not support setting styles, but cursor modes can be functions,
  1170. // in order to handle pixi rendered cursors, so we can't bail
  1171. if (globalThis.OffscreenCanvas && this.interactionDOMElement instanceof OffscreenCanvas) {
  1172. applyStyles = false;
  1173. }
  1174. // if the mode didn't actually change, bail early
  1175. if (this.currentCursorMode === mode) {
  1176. return;
  1177. }
  1178. this.currentCursorMode = mode;
  1179. var style = this.cursorStyles[mode];
  1180. // only do things if there is a cursor style for it
  1181. if (style) {
  1182. switch (typeof style) {
  1183. case 'string':
  1184. // string styles are handled as cursor CSS
  1185. if (applyStyles) {
  1186. this.interactionDOMElement.style.cursor = style;
  1187. }
  1188. break;
  1189. case 'function':
  1190. // functions are just called, and passed the cursor mode
  1191. style(mode);
  1192. break;
  1193. case 'object':
  1194. // if it is an object, assume that it is a dictionary of CSS styles,
  1195. // apply it to the interactionDOMElement
  1196. if (applyStyles) {
  1197. Object.assign(this.interactionDOMElement.style, style);
  1198. }
  1199. break;
  1200. }
  1201. }
  1202. else if (applyStyles && typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode)) {
  1203. // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
  1204. // for the mode, then assume that the dev wants it to be CSS for the cursor.
  1205. this.interactionDOMElement.style.cursor = mode;
  1206. }
  1207. };
  1208. /**
  1209. * Dispatches an event on the display object that was interacted with.
  1210. * @param displayObject - the display object in question
  1211. * @param eventString - the name of the event (e.g, mousedown)
  1212. * @param eventData - the event data object
  1213. */
  1214. InteractionManager.prototype.dispatchEvent = function (displayObject, eventString, eventData) {
  1215. // Even if the event was stopped, at least dispatch any remaining events
  1216. // for the same display object.
  1217. if (!eventData.stopPropagationHint || displayObject === eventData.stopsPropagatingAt) {
  1218. eventData.currentTarget = displayObject;
  1219. eventData.type = eventString;
  1220. displayObject.emit(eventString, eventData);
  1221. if (displayObject[eventString]) {
  1222. displayObject[eventString](eventData);
  1223. }
  1224. }
  1225. };
  1226. /**
  1227. * Puts a event on a queue to be dispatched later. This is used to guarantee correct
  1228. * ordering of over/out events.
  1229. * @param displayObject - the display object in question
  1230. * @param eventString - the name of the event (e.g, mousedown)
  1231. * @param eventData - the event data object
  1232. */
  1233. InteractionManager.prototype.delayDispatchEvent = function (displayObject, eventString, eventData) {
  1234. this.delayedEvents.push({ displayObject: displayObject, eventString: eventString, eventData: eventData });
  1235. };
  1236. /**
  1237. * Maps x and y coords from a DOM object and maps them correctly to the PixiJS view. The
  1238. * resulting value is stored in the point. This takes into account the fact that the DOM
  1239. * element could be scaled and positioned anywhere on the screen.
  1240. * @param point - the point that the result will be stored in
  1241. * @param x - the x coord of the position to map
  1242. * @param y - the y coord of the position to map
  1243. */
  1244. InteractionManager.prototype.mapPositionToPoint = function (point, x, y) {
  1245. var rect;
  1246. // IE 11 fix
  1247. if (!this.interactionDOMElement.parentElement) {
  1248. rect = {
  1249. x: 0,
  1250. y: 0,
  1251. width: this.interactionDOMElement.width,
  1252. height: this.interactionDOMElement.height,
  1253. left: 0,
  1254. top: 0
  1255. };
  1256. }
  1257. else {
  1258. rect = this.interactionDOMElement.getBoundingClientRect();
  1259. }
  1260. var resolutionMultiplier = 1.0 / this.resolution;
  1261. point.x = ((x - rect.left) * (this.interactionDOMElement.width / rect.width)) * resolutionMultiplier;
  1262. point.y = ((y - rect.top) * (this.interactionDOMElement.height / rect.height)) * resolutionMultiplier;
  1263. };
  1264. /**
  1265. * This function is provides a neat way of crawling through the scene graph and running a
  1266. * specified function on all interactive objects it finds. It will also take care of hit
  1267. * testing the interactive objects and passes the hit across in the function.
  1268. * @protected
  1269. * @param interactionEvent - event containing the point that
  1270. * is tested for collision
  1271. * @param displayObject - the displayObject
  1272. * that will be hit test (recursively crawls its children)
  1273. * @param func - the function that will be called on each interactive object. The
  1274. * interactionEvent, displayObject and hit will be passed to the function
  1275. * @param hitTest - indicates whether we want to calculate hits
  1276. * or just iterate through all interactive objects
  1277. */
  1278. InteractionManager.prototype.processInteractive = function (interactionEvent, displayObject, func, hitTest) {
  1279. var hit = this.search.findHit(interactionEvent, displayObject, func, hitTest);
  1280. var delayedEvents = this.delayedEvents;
  1281. if (!delayedEvents.length) {
  1282. return hit;
  1283. }
  1284. // Reset the propagation hint, because we start deeper in the tree again.
  1285. interactionEvent.stopPropagationHint = false;
  1286. var delayedLen = delayedEvents.length;
  1287. this.delayedEvents = [];
  1288. for (var i = 0; i < delayedLen; i++) {
  1289. var _a = delayedEvents[i], displayObject_1 = _a.displayObject, eventString = _a.eventString, eventData = _a.eventData;
  1290. // When we reach the object we wanted to stop propagating at,
  1291. // set the propagation hint.
  1292. if (eventData.stopsPropagatingAt === displayObject_1) {
  1293. eventData.stopPropagationHint = true;
  1294. }
  1295. this.dispatchEvent(displayObject_1, eventString, eventData);
  1296. }
  1297. return hit;
  1298. };
  1299. /**
  1300. * Is called when the pointer button is pressed down on the renderer element
  1301. * @param originalEvent - The DOM event of a pointer button being pressed down
  1302. */
  1303. InteractionManager.prototype.onPointerDown = function (originalEvent) {
  1304. // if we support touch events, then only use those for touch events, not pointer events
  1305. if (this.supportsTouchEvents && originalEvent.pointerType === 'touch')
  1306. { return; }
  1307. var events = this.normalizeToPointerData(originalEvent);
  1308. /*
  1309. * No need to prevent default on natural pointer events, as there are no side effects
  1310. * Normalized events, however, may have the double mousedown/touchstart issue on the native android browser,
  1311. * so still need to be prevented.
  1312. */
  1313. // Guaranteed that there will be at least one event in events, and all events must have the same pointer type
  1314. if (this.autoPreventDefault && events[0].isNormalized) {
  1315. var cancelable = originalEvent.cancelable || !('cancelable' in originalEvent);
  1316. if (cancelable) {
  1317. originalEvent.preventDefault();
  1318. }
  1319. }
  1320. var eventLen = events.length;
  1321. for (var i = 0; i < eventLen; i++) {
  1322. var event = events[i];
  1323. var interactionData = this.getInteractionDataForPointerId(event);
  1324. var interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
  1325. interactionEvent.data.originalEvent = originalEvent;
  1326. this.processInteractive(interactionEvent, this.lastObjectRendered, this.processPointerDown, true);
  1327. this.emit('pointerdown', interactionEvent);
  1328. if (event.pointerType === 'touch') {
  1329. this.emit('touchstart', interactionEvent);
  1330. }
  1331. // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
  1332. else if (event.pointerType === 'mouse' || event.pointerType === 'pen') {
  1333. var isRightButton = event.button === 2;
  1334. this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
  1335. }
  1336. }
  1337. };
  1338. /**
  1339. * Processes the result of the pointer down check and dispatches the event if need be
  1340. * @param interactionEvent - The interaction event wrapping the DOM event
  1341. * @param displayObject - The display object that was tested
  1342. * @param hit - the result of the hit test on the display object
  1343. */
  1344. InteractionManager.prototype.processPointerDown = function (interactionEvent, displayObject, hit) {
  1345. var data = interactionEvent.data;
  1346. var id = interactionEvent.data.identifier;
  1347. if (hit) {
  1348. if (!displayObject.trackedPointers[id]) {
  1349. displayObject.trackedPointers[id] = new InteractionTrackingData(id);
  1350. }
  1351. this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
  1352. if (data.pointerType === 'touch') {
  1353. this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
  1354. }
  1355. else if (data.pointerType === 'mouse' || data.pointerType === 'pen') {
  1356. var isRightButton = data.button === 2;
  1357. if (isRightButton) {
  1358. displayObject.trackedPointers[id].rightDown = true;
  1359. }
  1360. else {
  1361. displayObject.trackedPointers[id].leftDown = true;
  1362. }
  1363. this.dispatchEvent(displayObject, isRightButton ? 'rightdown' : 'mousedown', interactionEvent);
  1364. }
  1365. }
  1366. };
  1367. /**
  1368. * Is called when the pointer button is released on the renderer element
  1369. * @param originalEvent - The DOM event of a pointer button being released
  1370. * @param cancelled - true if the pointer is cancelled
  1371. * @param func - Function passed to {@link processInteractive}
  1372. */
  1373. InteractionManager.prototype.onPointerComplete = function (originalEvent, cancelled, func) {
  1374. var events = this.normalizeToPointerData(originalEvent);
  1375. var eventLen = events.length;
  1376. // if the event wasn't targeting our canvas, then consider it to be pointerupoutside
  1377. // in all cases (unless it was a pointercancel)
  1378. var target = originalEvent.target;
  1379. // if in shadow DOM use composedPath to access target
  1380. if (originalEvent.composedPath && originalEvent.composedPath().length > 0) {
  1381. target = originalEvent.composedPath()[0];
  1382. }
  1383. var eventAppend = target !== this.interactionDOMElement ? 'outside' : '';
  1384. for (var i = 0; i < eventLen; i++) {
  1385. var event = events[i];
  1386. var interactionData = this.getInteractionDataForPointerId(event);
  1387. var interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
  1388. interactionEvent.data.originalEvent = originalEvent;
  1389. // perform hit testing for events targeting our canvas or cancel events
  1390. this.processInteractive(interactionEvent, this.lastObjectRendered, func, cancelled || !eventAppend);
  1391. this.emit(cancelled ? 'pointercancel' : "pointerup" + eventAppend, interactionEvent);
  1392. if (event.pointerType === 'mouse' || event.pointerType === 'pen') {
  1393. var isRightButton = event.button === 2;
  1394. this.emit(isRightButton ? "rightup" + eventAppend : "mouseup" + eventAppend, interactionEvent);
  1395. }
  1396. else if (event.pointerType === 'touch') {
  1397. this.emit(cancelled ? 'touchcancel' : "touchend" + eventAppend, interactionEvent);
  1398. this.releaseInteractionDataForPointerId(event.pointerId);
  1399. }
  1400. }
  1401. };
  1402. /**
  1403. * Is called when the pointer button is cancelled
  1404. * @param event - The DOM event of a pointer button being released
  1405. */
  1406. InteractionManager.prototype.onPointerCancel = function (event) {
  1407. // if we support touch events, then only use those for touch events, not pointer events
  1408. if (this.supportsTouchEvents && event.pointerType === 'touch')
  1409. { return; }
  1410. this.onPointerComplete(event, true, this.processPointerCancel);
  1411. };
  1412. /**
  1413. * Processes the result of the pointer cancel check and dispatches the event if need be
  1414. * @param interactionEvent - The interaction event wrapping the DOM event
  1415. * @param displayObject - The display object that was tested
  1416. */
  1417. InteractionManager.prototype.processPointerCancel = function (interactionEvent, displayObject) {
  1418. var data = interactionEvent.data;
  1419. var id = interactionEvent.data.identifier;
  1420. if (displayObject.trackedPointers[id] !== undefined) {
  1421. delete displayObject.trackedPointers[id];
  1422. this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
  1423. if (data.pointerType === 'touch') {
  1424. this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
  1425. }
  1426. }
  1427. };
  1428. /**
  1429. * Is called when the pointer button is released on the renderer element
  1430. * @param event - The DOM event of a pointer button being released
  1431. */
  1432. InteractionManager.prototype.onPointerUp = function (event) {
  1433. // if we support touch events, then only use those for touch events, not pointer events
  1434. if (this.supportsTouchEvents && event.pointerType === 'touch')
  1435. { return; }
  1436. this.onPointerComplete(event, false, this.processPointerUp);
  1437. };
  1438. /**
  1439. * Processes the result of the pointer up check and dispatches the event if need be
  1440. * @param interactionEvent - The interaction event wrapping the DOM event
  1441. * @param displayObject - The display object that was tested
  1442. * @param hit - the result of the hit test on the display object
  1443. */
  1444. InteractionManager.prototype.processPointerUp = function (interactionEvent, displayObject, hit) {
  1445. var data = interactionEvent.data;
  1446. var id = interactionEvent.data.identifier;
  1447. var trackingData = displayObject.trackedPointers[id];
  1448. var isTouch = data.pointerType === 'touch';
  1449. var isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
  1450. // need to track mouse down status in the mouse block so that we can emit
  1451. // event in a later block
  1452. var isMouseTap = false;
  1453. // Mouse only
  1454. if (isMouse) {
  1455. var isRightButton = data.button === 2;
  1456. var flags = InteractionTrackingData.FLAGS;
  1457. var test = isRightButton ? flags.RIGHT_DOWN : flags.LEFT_DOWN;
  1458. var isDown = trackingData !== undefined && (trackingData.flags & test);
  1459. if (hit) {
  1460. this.dispatchEvent(displayObject, isRightButton ? 'rightup' : 'mouseup', interactionEvent);
  1461. if (isDown) {
  1462. this.dispatchEvent(displayObject, isRightButton ? 'rightclick' : 'click', interactionEvent);
  1463. // because we can confirm that the mousedown happened on this object, flag for later emit of pointertap
  1464. isMouseTap = true;
  1465. }
  1466. }
  1467. else if (isDown) {
  1468. this.dispatchEvent(displayObject, isRightButton ? 'rightupoutside' : 'mouseupoutside', interactionEvent);
  1469. }
  1470. // update the down state of the tracking data
  1471. if (trackingData) {
  1472. if (isRightButton) {
  1473. trackingData.rightDown = false;
  1474. }
  1475. else {
  1476. trackingData.leftDown = false;
  1477. }
  1478. }
  1479. }
  1480. // Pointers and Touches, and Mouse
  1481. if (hit) {
  1482. this.dispatchEvent(displayObject, 'pointerup', interactionEvent);
  1483. if (isTouch)
  1484. { this.dispatchEvent(displayObject, 'touchend', interactionEvent); }
  1485. if (trackingData) {
  1486. // emit pointertap if not a mouse, or if the mouse block decided it was a tap
  1487. if (!isMouse || isMouseTap) {
  1488. this.dispatchEvent(displayObject, 'pointertap', interactionEvent);
  1489. }
  1490. if (isTouch) {
  1491. this.dispatchEvent(displayObject, 'tap', interactionEvent);
  1492. // touches are no longer over (if they ever were) when we get the touchend
  1493. // so we should ensure that we don't keep pretending that they are
  1494. trackingData.over = false;
  1495. }
  1496. }
  1497. }
  1498. else if (trackingData) {
  1499. this.dispatchEvent(displayObject, 'pointerupoutside', interactionEvent);
  1500. if (isTouch)
  1501. { this.dispatchEvent(displayObject, 'touchendoutside', interactionEvent); }
  1502. }
  1503. // Only remove the tracking data if there is no over/down state still associated with it
  1504. if (trackingData && trackingData.none) {
  1505. delete displayObject.trackedPointers[id];
  1506. }
  1507. };
  1508. /**
  1509. * Is called when the pointer moves across the renderer element
  1510. * @param originalEvent - The DOM event of a pointer moving
  1511. */
  1512. InteractionManager.prototype.onPointerMove = function (originalEvent) {
  1513. // if we support touch events, then only use those for touch events, not pointer events
  1514. if (this.supportsTouchEvents && originalEvent.pointerType === 'touch')
  1515. { return; }
  1516. var events = this.normalizeToPointerData(originalEvent);
  1517. if (events[0].pointerType === 'mouse' || events[0].pointerType === 'pen') {
  1518. this._didMove = true;
  1519. this.cursor = null;
  1520. }
  1521. var eventLen = events.length;
  1522. for (var i = 0; i < eventLen; i++) {
  1523. var event = events[i];
  1524. var interactionData = this.getInteractionDataForPointerId(event);
  1525. var interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
  1526. interactionEvent.data.originalEvent = originalEvent;
  1527. this.processInteractive(interactionEvent, this.lastObjectRendered, this.processPointerMove, true);
  1528. this.emit('pointermove', interactionEvent);
  1529. if (event.pointerType === 'touch')
  1530. { this.emit('touchmove', interactionEvent); }
  1531. if (event.pointerType === 'mouse' || event.pointerType === 'pen')
  1532. { this.emit('mousemove', interactionEvent); }
  1533. }
  1534. if (events[0].pointerType === 'mouse') {
  1535. this.setCursorMode(this.cursor);
  1536. // TODO BUG for parents interactive object (border order issue)
  1537. }
  1538. };
  1539. /**
  1540. * Processes the result of the pointer move check and dispatches the event if need be
  1541. * @param interactionEvent - The interaction event wrapping the DOM event
  1542. * @param displayObject - The display object that was tested
  1543. * @param hit - the result of the hit test on the display object
  1544. */
  1545. InteractionManager.prototype.processPointerMove = function (interactionEvent, displayObject, hit) {
  1546. var data = interactionEvent.data;
  1547. var isTouch = data.pointerType === 'touch';
  1548. var isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
  1549. if (isMouse) {
  1550. this.processPointerOverOut(interactionEvent, displayObject, hit);
  1551. }
  1552. if (!this.moveWhenInside || hit) {
  1553. this.dispatchEvent(displayObject, 'pointermove', interactionEvent);
  1554. if (isTouch)
  1555. { this.dispatchEvent(displayObject, 'touchmove', interactionEvent); }
  1556. if (isMouse)
  1557. { this.dispatchEvent(displayObject, 'mousemove', interactionEvent); }
  1558. }
  1559. };
  1560. /**
  1561. * Is called when the pointer is moved out of the renderer element
  1562. * @private
  1563. * @param {PointerEvent} originalEvent - The DOM event of a pointer being moved out
  1564. */
  1565. InteractionManager.prototype.onPointerOut = function (originalEvent) {
  1566. // if we support touch events, then only use those for touch events, not pointer events
  1567. if (this.supportsTouchEvents && originalEvent.pointerType === 'touch')
  1568. { return; }
  1569. var events = this.normalizeToPointerData(originalEvent);
  1570. // Only mouse and pointer can call onPointerOut, so events will always be length 1
  1571. var event = events[0];
  1572. if (event.pointerType === 'mouse') {
  1573. this.mouseOverRenderer = false;
  1574. this.setCursorMode(null);
  1575. }
  1576. var interactionData = this.getInteractionDataForPointerId(event);
  1577. var interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
  1578. interactionEvent.data.originalEvent = event;
  1579. this.processInteractive(interactionEvent, this.lastObjectRendered, this.processPointerOverOut, false);
  1580. this.emit('pointerout', interactionEvent);
  1581. if (event.pointerType === 'mouse' || event.pointerType === 'pen') {
  1582. this.emit('mouseout', interactionEvent);
  1583. }
  1584. else {
  1585. // we can get touchleave events after touchend, so we want to make sure we don't
  1586. // introduce memory leaks
  1587. this.releaseInteractionDataForPointerId(interactionData.identifier);
  1588. }
  1589. };
  1590. /**
  1591. * Processes the result of the pointer over/out check and dispatches the event if need be.
  1592. * @param interactionEvent - The interaction event wrapping the DOM event
  1593. * @param displayObject - The display object that was tested
  1594. * @param hit - the result of the hit test on the display object
  1595. */
  1596. InteractionManager.prototype.processPointerOverOut = function (interactionEvent, displayObject, hit) {
  1597. var data = interactionEvent.data;
  1598. var id = interactionEvent.data.identifier;
  1599. var isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
  1600. var trackingData = displayObject.trackedPointers[id];
  1601. // if we just moused over the display object, then we need to track that state
  1602. if (hit && !trackingData) {
  1603. trackingData = displayObject.trackedPointers[id] = new InteractionTrackingData(id);
  1604. }
  1605. if (trackingData === undefined)
  1606. { return; }
  1607. if (hit && this.mouseOverRenderer) {
  1608. if (!trackingData.over) {
  1609. trackingData.over = true;
  1610. this.delayDispatchEvent(displayObject, 'pointerover', interactionEvent);
  1611. if (isMouse) {
  1612. this.delayDispatchEvent(displayObject, 'mouseover', interactionEvent);
  1613. }
  1614. }
  1615. // only change the cursor if it has not already been changed (by something deeper in the
  1616. // display tree)
  1617. if (isMouse && this.cursor === null) {
  1618. this.cursor = displayObject.cursor;
  1619. }
  1620. }
  1621. else if (trackingData.over) {
  1622. trackingData.over = false;
  1623. this.dispatchEvent(displayObject, 'pointerout', this.eventData);
  1624. if (isMouse) {
  1625. this.dispatchEvent(displayObject, 'mouseout', interactionEvent);
  1626. }
  1627. // if there is no mouse down information for the pointer, then it is safe to delete
  1628. if (trackingData.none) {
  1629. delete displayObject.trackedPointers[id];
  1630. }
  1631. }
  1632. };
  1633. /**
  1634. * Is called when the pointer is moved into the renderer element.
  1635. * @param originalEvent - The DOM event of a pointer button being moved into the renderer view.
  1636. */
  1637. InteractionManager.prototype.onPointerOver = function (originalEvent) {
  1638. if (this.supportsTouchEvents && originalEvent.pointerType === 'touch')
  1639. { return; }
  1640. var events = this.normalizeToPointerData(originalEvent);
  1641. // Only mouse and pointer can call onPointerOver, so events will always be length 1
  1642. var event = events[0];
  1643. var interactionData = this.getInteractionDataForPointerId(event);
  1644. var interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
  1645. interactionEvent.data.originalEvent = event;
  1646. if (event.pointerType === 'mouse') {
  1647. this.mouseOverRenderer = true;
  1648. }
  1649. this.emit('pointerover', interactionEvent);
  1650. if (event.pointerType === 'mouse' || event.pointerType === 'pen') {
  1651. this.emit('mouseover', interactionEvent);
  1652. }
  1653. };
  1654. /**
  1655. * Get InteractionData for a given pointerId. Store that data as well.
  1656. * @param event - Normalized pointer event, output from normalizeToPointerData.
  1657. * @returns - Interaction data for the given pointer identifier.
  1658. */
  1659. InteractionManager.prototype.getInteractionDataForPointerId = function (event) {
  1660. var pointerId = event.pointerId;
  1661. var interactionData;
  1662. if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse') {
  1663. interactionData = this.mouse;
  1664. }
  1665. else if (this.activeInteractionData[pointerId]) {
  1666. interactionData = this.activeInteractionData[pointerId];
  1667. }
  1668. else {
  1669. interactionData = this.interactionDataPool.pop() || new InteractionData();
  1670. interactionData.identifier = pointerId;
  1671. this.activeInteractionData[pointerId] = interactionData;
  1672. }
  1673. // copy properties from the event, so that we can make sure that touch/pointer specific
  1674. // data is available
  1675. interactionData.copyEvent(event);
  1676. return interactionData;
  1677. };
  1678. /**
  1679. * Return unused InteractionData to the pool, for a given pointerId
  1680. * @param pointerId - Identifier from a pointer event
  1681. */
  1682. InteractionManager.prototype.releaseInteractionDataForPointerId = function (pointerId) {
  1683. var interactionData = this.activeInteractionData[pointerId];
  1684. if (interactionData) {
  1685. delete this.activeInteractionData[pointerId];
  1686. interactionData.reset();
  1687. this.interactionDataPool.push(interactionData);
  1688. }
  1689. };
  1690. /**
  1691. * Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
  1692. * @param interactionEvent - The event to be configured
  1693. * @param pointerEvent - The DOM event that will be paired with the InteractionEvent
  1694. * @param interactionData - The InteractionData that will be paired
  1695. * with the InteractionEvent
  1696. * @returns - the interaction event that was passed in
  1697. */
  1698. InteractionManager.prototype.configureInteractionEventForDOMEvent = function (interactionEvent, pointerEvent, interactionData) {
  1699. interactionEvent.data = interactionData;
  1700. this.mapPositionToPoint(interactionData.global, pointerEvent.clientX, pointerEvent.clientY);
  1701. // Not really sure why this is happening, but it's how a previous version handled things
  1702. if (pointerEvent.pointerType === 'touch') {
  1703. pointerEvent.globalX = interactionData.global.x;
  1704. pointerEvent.globalY = interactionData.global.y;
  1705. }
  1706. interactionData.originalEvent = pointerEvent;
  1707. interactionEvent.reset();
  1708. return interactionEvent;
  1709. };
  1710. /**
  1711. * Ensures that the original event object contains all data that a regular pointer event would have
  1712. * @param {TouchEvent|MouseEvent|PointerEvent} event - The original event data from a touch or mouse event
  1713. * @returns - An array containing a single normalized pointer event, in the case of a pointer
  1714. * or mouse event, or a multiple normalized pointer events if there are multiple changed touches
  1715. */
  1716. InteractionManager.prototype.normalizeToPointerData = function (event) {
  1717. var normalizedEvents = [];
  1718. if (this.supportsTouchEvents && event instanceof TouchEvent) {
  1719. for (var i = 0, li = event.changedTouches.length; i < li; i++) {
  1720. var touch = event.changedTouches[i];
  1721. if (typeof touch.button === 'undefined')
  1722. { touch.button = event.touches.length ? 1 : 0; }
  1723. if (typeof touch.buttons === 'undefined')
  1724. { touch.buttons = event.touches.length ? 1 : 0; }
  1725. if (typeof touch.isPrimary === 'undefined') {
  1726. touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
  1727. }
  1728. if (typeof touch.width === 'undefined')
  1729. { touch.width = touch.radiusX || 1; }
  1730. if (typeof touch.height === 'undefined')
  1731. { touch.height = touch.radiusY || 1; }
  1732. if (typeof touch.tiltX === 'undefined')
  1733. { touch.tiltX = 0; }
  1734. if (typeof touch.tiltY === 'undefined')
  1735. { touch.tiltY = 0; }
  1736. if (typeof touch.pointerType === 'undefined')
  1737. { touch.pointerType = 'touch'; }
  1738. if (typeof touch.pointerId === 'undefined')
  1739. { touch.pointerId = touch.identifier || 0; }
  1740. if (typeof touch.pressure === 'undefined')
  1741. { touch.pressure = touch.force || 0.5; }
  1742. if (typeof touch.twist === 'undefined')
  1743. { touch.twist = 0; }
  1744. if (typeof touch.tangentialPressure === 'undefined')
  1745. { touch.tangentialPressure = 0; }
  1746. // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
  1747. // support, and the fill ins are not quite the same
  1748. // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
  1749. // left is not 0,0 on the page
  1750. if (typeof touch.layerX === 'undefined')
  1751. { touch.layerX = touch.offsetX = touch.clientX; }
  1752. if (typeof touch.layerY === 'undefined')
  1753. { touch.layerY = touch.offsetY = touch.clientY; }
  1754. // mark the touch as normalized, just so that we know we did it
  1755. touch.isNormalized = true;
  1756. normalizedEvents.push(touch);
  1757. }
  1758. }
  1759. // apparently PointerEvent subclasses MouseEvent, so yay
  1760. else if (!globalThis.MouseEvent
  1761. || (event instanceof MouseEvent && (!this.supportsPointerEvents || !(event instanceof globalThis.PointerEvent)))) {
  1762. var tempEvent = event;
  1763. if (typeof tempEvent.isPrimary === 'undefined')
  1764. { tempEvent.isPrimary = true; }
  1765. if (typeof tempEvent.width === 'undefined')
  1766. { tempEvent.width = 1; }
  1767. if (typeof tempEvent.height === 'undefined')
  1768. { tempEvent.height = 1; }
  1769. if (typeof tempEvent.tiltX === 'undefined')
  1770. { tempEvent.tiltX = 0; }
  1771. if (typeof tempEvent.tiltY === 'undefined')
  1772. { tempEvent.tiltY = 0; }
  1773. if (typeof tempEvent.pointerType === 'undefined')
  1774. { tempEvent.pointerType = 'mouse'; }
  1775. if (typeof tempEvent.pointerId === 'undefined')
  1776. { tempEvent.pointerId = MOUSE_POINTER_ID; }
  1777. if (typeof tempEvent.pressure === 'undefined')
  1778. { tempEvent.pressure = 0.5; }
  1779. if (typeof tempEvent.twist === 'undefined')
  1780. { tempEvent.twist = 0; }
  1781. if (typeof tempEvent.tangentialPressure === 'undefined')
  1782. { tempEvent.tangentialPressure = 0; }
  1783. // mark the mouse event as normalized, just so that we know we did it
  1784. tempEvent.isNormalized = true;
  1785. normalizedEvents.push(tempEvent);
  1786. }
  1787. else {
  1788. normalizedEvents.push(event);
  1789. }
  1790. return normalizedEvents;
  1791. };
  1792. /** Destroys the interaction manager. */
  1793. InteractionManager.prototype.destroy = function () {
  1794. this.removeEvents();
  1795. this.removeTickerListener();
  1796. this.removeAllListeners();
  1797. this.renderer = null;
  1798. this.mouse = null;
  1799. this.eventData = null;
  1800. this.interactionDOMElement = null;
  1801. this.onPointerDown = null;
  1802. this.processPointerDown = null;
  1803. this.onPointerUp = null;
  1804. this.processPointerUp = null;
  1805. this.onPointerCancel = null;
  1806. this.processPointerCancel = null;
  1807. this.onPointerMove = null;
  1808. this.processPointerMove = null;
  1809. this.onPointerOut = null;
  1810. this.processPointerOverOut = null;
  1811. this.onPointerOver = null;
  1812. this.search = null;
  1813. };
  1814. /** @ignore */
  1815. InteractionManager.extension = {
  1816. name: 'interaction',
  1817. type: [
  1818. ExtensionType.RendererPlugin,
  1819. ExtensionType.CanvasRendererPlugin ],
  1820. };
  1821. return InteractionManager;
  1822. }(EventEmitter));
  1823. export { InteractionData, InteractionEvent, InteractionManager, InteractionTrackingData, interactiveTarget };
  1824. //# sourceMappingURL=interaction.mjs.map