gmurphey / ember-did-resize-modifier

MIT License
34 stars 10 forks source link

ResizeObserver + polyfill as alternative to element-resize-detector #156

Open nickschot opened 4 years ago

nickschot commented 4 years ago

I've noticed the element-resize-detector is quite a huge library for what it does. Were using the native ResizeObserver + a polyfill like https://www.npmjs.com/package/resize-observer-polyfill (2.44kB gzipped) considered? Seems like it would work just as well and save many KB. The polyfill could even be loaded conditionally based on ember's config/targets.js feature.

image

lukemelia commented 4 years ago

Sounds like a great idea. I came across this addon when looking for a ResizeObserver-based modifier -- that was my expectation of how this problem would likely be solved.

ijlee2 commented 4 years ago

I'd like to see how well a modifier based on ResizeObserver works too. :)

My application is for ember-container-query, whereby O(10¹) resizes can happen at the same time (not just 1 or 2 resizes). I'm interested to see if ResizeObserver performs as well as (or better than) what element-resize-detector did.

nickschot commented 4 years ago

So I looked into this a bit and found out a couple of things while tinkering with a ResizeObserver proof of concept for this addon:

  1. The native ResizeObserver has no "don't run on insertion" type implementation, has to be worked around manually by ignoring the first call (no big deal here).
  2. It is most performant to use a single ResizeObserver for the entire app. See: https://groups.google.com/a/chromium.org/g/blink-dev/c/z6ienONUb5A/m/F5-VcUZtBAAJ?pli=1 The problem with this is that you cannot distinguish between multiple modifiers on the same element. We'd have to do some manual bookkeeping here to know when to actually unobserve the element.
  3. ResizeObserver passes the callback a ResizeObserverEntry by default with some pretty useful information including the new sizes of the element. In many use cases of this modifier it would be very beneficial to also provide this information through this modifier instead of just the element as it saves on layout calculations (i.e. having to call getBoundingClientRect manually in the callback forces another layout calculation). I'd prefer to simply pass the ResizeObserverEntry as the only argument, but that would be a breaking change.

Thoughts are welcome!

voltidev commented 4 years ago

I built ember-resize-observer-service to solve exactly the problem with multiple ResizeObserver instances. Feel free to use it to migrate this addon to ResizeObserver API, if it makes sense.

Building a size-responsive modifier with it is pretty straight forward:

export default class OnResizeModifier extends Modifier {
  @service resizeObserver;

  didInstall() {
    this.resizeObserver.observe(this.element, this.handleResize);
  }

  willRemove() {
    this.resizeObserver.unobserve(this.element, this.handleResize);
  }

  @action
  handleResize(...args) {
    let [callback] = this.args.positional;
    callback(...args);
  }
}

@nickschot If you need a complete ResizeObserver based solution with a single instance optimization, I built ember-on-resize-modifier.

And in case if you need a polyfill:

ember install ember-resize-observer-polyfill
nickschot commented 4 years ago

If it already exists it makes sense to migrate to that addon in my opinion. As far is I can see (after a very quick peek) the main feature difference is that your addon also runs on insert. For my use cases that doesn’t matter though.