我正在将项目从Ionic 3迁移到Ionic 4,但我注意到在Ionic 3中可以使用的自动播放视频功能在Ionic 4中不再可用。
根据我的判断,尽管视图中显示了视频,但并未填充ContentChildren
。我也尝试过使用forwardRef
,但这没做任何事情。
我检查了其他与此类似的问题,但所提供的解决方案均不适用于我,并且大多数都是低质量的答案,因此考虑到自解决方案以来已经过去的时间,我认为这不是重复的被问到。
我正在使用的重要软件包是:
角度:7.2.15
,@离子/角度4.4.0
,zone.js 0.8.29
,交点观察者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" />