Source: interaction/InteractionManager.js

interaction/InteractionManager.js

  1. import * as core from '../core';
  2. import InteractionData from './InteractionData';
  3. import InteractionEvent from './InteractionEvent';
  4. import InteractionTrackingData from './InteractionTrackingData';
  5. import EventEmitter from 'eventemitter3';
  6. import interactiveTarget from './interactiveTarget';
  7. //引入小程序补丁
  8. import {windowAlias, documentAlias, navigator} from '@ali/pixi-miniprogram-adapter';
  9. // Mix interactiveTarget into core.DisplayObject.prototype, after deprecation has been handled
  10. core.utils.mixins.delayMixin(
  11. core.DisplayObject.prototype,
  12. interactiveTarget
  13. );
  14. const MOUSE_POINTER_ID = 1;
  15. // helpers for hitTest() - only used inside hitTest()
  16. const hitTestEvent = {
  17. target: null,
  18. data: {
  19. global: null,
  20. },
  21. };
  22. /**
  23. * The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
  24. * if its interactive parameter is set to true
  25. * This manager also supports multitouch.
  26. *
  27. * An instance of this class is automatically created by default, and can be found at renderer.plugins.interaction
  28. *
  29. * @class
  30. * @extends EventEmitter
  31. * @memberof PIXI.interaction
  32. */
  33. export default class InteractionManager extends EventEmitter
  34. {
  35. /**
  36. * @param {PIXI.CanvasRenderer|PIXI.WebGLRenderer} renderer - A reference to the current renderer
  37. * @param {object} [options] - The options for the manager.
  38. * @param {boolean} [options.autoPreventDefault=true] - Should the manager automatically prevent default browser actions.
  39. * @param {number} [options.interactionFrequency=10] - Frequency increases the interaction events will be checked.
  40. */
  41. constructor(renderer, options)
  42. {
  43. super();
  44. options = options || {};
  45. /**
  46. * The renderer this interaction manager works for.
  47. *
  48. * @member {PIXI.SystemRenderer}
  49. */
  50. this.renderer = renderer;
  51. /**
  52. * Should default browser actions automatically be prevented.
  53. * Does not apply to pointer events for backwards compatibility
  54. * preventDefault on pointer events stops mouse events from firing
  55. * Thus, for every pointer event, there will always be either a mouse of touch event alongside it.
  56. *
  57. * @member {boolean}
  58. * @default true
  59. */
  60. this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true;
  61. /**
  62. * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked.
  63. *
  64. * @member {number}
  65. * @default 10
  66. */
  67. this.interactionFrequency = options.interactionFrequency || 10;
  68. /**
  69. * The mouse data
  70. *
  71. * @member {PIXI.interaction.InteractionData}
  72. */
  73. this.mouse = new InteractionData();
  74. this.mouse.identifier = MOUSE_POINTER_ID;
  75. // setting the mouse to start off far off screen will mean that mouse over does
  76. // not get called before we even move the mouse.
  77. this.mouse.global.set(-999999);
  78. /**
  79. * Actively tracked InteractionData
  80. *
  81. * @private
  82. * @member {Object.<number,PIXI.interation.InteractionData>}
  83. */
  84. this.activeInteractionData = {};
  85. this.activeInteractionData[MOUSE_POINTER_ID] = this.mouse;
  86. /**
  87. * Pool of unused InteractionData
  88. *
  89. * @private
  90. * @member {PIXI.interation.InteractionData[]}
  91. */
  92. this.interactionDataPool = [];
  93. /**
  94. * An event data object to handle all the event tracking/dispatching
  95. *
  96. * @member {object}
  97. */
  98. this.eventData = new InteractionEvent();
  99. /**
  100. * The DOM element to bind to.
  101. *
  102. * @private
  103. * @member {HTMLElement}
  104. */
  105. this.interactionDOMElement = null;
  106. /**
  107. * This property determines if mousemove and touchmove events are fired only when the cursor
  108. * is over the object.
  109. * Setting to true will make things work more in line with how the DOM verison works.
  110. * Setting to false can make things easier for things like dragging
  111. * It is currently set to false as this is how PixiJS used to work. This will be set to true in
  112. * future versions of pixi.
  113. *
  114. * @member {boolean}
  115. * @default false
  116. */
  117. this.moveWhenInside = false;
  118. /**
  119. * Have events been attached to the dom element?
  120. *
  121. * @private
  122. * @member {boolean}
  123. */
  124. this.eventsAdded = false;
  125. /**
  126. * Is the mouse hovering over the renderer?
  127. *
  128. * @private
  129. * @member {boolean}
  130. */
  131. this.mouseOverRenderer = false;
  132. /**
  133. * Does the device support touch events
  134. * https://www.w3.org/TR/touch-events/
  135. *
  136. * @readonly
  137. * @member {boolean}
  138. */
  139. this.supportsTouchEvents = 'ontouchstart' in windowAlias;
  140. /**
  141. * Does the device support pointer events
  142. * https://www.w3.org/Submission/pointer-events/
  143. *
  144. * @readonly
  145. * @member {boolean}
  146. */
  147. this.supportsPointerEvents = !!windowAlias.PointerEvent;
  148. // this will make it so that you don't have to call bind all the time
  149. /**
  150. * @private
  151. * @member {Function}
  152. */
  153. this.onPointerUp = this.onPointerUp.bind(this);
  154. this.processPointerUp = this.processPointerUp.bind(this);
  155. /**
  156. * @private
  157. * @member {Function}
  158. */
  159. this.onPointerCancel = this.onPointerCancel.bind(this);
  160. this.processPointerCancel = this.processPointerCancel.bind(this);
  161. /**
  162. * @private
  163. * @member {Function}
  164. */
  165. this.onPointerDown = this.onPointerDown.bind(this);
  166. this.processPointerDown = this.processPointerDown.bind(this);
  167. /**
  168. * @private
  169. * @member {Function}
  170. */
  171. this.onPointerMove = this.onPointerMove.bind(this);
  172. this.processPointerMove = this.processPointerMove.bind(this);
  173. /**
  174. * @private
  175. * @member {Function}
  176. */
  177. this.onPointerOut = this.onPointerOut.bind(this);
  178. this.processPointerOverOut = this.processPointerOverOut.bind(this);
  179. /**
  180. * @private
  181. * @member {Function}
  182. */
  183. this.onPointerOver = this.onPointerOver.bind(this);
  184. /**
  185. * Dictionary of how different cursor modes are handled. Strings are handled as CSS cursor
  186. * values, objects are handled as dictionaries of CSS values for interactionDOMElement,
  187. * and functions are called instead of changing the CSS.
  188. * Default CSS cursor values are provided for 'default' and 'pointer' modes.
  189. * @member {Object.<string, (string|Function|Object.<string, string>)>}
  190. */
  191. this.cursorStyles = {
  192. default: 'inherit',
  193. pointer: 'pointer',
  194. };
  195. /**
  196. * The mode of the cursor that is being used.
  197. * The value of this is a key from the cursorStyles dictionary.
  198. *
  199. * @member {string}
  200. */
  201. this.currentCursorMode = null;
  202. /**
  203. * Internal cached let.
  204. *
  205. * @private
  206. * @member {string}
  207. */
  208. this.cursor = null;
  209. /**
  210. * Internal cached let.
  211. *
  212. * @private
  213. * @member {PIXI.Point}
  214. */
  215. this._tempPoint = new core.Point();
  216. /**
  217. * The current resolution / device pixel ratio.
  218. *
  219. * @member {number}
  220. * @default 1
  221. */
  222. this.resolution = 1;
  223. this.setTargetElement(this.renderer.view, this.renderer.resolution);
  224. /**
  225. * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
  226. * object.
  227. *
  228. * @event PIXI.interaction.InteractionManager#mousedown
  229. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  230. */
  231. /**
  232. * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
  233. * on the display object.
  234. *
  235. * @event PIXI.interaction.InteractionManager#rightdown
  236. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  237. */
  238. /**
  239. * Fired when a pointer device button (usually a mouse left-button) is released over the display
  240. * object.
  241. *
  242. * @event PIXI.interaction.InteractionManager#mouseup
  243. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  244. */
  245. /**
  246. * Fired when a pointer device secondary button (usually a mouse right-button) is released
  247. * over the display object.
  248. *
  249. * @event PIXI.interaction.InteractionManager#rightup
  250. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  251. */
  252. /**
  253. * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
  254. * the display object.
  255. *
  256. * @event PIXI.interaction.InteractionManager#click
  257. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  258. */
  259. /**
  260. * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
  261. * and released on the display object.
  262. *
  263. * @event PIXI.interaction.InteractionManager#rightclick
  264. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  265. */
  266. /**
  267. * Fired when a pointer device button (usually a mouse left-button) is released outside the
  268. * display object that initially registered a
  269. * [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
  270. *
  271. * @event PIXI.interaction.InteractionManager#mouseupoutside
  272. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  273. */
  274. /**
  275. * Fired when a pointer device secondary button (usually a mouse right-button) is released
  276. * outside the display object that initially registered a
  277. * [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
  278. *
  279. * @event PIXI.interaction.InteractionManager#rightupoutside
  280. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  281. */
  282. /**
  283. * Fired when a pointer device (usually a mouse) is moved while over the display object
  284. *
  285. * @event PIXI.interaction.InteractionManager#mousemove
  286. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  287. */
  288. /**
  289. * Fired when a pointer device (usually a mouse) is moved onto the display object
  290. *
  291. * @event PIXI.interaction.InteractionManager#mouseover
  292. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  293. */
  294. /**
  295. * Fired when a pointer device (usually a mouse) is moved off the display object
  296. *
  297. * @event PIXI.interaction.InteractionManager#mouseout
  298. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  299. */
  300. /**
  301. * Fired when a pointer device button is pressed on the display object.
  302. *
  303. * @event PIXI.interaction.InteractionManager#pointerdown
  304. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  305. */
  306. /**
  307. * Fired when a pointer device button is released over the display object.
  308. * Not always fired when some buttons are held down while others are released. In those cases,
  309. * use [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown} and
  310. * [mouseup]{@link PIXI.interaction.InteractionManager#event:mouseup} instead.
  311. *
  312. * @event PIXI.interaction.InteractionManager#pointerup
  313. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  314. */
  315. /**
  316. * Fired when the operating system cancels a pointer event
  317. *
  318. * @event PIXI.interaction.InteractionManager#pointercancel
  319. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  320. */
  321. /**
  322. * Fired when a pointer device button is pressed and released on the display object.
  323. *
  324. * @event PIXI.interaction.InteractionManager#pointertap
  325. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  326. */
  327. /**
  328. * Fired when a pointer device button is released outside the display object that initially
  329. * registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
  330. *
  331. * @event PIXI.interaction.InteractionManager#pointerupoutside
  332. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  333. */
  334. /**
  335. * Fired when a pointer device is moved while over the display object
  336. *
  337. * @event PIXI.interaction.InteractionManager#pointermove
  338. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  339. */
  340. /**
  341. * Fired when a pointer device is moved onto the display object
  342. *
  343. * @event PIXI.interaction.InteractionManager#pointerover
  344. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  345. */
  346. /**
  347. * Fired when a pointer device is moved off the display object
  348. *
  349. * @event PIXI.interaction.InteractionManager#pointerout
  350. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  351. */
  352. /**
  353. * Fired when a touch point is placed on the display object.
  354. *
  355. * @event PIXI.interaction.InteractionManager#touchstart
  356. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  357. */
  358. /**
  359. * Fired when a touch point is removed from the display object.
  360. *
  361. * @event PIXI.interaction.InteractionManager#touchend
  362. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  363. */
  364. /**
  365. * Fired when the operating system cancels a touch
  366. *
  367. * @event PIXI.interaction.InteractionManager#touchcancel
  368. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  369. */
  370. /**
  371. * Fired when a touch point is placed and removed from the display object.
  372. *
  373. * @event PIXI.interaction.InteractionManager#tap
  374. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  375. */
  376. /**
  377. * Fired when a touch point is removed outside of the display object that initially
  378. * registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
  379. *
  380. * @event PIXI.interaction.InteractionManager#touchendoutside
  381. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  382. */
  383. /**
  384. * Fired when a touch point is moved along the display object.
  385. *
  386. * @event PIXI.interaction.InteractionManager#touchmove
  387. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  388. */
  389. /**
  390. * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
  391. * object. DisplayObject's `interactive` property must be set to `true` to fire event.
  392. *
  393. * @event PIXI.DisplayObject#mousedown
  394. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  395. */
  396. /**
  397. * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
  398. * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
  399. *
  400. * @event PIXI.DisplayObject#rightdown
  401. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  402. */
  403. /**
  404. * Fired when a pointer device button (usually a mouse left-button) is released over the display
  405. * object. DisplayObject's `interactive` property must be set to `true` to fire event.
  406. *
  407. * @event PIXI.DisplayObject#mouseup
  408. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  409. */
  410. /**
  411. * Fired when a pointer device secondary button (usually a mouse right-button) is released
  412. * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
  413. *
  414. * @event PIXI.DisplayObject#rightup
  415. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  416. */
  417. /**
  418. * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
  419. * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
  420. *
  421. * @event PIXI.DisplayObject#click
  422. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  423. */
  424. /**
  425. * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
  426. * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
  427. *
  428. * @event PIXI.DisplayObject#rightclick
  429. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  430. */
  431. /**
  432. * Fired when a pointer device button (usually a mouse left-button) is released outside the
  433. * display object that initially registered a
  434. * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
  435. * DisplayObject's `interactive` property must be set to `true` to fire event.
  436. *
  437. * @event PIXI.DisplayObject#mouseupoutside
  438. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  439. */
  440. /**
  441. * Fired when a pointer device secondary button (usually a mouse right-button) is released
  442. * outside the display object that initially registered a
  443. * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
  444. * DisplayObject's `interactive` property must be set to `true` to fire event.
  445. *
  446. * @event PIXI.DisplayObject#rightupoutside
  447. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  448. */
  449. /**
  450. * Fired when a pointer device (usually a mouse) is moved while over the display object.
  451. * DisplayObject's `interactive` property must be set to `true` to fire event.
  452. *
  453. * @event PIXI.DisplayObject#mousemove
  454. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  455. */
  456. /**
  457. * Fired when a pointer device (usually a mouse) is moved onto the display object.
  458. * DisplayObject's `interactive` property must be set to `true` to fire event.
  459. *
  460. * @event PIXI.DisplayObject#mouseover
  461. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  462. */
  463. /**
  464. * Fired when a pointer device (usually a mouse) is moved off the display object.
  465. * DisplayObject's `interactive` property must be set to `true` to fire event.
  466. *
  467. * @event PIXI.DisplayObject#mouseout
  468. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  469. */
  470. /**
  471. * Fired when a pointer device button is pressed on the display object.
  472. * DisplayObject's `interactive` property must be set to `true` to fire event.
  473. *
  474. * @event PIXI.DisplayObject#pointerdown
  475. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  476. */
  477. /**
  478. * Fired when a pointer device button is released over the display object.
  479. * DisplayObject's `interactive` property must be set to `true` to fire event.
  480. *
  481. * @event PIXI.DisplayObject#pointerup
  482. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  483. */
  484. /**
  485. * Fired when the operating system cancels a pointer event.
  486. * DisplayObject's `interactive` property must be set to `true` to fire event.
  487. *
  488. * @event PIXI.DisplayObject#pointercancel
  489. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  490. */
  491. /**
  492. * Fired when a pointer device button is pressed and released on the display object.
  493. * DisplayObject's `interactive` property must be set to `true` to fire event.
  494. *
  495. * @event PIXI.DisplayObject#pointertap
  496. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  497. */
  498. /**
  499. * Fired when a pointer device button is released outside the display object that initially
  500. * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
  501. * DisplayObject's `interactive` property must be set to `true` to fire event.
  502. *
  503. * @event PIXI.DisplayObject#pointerupoutside
  504. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  505. */
  506. /**
  507. * Fired when a pointer device is moved while over the display object.
  508. * DisplayObject's `interactive` property must be set to `true` to fire event.
  509. *
  510. * @event PIXI.DisplayObject#pointermove
  511. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  512. */
  513. /**
  514. * Fired when a pointer device is moved onto the display object.
  515. * DisplayObject's `interactive` property must be set to `true` to fire event.
  516. *
  517. * @event PIXI.DisplayObject#pointerover
  518. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  519. */
  520. /**
  521. * Fired when a pointer device is moved off the display object.
  522. * DisplayObject's `interactive` property must be set to `true` to fire event.
  523. *
  524. * @event PIXI.DisplayObject#pointerout
  525. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  526. */
  527. /**
  528. * Fired when a touch point is placed on the display object.
  529. * DisplayObject's `interactive` property must be set to `true` to fire event.
  530. *
  531. * @event PIXI.DisplayObject#touchstart
  532. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  533. */
  534. /**
  535. * Fired when a touch point is removed from the display object.
  536. * DisplayObject's `interactive` property must be set to `true` to fire event.
  537. *
  538. * @event PIXI.DisplayObject#touchend
  539. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  540. */
  541. /**
  542. * Fired when the operating system cancels a touch.
  543. * DisplayObject's `interactive` property must be set to `true` to fire event.
  544. *
  545. * @event PIXI.DisplayObject#touchcancel
  546. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  547. */
  548. /**
  549. * Fired when a touch point is placed and removed from the display object.
  550. * DisplayObject's `interactive` property must be set to `true` to fire event.
  551. *
  552. * @event PIXI.DisplayObject#tap
  553. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  554. */
  555. /**
  556. * Fired when a touch point is removed outside of the display object that initially
  557. * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
  558. * DisplayObject's `interactive` property must be set to `true` to fire event.
  559. *
  560. * @event PIXI.DisplayObject#touchendoutside
  561. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  562. */
  563. /**
  564. * Fired when a touch point is moved along the display object.
  565. * DisplayObject's `interactive` property must be set to `true` to fire event.
  566. *
  567. * @event PIXI.DisplayObject#touchmove
  568. * @param {PIXI.interaction.InteractionEvent} event - Interaction event
  569. */
  570. }
  571. /**
  572. * Hit tests a point against the display tree, returning the first interactive object that is hit.
  573. *
  574. * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
  575. * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
  576. * to the last rendered root of the associated renderer.
  577. * @return {PIXI.DisplayObject} The hit display object, if any.
  578. */
  579. hitTest(globalPoint, root)
  580. {
  581. // clear the target for our hit test
  582. hitTestEvent.target = null;
  583. // assign the global point
  584. hitTestEvent.data.global = globalPoint;
  585. // ensure safety of the root
  586. if (!root)
  587. {
  588. root = this.renderer._lastObjectRendered;
  589. }
  590. // run the hit test
  591. this.processInteractive(hitTestEvent, root, null, true);
  592. // return our found object - it'll be null if we didn't hit anything
  593. return hitTestEvent.target;
  594. }
  595. /**
  596. * Sets the DOM element which will receive mouse/touch events. This is useful for when you have
  597. * other DOM elements on top of the renderers Canvas element. With this you'll be bale to deletegate
  598. * another DOM element to receive those events.
  599. *
  600. * @param {HTMLCanvasElement} element - the DOM element which will receive mouse and touch events.
  601. * @param {number} [resolution=1] - The resolution / device pixel ratio of the new element (relative to the canvas).
  602. */
  603. setTargetElement(element, resolution = 1)
  604. {
  605. this.removeEvents();
  606. this.interactionDOMElement = element;
  607. this.resolution = resolution;
  608. this.addEvents();
  609. }
  610. /**
  611. * Registers all the DOM events
  612. *
  613. * @private
  614. */
  615. addEvents()
  616. {
  617. if (!this.interactionDOMElement)
  618. {
  619. return;
  620. }
  621. core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
  622. if (windowAlias.navigator.msPointerEnabled)
  623. {
  624. this.interactionDOMElement.style['-ms-content-zooming'] = 'none';
  625. this.interactionDOMElement.style['-ms-touch-action'] = 'none';
  626. }
  627. else if (this.supportsPointerEvents)
  628. {
  629. this.interactionDOMElement.style['touch-action'] = 'none';
  630. }
  631. //miniprogram去除pointer mouse等事件支持
  632. // /**
  633. // * These events are added first, so that if pointer events are normalised, they are fired
  634. // * in the same order as non-normalised events. ie. pointer event 1st, mouse / touch 2nd
  635. // */
  636. // if (this.supportsPointerEvents)
  637. // {
  638. // windowAlias.documentAlias.addEventListener('pointermove', this.onPointerMove, true);
  639. // this.interactionDOMElement.addEventListener('pointerdown', this.onPointerDown, true);
  640. // // pointerout is fired in addition to pointerup (for touch events) and pointercancel
  641. // // we already handle those, so for the purposes of what we do in onPointerOut, we only
  642. // // care about the pointerleave event
  643. // this.interactionDOMElement.addEventListener('pointerleave', this.onPointerOut, true);
  644. // this.interactionDOMElement.addEventListener('pointerover', this.onPointerOver, true);
  645. // windowAlias.addEventListener('pointercancel', this.onPointerCancel, true);
  646. // windowAlias.addEventListener('pointerup', this.onPointerUp, true);
  647. // }
  648. // else
  649. // {
  650. // windowAlias.documentAlias.addEventListener('mousemove', this.onPointerMove, true);
  651. // this.interactionDOMElement.addEventListener('mousedown', this.onPointerDown, true);
  652. // this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
  653. // this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
  654. // windowAlias.addEventListener('mouseup', this.onPointerUp, true);
  655. // }
  656. // always look directly for touch events so that we can provide original data
  657. // In a future version we should change this to being just a fallback and rely solely on
  658. // PointerEvents whenever available
  659. if (this.supportsTouchEvents)
  660. {
  661. this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
  662. this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
  663. this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
  664. this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
  665. }
  666. this.eventsAdded = true;
  667. }
  668. /**
  669. * Removes all the DOM events that were previously registered
  670. *
  671. * @private
  672. */
  673. removeEvents()
  674. {
  675. if (!this.interactionDOMElement)
  676. {
  677. return;
  678. }
  679. core.ticker.shared.remove(this.update, this);
  680. if (windowAlias.navigator.msPointerEnabled)
  681. {
  682. this.interactionDOMElement.style['-ms-content-zooming'] = '';
  683. this.interactionDOMElement.style['-ms-touch-action'] = '';
  684. }
  685. else if (this.supportsPointerEvents)
  686. {
  687. this.interactionDOMElement.style['touch-action'] = '';
  688. }
  689. if (this.supportsPointerEvents)
  690. {
  691. windowAlias.documentAlias.removeEventListener('pointermove', this.onPointerMove, true);
  692. this.interactionDOMElement.removeEventListener('pointerdown', this.onPointerDown, true);
  693. this.interactionDOMElement.removeEventListener('pointerleave', this.onPointerOut, true);
  694. this.interactionDOMElement.removeEventListener('pointerover', this.onPointerOver, true);
  695. windowAlias.removeEventListener('pointercancel', this.onPointerCancel, true);
  696. windowAlias.removeEventListener('pointerup', this.onPointerUp, true);
  697. }
  698. else
  699. {
  700. windowAlias.documentAlias.removeEventListener('mousemove', this.onPointerMove, true);
  701. this.interactionDOMElement.removeEventListener('mousedown', this.onPointerDown, true);
  702. this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
  703. this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
  704. windowAlias.removeEventListener('mouseup', this.onPointerUp, true);
  705. }
  706. if (this.supportsTouchEvents)
  707. {
  708. this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
  709. this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
  710. this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
  711. this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
  712. }
  713. this.interactionDOMElement = null;
  714. this.eventsAdded = false;
  715. }
  716. /**
  717. * Updates the state of interactive objects.
  718. * Invoked by a throttled ticker update from {@link PIXI.ticker.shared}.
  719. *
  720. * @param {number} deltaTime - time delta since last tick
  721. */
  722. update(deltaTime)
  723. {
  724. this._deltaTime += deltaTime;
  725. if (this._deltaTime < this.interactionFrequency)
  726. {
  727. return;
  728. }
  729. this._deltaTime = 0;
  730. if (!this.interactionDOMElement)
  731. {
  732. return;
  733. }
  734. // if the user move the mouse this check has already been done using the mouse move!
  735. if (this.didMove)
  736. {
  737. this.didMove = false;
  738. return;
  739. }
  740. this.cursor = null;
  741. // Resets the flag as set by a stopPropagation call. This flag is usually reset by a user interaction of any kind,
  742. // but there was a scenario of a display object moving under a static mouse cursor.
  743. // In this case, mouseover and mouseevents would not pass the flag test in dispatchEvent function
  744. for (const k in this.activeInteractionData)
  745. {
  746. // eslint-disable-next-line no-prototype-builtins
  747. if (this.activeInteractionData.hasOwnProperty(k))
  748. {
  749. const interactionData = this.activeInteractionData[k];
  750. if (interactionData.originalEvent && interactionData.pointerType !== 'touch')
  751. {
  752. const interactionEvent = this.configureInteractionEventForDOMEvent(
  753. this.eventData,
  754. interactionData.originalEvent,
  755. interactionData
  756. );
  757. this.processInteractive(
  758. interactionEvent,
  759. this.renderer._lastObjectRendered,
  760. this.processPointerOverOut,
  761. true
  762. );
  763. }
  764. }
  765. }
  766. this.setCursorMode(this.cursor);
  767. // TODO
  768. }
  769. /**
  770. * Sets the current cursor mode, handling any callbacks or CSS style changes.
  771. *
  772. * @param {string} mode - cursor mode, a key from the cursorStyles dictionary
  773. */
  774. setCursorMode(mode)
  775. {
  776. mode = mode || 'default';
  777. // if the mode didn't actually change, bail early
  778. if (this.currentCursorMode === mode)
  779. {
  780. return;
  781. }
  782. this.currentCursorMode = mode;
  783. const style = this.cursorStyles[mode];
  784. // only do things if there is a cursor style for it
  785. if (style)
  786. {
  787. switch (typeof style)
  788. {
  789. case 'string':
  790. // string styles are handled as cursor CSS
  791. this.interactionDOMElement.style.cursor = style;
  792. break;
  793. case 'function':
  794. // functions are just called, and passed the cursor mode
  795. style(mode);
  796. break;
  797. case 'object':
  798. // if it is an object, assume that it is a dictionary of CSS styles,
  799. // apply it to the interactionDOMElement
  800. Object.assign(this.interactionDOMElement.style, style);
  801. break;
  802. }
  803. }
  804. else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
  805. {
  806. // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
  807. // for the mode, then assume that the dev wants it to be CSS for the cursor.
  808. this.interactionDOMElement.style.cursor = mode;
  809. }
  810. }
  811. /**
  812. * Dispatches an event on the display object that was interacted with
  813. *
  814. * @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the display object in question
  815. * @param {string} eventString - the name of the event (e.g, mousedown)
  816. * @param {object} eventData - the event data object
  817. * @private
  818. */
  819. dispatchEvent(displayObject, eventString, eventData)
  820. {
  821. if (!eventData.stopped)
  822. {
  823. eventData.currentTarget = displayObject;
  824. eventData.type = eventString;
  825. displayObject.emit(eventString, eventData);
  826. if (displayObject[eventString])
  827. {
  828. displayObject[eventString](eventData);
  829. }
  830. }
  831. }
  832. /**
  833. * Maps x and y coords from a DOM object and maps them correctly to the PixiJS view. The
  834. * resulting value is stored in the point. This takes into account the fact that the DOM
  835. * element could be scaled and positioned anywhere on the screen.
  836. *
  837. * @param {PIXI.Point} point - the point that the result will be stored in
  838. * @param {number} x - the x coord of the position to map
  839. * @param {number} y - the y coord of the position to map
  840. */
  841. mapPositionToPoint(point, x, y)
  842. {
  843. let rect;
  844. // IE 11 fix
  845. if (!this.interactionDOMElement.parentElement)
  846. {
  847. rect = { x: 0, y: 0, width: 0, height: 0 };
  848. }
  849. else
  850. {
  851. rect = this.interactionDOMElement.getBoundingClientRect();
  852. }
  853. const resolutionMultiplier = navigator.isCocoonJS ? this.resolution : (1.0 / this.resolution);
  854. point.x = ((x - rect.left) * (this.interactionDOMElement.width / rect.width)) * resolutionMultiplier;
  855. point.y = ((y - rect.top) * (this.interactionDOMElement.height / rect.height)) * resolutionMultiplier;
  856. }
  857. /**
  858. * This function is provides a neat way of crawling through the scene graph and running a
  859. * specified function on all interactive objects it finds. It will also take care of hit
  860. * testing the interactive objects and passes the hit across in the function.
  861. *
  862. * @private
  863. * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
  864. * is tested for collision
  865. * @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
  866. * that will be hit test (recursively crawls its children)
  867. * @param {Function} [func] - the function that will be called on each interactive object. The
  868. * interactionEvent, displayObject and hit will be passed to the function
  869. * @param {boolean} [hitTest] - this indicates if the objects inside should be hit test against the point
  870. * @param {boolean} [interactive] - Whether the displayObject is interactive
  871. * @return {boolean} returns true if the displayObject hit the point
  872. */
  873. processInteractive(interactionEvent, displayObject, func, hitTest, interactive)
  874. {
  875. if (!displayObject || !displayObject.visible)
  876. {
  877. return false;
  878. }
  879. const point = interactionEvent.data.global;
  880. // Took a little while to rework this function correctly! But now it is done and nice and optimised. ^_^
  881. //
  882. // This function will now loop through all objects and then only hit test the objects it HAS
  883. // to, not all of them. MUCH faster..
  884. // An object will be hit test if the following is true:
  885. //
  886. // 1: It is interactive.
  887. // 2: It belongs to a parent that is interactive AND one of the parents children have not already been hit.
  888. //
  889. // As another little optimisation once an interactive object has been hit we can carry on
  890. // through the scenegraph, but we know that there will be no more hits! So we can avoid extra hit tests
  891. // A final optimisation is that an object is not hit test directly if a child has already been hit.
  892. interactive = displayObject.interactive || interactive;
  893. let hit = false;
  894. let interactiveParent = interactive;
  895. // Flag here can set to false if the event is outside the parents hitArea or mask
  896. let hitTestChildren = true;
  897. // If there is a hitArea, no need to test against anything else if the pointer is not within the hitArea
  898. // There is also no longer a need to hitTest children.
  899. if (displayObject.hitArea)
  900. {
  901. if (hitTest)
  902. {
  903. displayObject.worldTransform.applyInverse(point, this._tempPoint);
  904. if (!displayObject.hitArea.contains(this._tempPoint.x, this._tempPoint.y))
  905. {
  906. hitTest = false;
  907. hitTestChildren = false;
  908. }
  909. else
  910. {
  911. hit = true;
  912. }
  913. }
  914. interactiveParent = false;
  915. }
  916. // If there is a mask, no need to test against anything else if the pointer is not within the mask
  917. else if (displayObject._mask)
  918. {
  919. if (hitTest)
  920. {
  921. if (!displayObject._mask.containsPoint(point))
  922. {
  923. hitTest = false;
  924. hitTestChildren = false;
  925. }
  926. }
  927. }
  928. // ** FREE TIP **! If an object is not interactive or has no buttons in it
  929. // (such as a game scene!) set interactiveChildren to false for that displayObject.
  930. // This will allow PixiJS to completely ignore and bypass checking the displayObjects children.
  931. if (hitTestChildren && displayObject.interactiveChildren && displayObject.children)
  932. {
  933. const children = displayObject.children;
  934. for (let i = children.length - 1; i >= 0; i--)
  935. {
  936. const child = children[i];
  937. // time to get recursive.. if this function will return if something is hit..
  938. const childHit = this.processInteractive(interactionEvent, child, func, hitTest, interactiveParent);
  939. if (childHit)
  940. {
  941. // its a good idea to check if a child has lost its parent.
  942. // this means it has been removed whilst looping so its best
  943. if (!child.parent)
  944. {
  945. continue;
  946. }
  947. // we no longer need to hit test any more objects in this container as we we
  948. // now know the parent has been hit
  949. interactiveParent = false;
  950. // If the child is interactive , that means that the object hit was actually
  951. // interactive and not just the child of an interactive object.
  952. // This means we no longer need to hit test anything else. We still need to run
  953. // through all objects, but we don't need to perform any hit tests.
  954. if (childHit)
  955. {
  956. if (interactionEvent.target)
  957. {
  958. hitTest = false;
  959. }
  960. hit = true;
  961. }
  962. }
  963. }
  964. }
  965. // no point running this if the item is not interactive or does not have an interactive parent.
  966. if (interactive)
  967. {
  968. // if we are hit testing (as in we have no hit any objects yet)
  969. // We also don't need to worry about hit testing if once of the displayObjects children
  970. // has already been hit - but only if it was interactive, otherwise we need to keep
  971. // looking for an interactive child, just in case we hit one
  972. if (hitTest && !interactionEvent.target)
  973. {
  974. // already tested against hitArea if it is defined
  975. if (!displayObject.hitArea && displayObject.containsPoint)
  976. {
  977. if (displayObject.containsPoint(point))
  978. {
  979. hit = true;
  980. }
  981. }
  982. }
  983. if (displayObject.interactive)
  984. {
  985. if (hit && !interactionEvent.target)
  986. {
  987. interactionEvent.target = displayObject;
  988. }
  989. if (func)
  990. {
  991. func(interactionEvent, displayObject, !!hit);
  992. }
  993. }
  994. }
  995. return hit;
  996. }
  997. /**
  998. * Is called when the pointer button is pressed down on the renderer element
  999. *
  1000. * @private
  1001. * @param {PointerEvent} originalEvent - The DOM event of a pointer button being pressed down
  1002. */
  1003. onPointerDown(originalEvent)
  1004. {
  1005. // miniprogram去除pointerType ==='touch'判断
  1006. // // if we support touch events, then only use those for touch events, not pointer events
  1007. // if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
  1008. const events = this.normalizeToPointerData(originalEvent);
  1009. /**
  1010. * No need to prevent default on natural pointer events, as there are no side effects
  1011. * Normalized events, however, may have the double mousedown/touchstart issue on the native android browser,
  1012. * so still need to be prevented.
  1013. */
  1014. // Guaranteed that there will be at least one event in events, and all events must have the same pointer type
  1015. if (this.autoPreventDefault && events[0].isNormalized)
  1016. {
  1017. originalEvent.preventDefault();
  1018. }
  1019. const eventLen = events.length;
  1020. for (let i = 0; i < eventLen; i++)
  1021. {
  1022. const event = events[i];
  1023. const interactionData = this.getInteractionDataForPointerId(event);
  1024. const interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
  1025. interactionEvent.data.originalEvent = originalEvent;
  1026. this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerDown, true);
  1027. this.emit('pointerdown', interactionEvent);
  1028. if (event.pointerType === 'touch')
  1029. {
  1030. this.emit('touchstart', interactionEvent);
  1031. }
  1032. // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
  1033. else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
  1034. {
  1035. const isRightButton = event.button === 2;
  1036. this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
  1037. }
  1038. }
  1039. }
  1040. /**
  1041. * Processes the result of the pointer down check and dispatches the event if need be
  1042. *
  1043. * @private
  1044. * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
  1045. * @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
  1046. * @param {boolean} hit - the result of the hit test on the display object
  1047. */
  1048. processPointerDown(interactionEvent, displayObject, hit)
  1049. {
  1050. const data = interactionEvent.data;
  1051. const id = interactionEvent.data.identifier;
  1052. if (hit)
  1053. {
  1054. if (!displayObject.trackedPointers[id])
  1055. {
  1056. displayObject.trackedPointers[id] = new InteractionTrackingData(id);
  1057. }
  1058. this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
  1059. if (data.pointerType === 'touch')
  1060. {
  1061. this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
  1062. }
  1063. else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
  1064. {
  1065. const isRightButton = data.button === 2;
  1066. if (isRightButton)
  1067. {
  1068. displayObject.trackedPointers[id].rightDown = true;
  1069. }
  1070. else
  1071. {
  1072. displayObject.trackedPointers[id].leftDown = true;
  1073. }
  1074. this.dispatchEvent(displayObject, isRightButton ? 'rightdown' : 'mousedown', interactionEvent);
  1075. }
  1076. }
  1077. }
  1078. /**
  1079. * Is called when the pointer button is released on the renderer element
  1080. *
  1081. * @private
  1082. * @param {PointerEvent} originalEvent - The DOM event of a pointer button being released
  1083. * @param {boolean} cancelled - true if the pointer is cancelled
  1084. * @param {Function} func - Function passed to {@link processInteractive}
  1085. */
  1086. onPointerComplete(originalEvent, cancelled, func)
  1087. {
  1088. const events = this.normalizeToPointerData(originalEvent);
  1089. const eventLen = events.length;
  1090. // if the event wasn't targeting our canvas, then consider it to be pointerupoutside
  1091. // in all cases (unless it was a pointercancel)
  1092. const eventAppend = originalEvent.target !== this.interactionDOMElement ? 'outside' : '';
  1093. for (let i = 0; i < eventLen; i++)
  1094. {
  1095. const event = events[i];
  1096. const interactionData = this.getInteractionDataForPointerId(event);
  1097. const interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
  1098. interactionEvent.data.originalEvent = originalEvent;
  1099. // perform hit testing for events targeting our canvas or cancel events
  1100. this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, func, cancelled || !eventAppend);
  1101. this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
  1102. if (event.pointerType === 'mouse' || event.pointerType === 'pen')
  1103. {
  1104. const isRightButton = event.button === 2;
  1105. this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
  1106. }
  1107. else if (event.pointerType === 'touch')
  1108. {
  1109. this.emit(cancelled ? 'touchcancel' : `touchend${eventAppend}`, interactionEvent);
  1110. this.releaseInteractionDataForPointerId(event.pointerId, interactionData);
  1111. }
  1112. }
  1113. }
  1114. /**
  1115. * Is called when the pointer button is cancelled
  1116. *
  1117. * @private
  1118. * @param {PointerEvent} event - The DOM event of a pointer button being released
  1119. */
  1120. onPointerCancel(event)
  1121. {
  1122. //miniprogram去除判断
  1123. // // if we support touch events, then only use those for touch events, not pointer events
  1124. // if (this.supportsTouchEvents && event.pointerType === 'touch') return;
  1125. this.onPointerComplete(event, true, this.processPointerCancel);
  1126. }
  1127. /**
  1128. * Processes the result of the pointer cancel check and dispatches the event if need be
  1129. *
  1130. * @private
  1131. * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
  1132. * @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
  1133. */
  1134. processPointerCancel(interactionEvent, displayObject)
  1135. {
  1136. const data = interactionEvent.data;
  1137. const id = interactionEvent.data.identifier;
  1138. if (displayObject.trackedPointers[id] !== undefined)
  1139. {
  1140. delete displayObject.trackedPointers[id];
  1141. this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
  1142. if (data.pointerType === 'touch')
  1143. {
  1144. this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
  1145. }
  1146. }
  1147. }
  1148. /**
  1149. * Is called when the pointer button is released on the renderer element
  1150. *
  1151. * @private
  1152. * @param {PointerEvent} event - The DOM event of a pointer button being released
  1153. */
  1154. onPointerUp(event)
  1155. {
  1156. // miniprogram去除判断
  1157. // // if we support touch events, then only use those for touch events, not pointer events
  1158. // if (this.supportsTouchEvents && event.pointerType === 'touch') return;
  1159. this.onPointerComplete(event, false, this.processPointerUp);
  1160. }
  1161. /**
  1162. * Processes the result of the pointer up check and dispatches the event if need be
  1163. *
  1164. * @private
  1165. * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
  1166. * @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
  1167. * @param {boolean} hit - the result of the hit test on the display object
  1168. */
  1169. processPointerUp(interactionEvent, displayObject, hit)
  1170. {
  1171. const data = interactionEvent.data;
  1172. const id = interactionEvent.data.identifier;
  1173. const trackingData = displayObject.trackedPointers[id];
  1174. const isTouch = data.pointerType === 'touch';
  1175. const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
  1176. // need to track mouse down status in the mouse block so that we can emit
  1177. // event in a later block
  1178. let isMouseTap = false;
  1179. // Mouse only
  1180. if (isMouse)
  1181. {
  1182. const isRightButton = data.button === 2;
  1183. const flags = InteractionTrackingData.FLAGS;
  1184. const test = isRightButton ? flags.RIGHT_DOWN : flags.LEFT_DOWN;
  1185. const isDown = trackingData !== undefined && (trackingData.flags & test);
  1186. if (hit)
  1187. {
  1188. this.dispatchEvent(displayObject, isRightButton ? 'rightup' : 'mouseup', interactionEvent);
  1189. if (isDown)
  1190. {
  1191. this.dispatchEvent(displayObject, isRightButton ? 'rightclick' : 'click', interactionEvent);
  1192. // because we can confirm that the mousedown happened on this object, flag for later emit of pointertap
  1193. isMouseTap = true;
  1194. }
  1195. }
  1196. else if (isDown)
  1197. {
  1198. this.dispatchEvent(displayObject, isRightButton ? 'rightupoutside' : 'mouseupoutside', interactionEvent);
  1199. }
  1200. // update the down state of the tracking data
  1201. if (trackingData)
  1202. {
  1203. if (isRightButton)
  1204. {
  1205. trackingData.rightDown = false;
  1206. }
  1207. else
  1208. {
  1209. trackingData.leftDown = false;
  1210. }
  1211. }
  1212. }
  1213. // Pointers and Touches, and Mouse
  1214. if (hit)
  1215. {
  1216. this.dispatchEvent(displayObject, 'pointerup', interactionEvent);
  1217. if (isTouch) this.dispatchEvent(displayObject, 'touchend', interactionEvent);
  1218. if (trackingData)
  1219. {
  1220. // emit pointertap if not a mouse, or if the mouse block decided it was a tap
  1221. if (!isMouse || isMouseTap)
  1222. {
  1223. this.dispatchEvent(displayObject, 'pointertap', interactionEvent);
  1224. }
  1225. if (isTouch)
  1226. {
  1227. this.dispatchEvent(displayObject, 'tap', interactionEvent);
  1228. // touches are no longer over (if they ever were) when we get the touchend
  1229. // so we should ensure that we don't keep pretending that they are
  1230. trackingData.over = false;
  1231. }
  1232. }
  1233. }
  1234. else if (trackingData)
  1235. {
  1236. this.dispatchEvent(displayObject, 'pointerupoutside', interactionEvent);
  1237. if (isTouch) this.dispatchEvent(displayObject, 'touchendoutside', interactionEvent);
  1238. }
  1239. // Only remove the tracking data if there is no over/down state still associated with it
  1240. if (trackingData && trackingData.none)
  1241. {
  1242. delete displayObject.trackedPointers[id];
  1243. }
  1244. }
  1245. /**
  1246. * Is called when the pointer moves across the renderer element
  1247. *
  1248. * @private
  1249. * @param {PointerEvent} originalEvent - The DOM event of a pointer moving
  1250. */
  1251. onPointerMove(originalEvent)
  1252. {
  1253. // miniprogram去除判断
  1254. // // if we support touch events, then only use those for touch events, not pointer events
  1255. // if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
  1256. const events = this.normalizeToPointerData(originalEvent);
  1257. if (events[0].pointerType === 'mouse' || events[0].pointerType === 'pen')
  1258. {
  1259. this.didMove = true;
  1260. this.cursor = null;
  1261. }
  1262. const eventLen = events.length;
  1263. for (let i = 0; i < eventLen; i++)
  1264. {
  1265. const event = events[i];
  1266. const interactionData = this.getInteractionDataForPointerId(event);
  1267. const interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
  1268. interactionEvent.data.originalEvent = originalEvent;
  1269. const interactive = event.pointerType === 'touch' ? this.moveWhenInside : true;
  1270. this.processInteractive(
  1271. interactionEvent,
  1272. this.renderer._lastObjectRendered,
  1273. this.processPointerMove,
  1274. interactive
  1275. );
  1276. this.emit('pointermove', interactionEvent);
  1277. if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
  1278. if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
  1279. }
  1280. if (events[0].pointerType === 'mouse')
  1281. {
  1282. this.setCursorMode(this.cursor);
  1283. // TODO BUG for parents interactive object (border order issue)
  1284. }
  1285. }
  1286. /**
  1287. * Processes the result of the pointer move check and dispatches the event if need be
  1288. *
  1289. * @private
  1290. * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
  1291. * @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
  1292. * @param {boolean} hit - the result of the hit test on the display object
  1293. */
  1294. processPointerMove(interactionEvent, displayObject, hit)
  1295. {
  1296. const data = interactionEvent.data;
  1297. const isTouch = data.pointerType === 'touch';
  1298. const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
  1299. if (isMouse)
  1300. {
  1301. this.processPointerOverOut(interactionEvent, displayObject, hit);
  1302. }
  1303. if (!this.moveWhenInside || hit)
  1304. {
  1305. this.dispatchEvent(displayObject, 'pointermove', interactionEvent);
  1306. if (isTouch) this.dispatchEvent(displayObject, 'touchmove', interactionEvent);
  1307. if (isMouse) this.dispatchEvent(displayObject, 'mousemove', interactionEvent);
  1308. }
  1309. }
  1310. /**
  1311. * Is called when the pointer is moved out of the renderer element
  1312. *
  1313. * @private
  1314. * @param {PointerEvent} originalEvent - The DOM event of a pointer being moved out
  1315. */
  1316. onPointerOut(originalEvent)
  1317. {
  1318. // if we support touch events, then only use those for touch events, not pointer events
  1319. if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
  1320. const events = this.normalizeToPointerData(originalEvent);
  1321. // Only mouse and pointer can call onPointerOut, so events will always be length 1
  1322. const event = events[0];
  1323. if (event.pointerType === 'mouse')
  1324. {
  1325. this.mouseOverRenderer = false;
  1326. this.setCursorMode(null);
  1327. }
  1328. const interactionData = this.getInteractionDataForPointerId(event);
  1329. const interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
  1330. interactionEvent.data.originalEvent = event;
  1331. this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
  1332. this.emit('pointerout', interactionEvent);
  1333. if (event.pointerType === 'mouse' || event.pointerType === 'pen')
  1334. {
  1335. this.emit('mouseout', interactionEvent);
  1336. }
  1337. else
  1338. {
  1339. // we can get touchleave events after touchend, so we want to make sure we don't
  1340. // introduce memory leaks
  1341. this.releaseInteractionDataForPointerId(interactionData.identifier);
  1342. }
  1343. }
  1344. /**
  1345. * Processes the result of the pointer over/out check and dispatches the event if need be
  1346. *
  1347. * @private
  1348. * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
  1349. * @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
  1350. * @param {boolean} hit - the result of the hit test on the display object
  1351. */
  1352. processPointerOverOut(interactionEvent, displayObject, hit)
  1353. {
  1354. const data = interactionEvent.data;
  1355. const id = interactionEvent.data.identifier;
  1356. const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
  1357. let trackingData = displayObject.trackedPointers[id];
  1358. // if we just moused over the display object, then we need to track that state
  1359. if (hit && !trackingData)
  1360. {
  1361. trackingData = displayObject.trackedPointers[id] = new InteractionTrackingData(id);
  1362. }
  1363. if (trackingData === undefined) return;
  1364. if (hit && this.mouseOverRenderer)
  1365. {
  1366. if (!trackingData.over)
  1367. {
  1368. trackingData.over = true;
  1369. this.dispatchEvent(displayObject, 'pointerover', interactionEvent);
  1370. if (isMouse)
  1371. {
  1372. this.dispatchEvent(displayObject, 'mouseover', interactionEvent);
  1373. }
  1374. }
  1375. // only change the cursor if it has not already been changed (by something deeper in the
  1376. // display tree)
  1377. if (isMouse && this.cursor === null)
  1378. {
  1379. this.cursor = displayObject.cursor;
  1380. }
  1381. }
  1382. else if (trackingData.over)
  1383. {
  1384. trackingData.over = false;
  1385. this.dispatchEvent(displayObject, 'pointerout', this.eventData);
  1386. if (isMouse)
  1387. {
  1388. this.dispatchEvent(displayObject, 'mouseout', interactionEvent);
  1389. }
  1390. // if there is no mouse down information for the pointer, then it is safe to delete
  1391. if (trackingData.none)
  1392. {
  1393. delete displayObject.trackedPointers[id];
  1394. }
  1395. }
  1396. }
  1397. /**
  1398. * Is called when the pointer is moved into the renderer element
  1399. *
  1400. * @private
  1401. * @param {PointerEvent} originalEvent - The DOM event of a pointer button being moved into the renderer view
  1402. */
  1403. onPointerOver(originalEvent)
  1404. {
  1405. const events = this.normalizeToPointerData(originalEvent);
  1406. // Only mouse and pointer can call onPointerOver, so events will always be length 1
  1407. const event = events[0];
  1408. const interactionData = this.getInteractionDataForPointerId(event);
  1409. const interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
  1410. interactionEvent.data.originalEvent = event;
  1411. if (event.pointerType === 'mouse')
  1412. {
  1413. this.mouseOverRenderer = true;
  1414. }
  1415. this.emit('pointerover', interactionEvent);
  1416. if (event.pointerType === 'mouse' || event.pointerType === 'pen')
  1417. {
  1418. this.emit('mouseover', interactionEvent);
  1419. }
  1420. }
  1421. /**
  1422. * Get InteractionData for a given pointerId. Store that data as well
  1423. *
  1424. * @private
  1425. * @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
  1426. * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
  1427. */
  1428. getInteractionDataForPointerId(event)
  1429. {
  1430. const pointerId = event.pointerId;
  1431. let interactionData;
  1432. if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
  1433. {
  1434. interactionData = this.mouse;
  1435. }
  1436. else if (this.activeInteractionData[pointerId])
  1437. {
  1438. interactionData = this.activeInteractionData[pointerId];
  1439. }
  1440. else
  1441. {
  1442. interactionData = this.interactionDataPool.pop() || new InteractionData();
  1443. interactionData.identifier = pointerId;
  1444. this.activeInteractionData[pointerId] = interactionData;
  1445. }
  1446. // copy properties from the event, so that we can make sure that touch/pointer specific
  1447. // data is available
  1448. interactionData.copyEvent(event);
  1449. return interactionData;
  1450. }
  1451. /**
  1452. * Return unused InteractionData to the pool, for a given pointerId
  1453. *
  1454. * @private
  1455. * @param {number} pointerId - Identifier from a pointer event
  1456. */
  1457. releaseInteractionDataForPointerId(pointerId)
  1458. {
  1459. const interactionData = this.activeInteractionData[pointerId];
  1460. if (interactionData)
  1461. {
  1462. delete this.activeInteractionData[pointerId];
  1463. interactionData.reset();
  1464. this.interactionDataPool.push(interactionData);
  1465. }
  1466. }
  1467. /**
  1468. * Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
  1469. *
  1470. * @private
  1471. * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
  1472. * @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
  1473. * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
  1474. * with the InteractionEvent
  1475. * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
  1476. */
  1477. configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
  1478. {
  1479. interactionEvent.data = interactionData;
  1480. this.mapPositionToPoint(interactionData.global, pointerEvent.clientX, pointerEvent.clientY);
  1481. // 去除CocoonJs判断
  1482. // This is the way InteractionManager processed touch events before the refactoring, so I've kept
  1483. // it here. But it doesn't make that much sense to me, since mapPositionToPoint already factors
  1484. // in this.resolution, so this just divides by this.resolution twice for touch events...
  1485. // if (navigator.isCocoonJS && pointerEvent.pointerType === 'touch')
  1486. // {
  1487. // interactionData.global.x = interactionData.global.x / this.resolution;
  1488. // interactionData.global.y = interactionData.global.y / this.resolution;
  1489. // }
  1490. // Not really sure why this is happening, but it's how a previous version handled things
  1491. if (pointerEvent.pointerType === 'touch')
  1492. {
  1493. pointerEvent.globalX = interactionData.global.x;
  1494. pointerEvent.globalY = interactionData.global.y;
  1495. }
  1496. interactionData.originalEvent = pointerEvent;
  1497. interactionEvent.reset();
  1498. return interactionEvent;
  1499. }
  1500. /**
  1501. * Ensures that the original event object contains all data that a regular pointer event would have
  1502. *
  1503. * @private
  1504. * @param {TouchEvent|MouseEvent|PointerEvent} event - The original event data from a touch or mouse event
  1505. * @return {PointerEvent[]} An array containing a single normalized pointer event, in the case of a pointer
  1506. * or mouse event, or a multiple normalized pointer events if there are multiple changed touches
  1507. */
  1508. normalizeToPointerData(event)
  1509. {
  1510. const normalizedEvents = [];
  1511. // miniprogram去除event instanceof TouchEvent
  1512. // if (this.supportsTouchEvents && event instanceof TouchEvent)
  1513. if (this.supportsTouchEvents)
  1514. {
  1515. for (let i = 0, li = event.changedTouches.length; i < li; i++)
  1516. {
  1517. const touch = event.changedTouches[i];
  1518. if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
  1519. if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
  1520. if (typeof touch.isPrimary === 'undefined')
  1521. {
  1522. touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
  1523. }
  1524. if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
  1525. if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
  1526. if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
  1527. if (typeof touch.tiltY === 'undefined') touch.tiltY = 0;
  1528. if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
  1529. if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
  1530. if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
  1531. if (typeof touch.twist === 'undefined') touch.twist = 0;
  1532. if (typeof touch.tangentialPressure === 'undefined') touch.tangentialPressure = 0;
  1533. // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
  1534. // support, and the fill ins are not quite the same
  1535. // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
  1536. // left is not 0,0 on the page
  1537. if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
  1538. if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
  1539. // mark the touch as normalized, just so that we know we did it
  1540. touch.isNormalized = true;
  1541. normalizedEvents.push(touch);
  1542. }
  1543. }
  1544. // apparently PointerEvent subclasses MouseEvent, so yay
  1545. else if (event instanceof MouseEvent && (!this.supportsPointerEvents || !(event instanceof windowAlias.PointerEvent)))
  1546. {
  1547. if (typeof event.isPrimary === 'undefined') event.isPrimary = true;
  1548. if (typeof event.width === 'undefined') event.width = 1;
  1549. if (typeof event.height === 'undefined') event.height = 1;
  1550. if (typeof event.tiltX === 'undefined') event.tiltX = 0;
  1551. if (typeof event.tiltY === 'undefined') event.tiltY = 0;
  1552. if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
  1553. if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
  1554. if (typeof event.pressure === 'undefined') event.pressure = 0.5;
  1555. if (typeof event.twist === 'undefined') event.twist = 0;
  1556. if (typeof event.tangentialPressure === 'undefined') event.tangentialPressure = 0;
  1557. // mark the mouse event as normalized, just so that we know we did it
  1558. event.isNormalized = true;
  1559. normalizedEvents.push(event);
  1560. }
  1561. else
  1562. {
  1563. normalizedEvents.push(event);
  1564. }
  1565. return normalizedEvents;
  1566. }
  1567. /**
  1568. * Destroys the interaction manager
  1569. *
  1570. */
  1571. destroy()
  1572. {
  1573. this.removeEvents();
  1574. this.removeAllListeners();
  1575. this.renderer = null;
  1576. this.mouse = null;
  1577. this.eventData = null;
  1578. this.interactionDOMElement = null;
  1579. this.onPointerDown = null;
  1580. this.processPointerDown = null;
  1581. this.onPointerUp = null;
  1582. this.processPointerUp = null;
  1583. this.onPointerCancel = null;
  1584. this.processPointerCancel = null;
  1585. this.onPointerMove = null;
  1586. this.processPointerMove = null;
  1587. this.onPointerOut = null;
  1588. this.processPointerOverOut = null;
  1589. this.onPointerOver = null;
  1590. this._tempPoint = null;
  1591. }
  1592. }
  1593. core.WebGLRenderer.registerPlugin('interaction', InteractionManager);
  1594. core.CanvasRenderer.registerPlugin('interaction', InteractionManager);