interaction.js 85 KB

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