UNPKG

@flosportsinc/ng-video-autoplay

Version:

Simple way to ensure videos autoplay even if they need to be muted first.

275 lines (271 loc) 8.42 kB
import { Directive, ElementRef, Renderer2, Input, ContentChildren, NgModule } from '@angular/core'; import { share, map, takeUntil, tap, take, filter, flatMap } from 'rxjs/operators'; import { Subject, fromEvent } from 'rxjs'; import { maybe, either } from 'typescript-monads'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ // tslint:disable:no-object-mutation /** @type {?} */ const filterHalted = (/** * @param {?} source * @return {?} */ (source) => source.pipe(filter((/** * @param {?} res * @return {?} */ res => res.halted === true)))); /** @type {?} */ const tryPlayVideoAsIs = (/** * @param {?} source * @return {?} */ (source) => source.pipe(flatMap((/** * @param {?} videoElement * @return {?} */ videoElement => videoElement.play() .then((/** * @return {?} */ () => ({ videoElement, halted: false }))) .catch((/** * @return {?} */ () => ({ videoElement, halted: true }))))), filterHalted)); /** @type {?} */ const tryPlayVideoMuted = (/** * @param {?} source * @return {?} */ (source) => source.pipe(map((/** * @param {?} res * @return {?} */ res => { res.videoElement.muted = true; res.videoElement.volume = 0; return res.videoElement; })), tryPlayVideoAsIs)) // 1) attempt to play as-is (including autoplay + unmuted) // 2) attempt to play muted, showing unmite button // 3) show click to play ; // 1) attempt to play as-is (including autoplay + unmuted) // 2) attempt to play muted, showing unmite button // 3) show click to play class FloVideoAutoplayDirective { /** * @param {?} elmRef * @param {?} rd */ constructor(elmRef, rd) { this.elmRef = elmRef; this.rd = rd; this.floVideoAutoplay = true; this.floVideoAutoplayIndex = 0; this.onDestroySource = new Subject(); this.onDestroy = this.onDestroySource.pipe(share()); this.canExecute = (/** * @return {?} */ () => this.floVideoAutoplay === true || this.floVideoAutoplay === ''); this.maybeVideoElement = (/** * @return {?} */ () => maybe(this.elmRef.nativeElement).filter((/** * @param {?} e * @return {?} */ e => e.nodeName === 'VIDEO'))); this.maybeUnmuteActionRef = (/** * @return {?} */ () => maybe(this.floVideoAutoplayClickUnmuteRef).filter((/** * @param {?} a * @return {?} */ a => (/** @type {?} */ (a)) !== ''))); this.maybePlayActionRef = (/** * @return {?} */ () => maybe(this.floVideoAutoplayClickPlayRef).filter((/** * @param {?} a * @return {?} */ a => (/** @type {?} */ (a)) !== ''))); this.hideRef = (/** * @param {?} ref * @return {?} */ (ref) => this.rd.setStyle(ref, 'display', 'none')); this.showRef = (/** * @param {?} ref * @return {?} */ (ref) => this.rd.setStyle(ref, 'display', 'block')); this.volumeChange = (/** * @param {?} videoElement * @return {?} */ (videoElement) => fromEvent(videoElement, 'volumechange', { passive: true }).pipe(map((/** * @param {?} evt * @return {?} */ evt => (/** @type {?} */ (evt.target)))), share(), takeUntil(this.onDestroy))); this.initOnVideo = (/** * @param {?} videoElement * @return {?} */ (videoElement) => fromEvent(videoElement, 'loadstart').pipe(tap((/** * @return {?} */ () => videoElement.setAttribute('autoplay', 'true'))), map((/** * @param {?} evt * @return {?} */ evt => (/** @type {?} */ (evt.target)))), tryPlayVideoAsIs, tryPlayVideoMuted, take(1)).subscribe((/** * @param {?} res * @return {?} */ res => { res.videoElement.preload = 'none'; res.videoElement.muted = false; res.videoElement.volume = 1; this.maybePlayActionRef().tapSome(this.showRef); }))); this.runUnmuteSequence = (/** * @param {?} actionRef * @return {?} */ (actionRef) => (/** * @param {?} runOnce * @return {?} */ (runOnce) => (/** * @param {?} videoElement * @return {?} */ (videoElement) => { videoElement.setAttribute('autoplay', 'true'); fromEvent(actionRef, 'click').pipe(takeUntil(this.onDestroy)).subscribe((/** * @return {?} */ () => { videoElement.muted = false; videoElement.volume = 1; })); this.volumeChange(videoElement).pipe(filter((/** * @param {?} v * @return {?} */ v => !v.muted || v.volume > 0)), takeUntil(this.onDestroy)) .subscribe((/** * @return {?} */ () => this.hideRef(actionRef))); /** @type {?} */ const showRef = this.volumeChange(videoElement).pipe(filter((/** * @param {?} v * @return {?} */ v => v.muted || v.volume <= 0))); (runOnce ? showRef.pipe(take(1)) : showRef).subscribe((/** * @return {?} */ () => this.showRef(actionRef))); }))); } /** * @return {?} */ ngAfterContentInit() { if (!this.canExecute()) { return; } this.maybeVideoElement().tapSome(this.initOnVideo); this.videos.map((/** * @param {?} a * @return {?} */ a => a.nativeElement)).forEach(this.initOnVideo); this.maybeUnmuteActionRef().tapSome((/** * @param {?} ref * @return {?} */ ref => { this.hideRef(ref); either(this.maybeVideoElement().valueOrUndefined(), maybe(this.videos.toArray()[this.floVideoAutoplayIndex]).map((/** * @param {?} a * @return {?} */ a => a.nativeElement)).valueOrUndefined()) .tap({ left: this.runUnmuteSequence(ref)(false), right: this.runUnmuteSequence(ref)(true) }); })); this.maybePlayActionRef().tapSome((/** * @param {?} actionRef * @return {?} */ actionRef => { this.hideRef(actionRef); fromEvent(actionRef, 'click').pipe(takeUntil(this.onDestroy)).subscribe((/** * @return {?} */ () => { this.hideRef(actionRef); this.maybeVideoElement().tapSome((/** * @param {?} v * @return {?} */ v => v.play())); })); })); } /** * @return {?} */ ngOnDestroy() { this.onDestroySource.next(); this.onDestroySource.complete(); } } FloVideoAutoplayDirective.decorators = [ { type: Directive, args: [{ selector: '[floVideoAutoplay]' },] } ]; /** @nocollapse */ FloVideoAutoplayDirective.ctorParameters = () => [ { type: ElementRef }, { type: Renderer2 } ]; FloVideoAutoplayDirective.propDecorators = { floVideoAutoplay: [{ type: Input }], floVideoAutoplayClickUnmuteRef: [{ type: Input }], floVideoAutoplayClickPlayRef: [{ type: Input }], floVideoAutoplayIndex: [{ type: Input }], videos: [{ type: ContentChildren, args: ['floVideoAutoplay', { descendants: true },] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class FloVideoAutoplayModule { } FloVideoAutoplayModule.decorators = [ { type: NgModule, args: [{ declarations: [ FloVideoAutoplayDirective ], exports: [ FloVideoAutoplayDirective ] },] } ]; export { FloVideoAutoplayModule, FloVideoAutoplayDirective as ɵa }; //# sourceMappingURL=flosportsinc-ng-video-autoplay.js.map