Last active
April 30, 2020 16:51
-
-
Save Bengejd/3009ea4674eb60a729ec07f26e2e9bd6 to your computer and use it in GitHub Desktop.
Ionic - Auto play/pause videos on scroll.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Directive } from '@angular/core'; | |
@Directive({ | |
selector: 'video' | |
}) | |
export class AutoplayVideoDirective { } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
}); | |
} | |
} | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> | |
| |
</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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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