Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Get access to the image in observable creation #304

Closed
tjoskar opened this issue Feb 3, 2018 · 4 comments
Closed

Feature request: Get access to the image in observable creation #304

tjoskar opened this issue Feb 3, 2018 · 4 comments

Comments

@tjoskar
Copy link
Owner

tjoskar commented Feb 3, 2018

Motivation and implementation

Right now it's possible to pass an observable which is a great way to extend the library. However, it is a bit limited since the uses can't get access to the image in a simple way.

One of the most common issues is when the user doesn't use the window to scroll or using a 3-part library that adds extra scroll layers (eg. ionic, ng material design, etc), and it can sometimes be hard to find out which element that actually scrolls. One way to solve this could be to use an Intersection Observer (which also can give a performance boost). Eg.

@Component({
  selector: 'intersection-observer',
  template: `<img #img [defaultImage]="defaultImage" [lazyLoad]="image" [scrollObservable]="subject">`,
})
export class IntersectionObserverComponent {
  @ViewChild('img') img;
  subject = new Subject();
  defaultImage = 'https://www.placecage.com/1000/1000';
  image = 'https://images.unsplash.com/photo-1467932760935-519284fc87fa?dpr=2&auto=compress,format&fit=crop&w=1199&h=800&q=80';

  ngAfterViewInit() {
    const config = {
      rootMargin: '50px 50px',
      threshold: 0.01
    };

    function onIntersection(entries) {
      entries.forEach(entry => {
        if (entry.intersectionRatio > 0) {
          observer.unobserve(entry.target);
          this.subject.next();
        }
      });
    }
    const observer = new IntersectionObserver(onIntersection, config);
    observer.observe(this.img.nativeElement);
  }
}

This is however quite cumbersome.

What we can do is to accept a function as scrollObservable, something like:

if (!this.scrollObservable) {
  const windowTarget = isWindowDefined() ? window : undefined;
  scrollObservable = getScrollListener(this.scrollTarget || windowTarget);
} else if (typeof this.scrollObservable === 'function') {
  scrollObservable = this.scrollObservable(image, offset) // and maybe more
} else {
  scrollObservable = this.scrollObservable.startWith('');
}

We can not rewrite the component above to:

@Component({
  selector: 'intersection-observer',
  template: `<img [defaultImage]="defaultImage" [lazyLoad]="image"
 [scrollObservable]="startLazyload" offest="50">`,
})
export class IntersectionObserverComponent {
  defaultImage = 'https://www.placecage.com/1000/1000';
  image = 'https://images.unsplash.com/photo-1467932760935-519284fc87fa?dpr=2&auto=compress,format&fit=crop&w=1199&h=800&q=80';

  startLazyload(image, offset) {
    Observable.create(observer => {
      const intersectionObserver = new IntersectionObserver(entries => {
        entries.forEach(entry => {
          if (entry.intersectionRatio > 0) {
            observer.next();
          }
        });
      }, {
        rootMargin: offset ? `${offset}px` : undefined,
        threshold: 0.01
      });
      intersectionObserver.observe(image);
      return () => observer.unobserve(image);
    });
  }
}

Better but still a bit noisy, especially when the user wants to use IntersectionObserver on all images.

A way to solve this could be too pass a default Observable in NgModule:

import { intersectionObserverLazyLoad } from 'some-place';

@NgModule({
    declarations: [ AppComponent ],
    imports: [ BrowserModule, lazyLoadImage({
      observable: intersectionObserverLazyLoad
    })],
    bootstrap: [ AppComponent ]
})
export class MyAppModule {}

We can no rewrite the component above to:

@Component({
  selector: 'intersection-observer',
  template: `<img [defaultImage]="defaultImage" [lazyLoad]="image" offest="50">`,
})
export class IntersectionObserverComponent {
  defaultImage = 'https://www.placecage.com/1000/1000';
  image = 'https://images.unsplash.com/photo-1467932760935-519284fc87fa?dpr=2&auto=compress,format&fit=crop&w=1199&h=800&q=80';
}

The ability to pass default functions in NgModule could also open up doors to create custom functions for isVisible and loadImage but that is a later story.

Would solve: #287, #195, #286, #285, #275, #259 and more. Most of the issues are solved in other ways but it would be a nice if we could give the user more flexibility when creating an observable.

What do you think @sapierens, @rimlin?

@rimlin
Copy link
Collaborator

rimlin commented Feb 3, 2018

@tjoskar as i understand you want to enhance scrollObservable by provide function as option for scrollObservable, which will return Observable, which is responsible to emit image load event? If it is, i think is really cool idea!
But i think is better to use object instead of enumerate arguments:

...
} else if (typeof this.scrollObservable === 'function') {
  scrollObservable = this.scrollObservable({
    image, 
    offset
  });
} else {
...

@tjoskar
Copy link
Owner Author

tjoskar commented Feb 3, 2018

provide function as option for scrollObservable, which will return Observable, which is responsible to emit image load event

Correct, I may also want to rename scrollObservable but that would be a breaking change.

i think is better to use object instead of enumerate arguments

Agree!

@sapierens
Copy link
Collaborator

I think this is a nice idea, especially passing a default observer.

i think is better to use object instead of enumerate arguments

I also agree with this.

@tjoskar
Copy link
Owner Author

tjoskar commented Nov 7, 2018

Fixed in #365 and in version 2.1.0

@tjoskar tjoskar closed this as completed Nov 7, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants