Skip to content

Instantly share code, notes, and snippets.

@Bengejd
Last active April 30, 2020 16:51
Show Gist options
  • Save Bengejd/3009ea4674eb60a729ec07f26e2e9bd6 to your computer and use it in GitHub Desktop.
Save Bengejd/3009ea4674eb60a729ec07f26e2e9bd6 to your computer and use it in GitHub Desktop.
Ionic - Auto play/pause videos on scroll.
import { Directive } from '@angular/core';
@Directive({
selector: 'video'
})
export class AutoplayVideoDirective { }
import {AutoplayVideoDirective} from '../../directives/autoplay-video/autoplay-video';
import {Component, ContentChildren, ElementRef, NgZone, OnDestroy, OnInit, QueryList} from '@angular/core';
/*
* Make sure you install the 'intersection-observer' polyfill package from npm, and import it into your app.module.ts
*/
@Component({
selector: 'autoplay-content',
template: `<ng-content></ng-content>`
})
export class AutoplayContentComponent implements OnInit, OnDestroy {
@ContentChildren(
AutoplayVideoDirective,
{ read: ElementRef, descendants: true })
autoPlayVideoRefs: QueryList<AutoplayVideoDirective>;
private intersectionObserver: IntersectionObserver;
private mutationObserver: MutationObserver;
private play: Promise<any>;
constructor(private element: ElementRef,
public ngZone: NgZone) { }
public ngOnInit() {
// we can run this outside the ngZone, no need
// to trigger change detection
this.ngZone.runOutsideAngular(() => {
this.intersectionObserver = this.getIntersectionObserver();
this.mutationObserver = this.getMutationObserver(this.element.nativeElement);
});
}
// clean things ups
public ngOnDestroy() {
if (this.intersectionObserver) {
this.intersectionObserver.disconnect();
}
if (this.mutationObserver) {
this.mutationObserver.disconnect();
}
}
// construct the InterSectionObserver and return it
private getIntersectionObserver() {
return new IntersectionObserver(
// trigger the 'onIntersection' callback on ...
entries => this.onIntersection(entries), {
// ... both 0% and 50% percent of the intersection of the video
threshold: [0, 0.5]
});
}
// construct the MutationObserver and return it
private getMutationObserver(containerElement: HTMLElement) {
let mutationObserver = new MutationObserver(
// execute the onDomChange
() => this.onDomChange()
);
// at the very least, childList, attributes, or characterData
// must be set to true
const config = {
attributes: true,
characterData: true,
childList: true
};
// attach the mutation observer to the container element
// and start observing it
mutationObserver.observe(containerElement, config);
return mutationObserver;
}
private onDomChange() {
// when the DOM changes, loop over each element
// we want to observe for its interection,
// and do observe it
this.autoPlayVideoRefs.forEach((video: ElementRef) => {
this.intersectionObserver.observe(video.nativeElement);
});
}
private onIntersection(entries: IntersectionObserverEntry[]) {
entries.forEach((entry: any) => {
// get the video element
let video = entry.target;
// are we intersecting?
if (!entry.isIntersecting) {
return;
}
// play the video if we passed the threshold
// of 0.5 and store the promise so we can safely
// pause it again
if (entry.intersectionRatio >= 0.5) {
// for demo purposes we use a single video
// this code can easely be
// extended to support multiple videos
if (this.play === undefined) {
this.play = video.play();
}
} else if (entry.intersectionRatio < 0.5) {
// no need to pause something if it didn't start
// playing yet.
if (this.play !== undefined) {
// wait for the promise to resolve,
// then pause the video
this.play.then(() => {
video.pause();
this.play = undefined;
});
}
}
});
}
}
<ion-content>
<!--This is the important bit. You have to wrap your cards with the autoplay-content.component-->
<autoplay-content>
<ion-card *ngFor="let card of cards"
no-padding>
<ion-item>
<ion-avatar item-start>
<img [src]="card.avatarSrc">
</ion-avatar>
<h2>{{ card.name }}</h2>
<p>{{ card.location }}</p>
</ion-item>
<img *ngIf="!card.hasVideo"
[src]="card.src" />
<video *ngIf="card.hasVideo"
playsinline [muted]="true" loop preload="auto">
<source [src]="card.src"
type="video/mp4">
</video>
<ion-card-content>
<p>
Nullam sed tortor ex. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Etiam
dapibus, diam sit amet cursus pellentesque, dolor odio faucibus justo, non ornare ipsum nisl ut sem.
</p>
</ion-card-content>
<ion-row class="comment">
<ion-col>
<strong>
Sarah
</strong> quisque nibh purus, gravida eget arcu blandit? Etiam eros eros, volutpat vitae velit id!
</ion-col>
</ion-row>
<ion-row class="comment">
<ion-col>
<strong>
Marty
</strong> cras a elit eu quam consectetur pretium ... etiam eros eros, volutpat vitae velit id!
</ion-col>
</ion-row>
<ion-row>
<ion-col col-auto>
<button ion-button
color="dark"
clear
small>
<ion-icon name="heart"></ion-icon>
</button>
</ion-col>
<ion-col col-auto>
<button ion-button
color="dark"
clear
small>
<ion-icon name="text"></ion-icon>
</button>
</ion-col>
<ion-col col-auto>
<button ion-button
color="dark"
clear
small>
<ion-icon name="paper-plane"></ion-icon>
</button>
</ion-col>
<ion-col>
&nbsp;
</ion-col>
<ion-col col-auto>
<button ion-button
color="dark"
clear
small>
<ion-icon name="bookmark"></ion-icon>
</button>
</ion-col>
</ion-row>
</ion-card>
</autoplay-content>
</ion-content>
@Bengejd
Copy link
Author

Bengejd commented Jul 9, 2019

You could. I would recommend though, that you look at this article I wrote about the subject, for a much simpler implementation.

https://link.medium.com/aiJIHF06bY

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment