1

我正在将我的项目从 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" />

4

0 回答 0