watch.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import {ElementRef} from '@angular/core';
  2. import extend from 'lodash-es/extend';
  3. import {IncrementalStackManager} from '@ex-helpers/stack';
  4. import {bounds} from './bounds';
  5. interface SizeCollectionItem {
  6. triggerCheck: () => void;
  7. startCountdown: () => void;
  8. callback: (height: number, width: number) => void;
  9. observer?: MutationObserver;
  10. id?: number;
  11. legacy?: boolean;
  12. }
  13. const DEFAULT_OPTIONS = {
  14. attributes: true,
  15. childList: true,
  16. characterData: true
  17. };
  18. export class ElementWatcher {
  19. private _watchSizeCollection: any;
  20. public nativeElement: HTMLElement;
  21. constructor(ref: HTMLElement | ElementRef) {
  22. this.nativeElement = ref instanceof ElementRef ? ref.nativeElement : ref;
  23. }
  24. watchSize(callback: (height: number, width: number) => void,
  25. options?: { attributes?: boolean, childList?: boolean, characterData?: boolean },
  26. params: { timeout?: number, legacy: boolean } = {legacy: false}) {
  27. if (!this._watchSizeCollection) {
  28. Object.defineProperty(this, '_watchSizeCollection', {
  29. enumerable: false,
  30. configurable: true,
  31. writable: false,
  32. value: new IncrementalStackManager<SizeCollectionItem>('id')
  33. });
  34. }
  35. options = extend(DEFAULT_OPTIONS, options);
  36. let timeout: any;
  37. let lastHeight: number;
  38. let lastWidth: number;
  39. const triggerCheck = () => {
  40. const sizes = bounds(this.nativeElement).size;
  41. if (lastWidth === sizes.width && lastHeight === sizes.height) {
  42. return;
  43. }
  44. lastHeight = sizes.height;
  45. lastWidth = sizes.width;
  46. callback(sizes.height, sizes.width);
  47. };
  48. const startCountdown = () => {
  49. clearTimeout(timeout);
  50. timeout = setTimeout(() => {
  51. triggerCheck();
  52. }, params.timeout || 100);
  53. };
  54. const item = this._watchSizeCollection.add({
  55. callback,
  56. startCountdown,
  57. triggerCheck
  58. });
  59. if (MutationObserver && params.legacy === false) {
  60. const observer = new MutationObserver(mutations => {
  61. startCountdown();
  62. });
  63. item.observer = observer;
  64. item.legacy = false;
  65. startCountdown();
  66. observer.observe(this.nativeElement, options);
  67. return () => observer.disconnect();
  68. } else {
  69. item.legacy = true;
  70. const checkInterval = setInterval(() => {
  71. triggerCheck();
  72. }, 200);
  73. startCountdown();
  74. return () => clearInterval(checkInterval);
  75. }
  76. }
  77. triggerSizeCheck() {
  78. if (this._watchSizeCollection) {
  79. this._watchSizeCollection.execute('triggerCheck');
  80. }
  81. }
  82. watchChanges(callback: MutationCallback, options: MutationObserverInit) {
  83. if (MutationObserver) {
  84. const observer = new MutationObserver(callback);
  85. observer.observe(this.nativeElement, options);
  86. return () => observer.disconnect();
  87. }
  88. return () => {
  89. };
  90. }
  91. }
  92. export function watchElement(ref: HTMLElement | ElementRef) {
  93. return new ElementWatcher(ref);
  94. }