我正在将我的项目从 Ionic 3 迁移到 Ionic 4,但我注意到在 Ionic 3 中运行的自动播放视频功能不再在 Ionic 4 中运行。
据我所知ContentChildren
,尽管视图中存在视频,但我并没有被填充。我也尝试过使用forwardRef
,但这并没有做任何事情。
我检查了与此类似的其他问题,提供的解决方案都没有为我工作,而且大多数都是低质量的答案,所以考虑到他们被问到已经过去的时间,我不认为这是重复的。
我正在使用的重要包是:Angular: 7.2.15
、@ionic/angular 4.4.0
、zone.js 0.8.29
、intersection-observer 0.7.0
。
这是自动播放组件
import { Component, ContentChildren, ElementRef, forwardRef, NgZone, OnDestroy, OnInit, QueryList } from '@angular/core';
import { AutoplayVideoDirective } from '../../../directives/autoplay-video.directive';
@Component({
selector: 'autoplay',
template: `<ng-content></ng-content>`
})
export class AutoplayContentComponent implements OnInit, OnDestroy {
@ContentChildren(forwardRef(() => AutoplayVideoDirective),
{
read: ElementRef,
descendants: true,
},
) autoPlayVideoRefs: QueryList<any>;
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() {
// execute the onIntersection on the threshold intersection of 0 and 70%
return new IntersectionObserver(entries => this.onIntersection(entries), {
threshold: [0, 0.70],
});
}
// construct the MutationObserver and return it
private getMutationObserver(containerElement: HTMLElement) {
console.log(containerElement);
// execute the onDomChange
let mutationObserver = new MutationObserver(() => 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 interaction,
// and do observe it
console.log(this.autoPlayVideoRefs);
this.autoPlayVideoRefs.forEach((video: ElementRef) => {
this.checkIfVideosCanLoad(video.nativeElement).then((canPlay) => {
console.log('Video can play: ', canPlay);
});
this.intersectionObserver.observe(video.nativeElement);
});
}
/*
* In low-power mode, videos do not load.
* So this quickly checks to see if videos have the capability of loading.
*/
private async checkIfVideosCanLoad(video: any) {
let canPlay: boolean;
return new Promise((resolve) => {
// A safe timeout of 3 seconds, before we declare that the phone is in low power mode.
let timeout = setTimeout(() => {
canPlay = false;
resolve(canPlay);
}, 3000);
// Loads meta data about the video, but not the whole video itself.
video.onloadeddata = () => {
canPlay = true;
clearTimeout(timeout);
resolve(canPlay);
};
});
}
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.7 and store the promise so we can safely
// pause it again
if (entry.intersectionRatio >= 0.70) {
if (this.play === undefined) this.play = video.play();
} else if (entry.intersectionRatio < 0.70) {
// 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;
}).catch(() => {});
}
}
});
}
}
自动播放指令非常简单。
import { Directive } from '@angular/core';
/*
* To be used with the autoplay-content.ts component.
*/
@Directive({
selector: 'video'
})
export class AutoplayVideoDirective {}
自动播放组件被包裹的组件也是如此。这就是所谓的inline-video
<autoplay>
<div tappable (tap)="changeVideoAudio(video?.id)">
<video video playsinline loop [muted]="'muted'" [autoplay]="true" preload="auto" muted="muted"
[poster]="(video?.src | AspectRatio) | videoPoster" [id]="'media-' + video?.id" class="video-media">
<source [src]="video.src | AspectRatio" type="video/mp4" src="">
</video>
</div>
</autoplay>
我不确定为什么这在 Ionic 4 中不起作用,但在 ionic 3 中起作用......所以任何帮助将不胜感激。
组件的层次结构如下:
Feed Page > Feed Item > Autoplay > Inline Video
. autoplay
组件包裹在组件周围的位置inline video
。
编辑:我在 config.xml 中也设置了 AllowInlineMediaPlayback 首选项
<preference name="AllowInlineMediaPlayback" value="true" />