【问题标题】:A-frame switching template causes sound (video?) to overlapA帧切换模板导致声音(视频?)重叠
【发布时间】:2021-07-13 21:44:25
【问题描述】:

我的行为(已编辑):

我正在使用 A-Frame 和 A-Frame 模板组件。
我使用模板组件从 script id="..."> 标签中加载模板,例如example。

我想像example 一样使用 el.setAttribute 即时更改模板。

我的模板包含一个以视频为来源的天空>元素。
我可以加载一个模板,它就像一个魅力。我可以暂停、播放等等。

当我想将模板切换到使用相同视频资源的新模板,或加载新模板然后返回到第一个模板时,会出现此问题。

现在我有 2 种声音 - 一种没有与视频同步。

显示一个视频,但在后台播放第二个声音。我还可以验证声音不是来自显示的视频。我仍然可以播放/暂停它,并且它的声音会在背景中播放。

已编辑
我不仅希望能够修改 sky> 元素,还希望能够修改其他一些元素,这就是我想使用模板组件的原因。假设我不知道最终会使用哪些资产,并且我不想使用资产管理系统来加载数十 Mb 的视频/对象和其他内容,并在加载屏幕上暂停用户。如here所述:

在浏览器获取(或出错)所有资产或资产系统达到超时之前,场景不会渲染或初始化。

为了更清楚地说明我的意图,但仍然保持简单,我希望用户能够通过选择预制件来改变场景(假设每个视频 15Mb,即使有 6-7 个资产,它也可能几乎达到 100Mb),或通过上传一个。而且我真的很想避免重新加载页面,因为从用户的角度来看,这不是一个优雅的解决方案。

我的猜测:

我认为旧视频并没有完全处理掉。我尝试对各种元素/材料/视频本身使用 three.js .dispose() 方法,但没有成功。
我也尝试使用 a-video> 作为来源,并在切换之前删除元素,但结果相同。

问题

  • 如何正确移除模板加载的元素,使视频不存储/缓存在任何地方?
  • 我能以某种方式设置 autoplay="false" 或类似于 sky> 视频吗?
  • 如果我的方法完全错误,您能否指出正确的方向?

示例:

function switchVideoToFirst() {
  console.log("switching to first");
  document.getElementById("skyVideoTemplate").setAttribute('template', 'src', "#firstVideo");
}

function switchVideoToSecond() {
  console.log('switching to second');
  document.getElementById("skyVideoTemplate").setAttribute('template', 'src', '#secondVideo');
}

function playVideo() {
  document.getElementById("skyVideo").components.material.material.map.image.play();
}

function pauseVideo() {
  document.getElementById("skyVideo").components.material.material.map.image.pause();
}
<!DOCTYPE html>

<head>
  <meta charset="UTF-8">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/nunjucks/2.3.0/nunjucks.min.js"></script>
  <script src="https://aframe.io/releases/1.1.0/aframe.min.js"></script>
  <script src="https://unpkg.com/aframe-template-component@3.2.1/dist/aframe-template-component.min.js"></script>
</head>

<body>
  <div style="width: 400px; height: 200px;">
    <a-scene embedded>
      <a-entity id="skyVideoTemplate" template="src: #"></a-entity>
    </a-scene>
  </div>
  <button onclick="switchVideoToFirst()">switch video to first</button>
  <button onclick="switchVideoToSecond()">switch video to second</button>
  </br>
  </br>
  <button onclick="playVideo()">play</button>
  <button onclick="pauseVideo()">Pause</button>
</body>

<script id="firstVideo" type='text/nunjucks'>
  <a-sky id="skyVideo" src="https://upload.wikimedia.org/wikipedia/commons/transcoded/f/fb/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm.360p.vp9.webm">
  </a-sky>
  <a-box position="-1 1 -1" color="red"></a-box>
</script>

<script id="secondVideo" type='text/nunjucks'>
  <a-sky id="skyVideo" src="https://upload.wikimedia.org/wikipedia/commons/transcoded/f/fb/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm.360p.vp9.webm">
  </a-sky>
  <a-sphere position="-1 1 -1" color="green"></a-sphere>
</script>

我知道我在两个模板中都有相同的视频,但这是故意缩短示例。该问题与重新加载相同的模板密切相关,要么通过切换到不同的模板并返回,要么通过加载具有相同视频资源的新模板。

如何重现我的问题:

  • 按“将视频切换到第一个”以加载第一个模板。
  • 按“将视频切换到第二个”以加载第二个模板。
  • 视频已消失,但声音仍在播放。
  • 这一点可能不起作用,但如果您等待足够长的时间,第二个视频将加载,您将听到 2 个声音。然后播放/暂停控件将只对其中一个起作用。

我也经历过:

  • 按“将视频切换到第一个”以加载第一个模板
  • 暂停视频
  • 按“将视频切换到第二个”以加载第二个模板
  • 白屏可能需要一段时间
  • 加载“第二个”视频时,“第一个”视频仍会显示并暂停,但新视频会从头开始播放声音。那么您应该(或者不,无法弄清楚这个)能够播放/暂停“第一个”视频。

示例 2

在得到 Piotr Adam Milewski 的大力支持后,我尝试了以下方法

  • 按“将视频切换到第一个”以加载第一个模板
  • 按“将视频切换到第二个”以加载第二个模板

更换模板时的声音消失了,但加载第二个模板后声音从头开始但视频暂停。

AFRAME.registerComponent("pausewhenremoved", {
    init: function() {
        this.el.addEventListener('materialvideoloadeddata', evt => {
            this.videoElement = evt.detail.src;
            //this.videoElement.pause();
        });
    },
    remove() {
        // once i'm removed, the video is going down too
        this.videoElement.pause();
    // I also tried removing the video from DOM but this only changed video to blank (sound was still on)
        //this.videoElement.removeAttribute('src');
        //this.videoElement.load();
        //$(this.videoElement).empty().remove();
    }
});

function switchVideoToFirst() {
  console.log("switching to first");
  document.getElementById("skyVideoTemplate").setAttribute('template', 'src', "#firstVideo");
}

function switchVideoToSecond() {
  console.log('switching to second');
  document.getElementById("skyVideoTemplate").setAttribute('template', 'src', '#secondVideo');
}
<!DOCTYPE html>

<head>
  <meta charset="UTF-8">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/nunjucks/2.3.0/nunjucks.min.js"></script>
  <script src="https://aframe.io/releases/1.1.0/aframe.min.js"></script>
  <script src="https://unpkg.com/aframe-template-component@3.2.1/dist/aframe-template-component.min.js"></script>
</head>

<body>
  <div style="width: 400px; height: 200px;">
    <a-scene embedded>
      <a-entity id="skyVideoTemplate" template="src: #"></a-entity>
    </a-scene>
  </div>
  <button onclick="switchVideoToFirst()">switch video to first</button>
  <button onclick="switchVideoToSecond()">switch video to second</button>
</body>

<script id="firstVideo" type='text/nunjucks'>
  <a-sky id="skyVideo" pausewhenremoved src="https://upload.wikimedia.org/wikipedia/commons/transcoded/f/fb/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm.360p.vp9.webm">
  </a-sky>
  <a-box position="-1 1 -1" color="red"></a-box>
</script>

<script id="secondVideo" type='text/nunjucks'>
  <a-sky id="skyVideo" pausewhenremoved src="https://upload.wikimedia.org/wikipedia/commons/transcoded/f/fb/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm.360p.vp9.webm">
  </a-sky>
  <a-sphere position="-1 1 -1" color="green"></a-sphere>
</script>

一个“有点”的工作示例

这是我(至少部分)解决此问题的方法。

我创建了一个标准的 HTML 视频元素来存储视频并修改其来源,而不是将其留给 A-Frame。

你可以在两个模板之间切换,只要你不做得太快,它就会正常渲染(如果你尝试过早切换,屏幕变黑但声音正常)。

function switchVideoToFirst(buttonElement) {
  console.log("switching to first");
  
  if(document.getElementById("sky")) {
    document.getElementById("sky").remove();
  }
  
  document.getElementById("skyVideoTemplate").setAttribute('template', 'src', "#firstVideo");
}

function switchVideoToSecond(buttonElement) {
  console.log('switching to second');
  
  if(document.getElementById("sky")) {
    document.getElementById("sky").remove();
  }
  
  document.getElementById("skyVideoTemplate").setAttribute('template', 'src', '#secondVideo');
}
<!DOCTYPE html>

<head>
  <meta charset="UTF-8">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/nunjucks/2.3.0/nunjucks.min.js"></script>
  <script src="https://aframe.io/releases/1.1.0/aframe.min.js"></script>
  <script src="https://unpkg.com/aframe-template-component@3.2.1/dist/aframe-template-component.min.js"></script>
</head>

<body>
  <script>
    AFRAME.registerComponent("pausewhenremoved", {
      schema: {
        src: {
          type: 'string',
          default: ''
        }
      },
      init: function() {
        this.videoElement = document.getElementById("skyVideoHolder");
        this.videoElement.setAttribute('src', this.data.src);
        this.videoElement.load();
      },
      play() {
        if (this.videoElement) {
          this.videoElement.play();
        }
      },
      pause() {
        if (this.videoElement) {
          this.videoElement.pause();
        }
      },
      remove() {
        // once i'm removed, the video is going down too
        this.videoElement.pause();
      }
    });
  </script>
  <div style="width: 400px; height: 200px;">
    <a-scene embedded>
      <video id="skyVideoHolder" src="" loop></video>
      <a-entity id="skyVideoTemplate" template="src: #"></a-entity>
    </a-scene>
  </div>
  <button onclick="switchVideoToFirst(this)">switch video to first</button>
  <button onclick="switchVideoToSecond(this)">switch video to second</button>
</body>

<script id="firstVideo" type='text/nunjucks'>
  <a-sky id="skyVideo" pausewhenremoved="src: https://upload.wikimedia.org/wikipedia/commons/transcoded/f/fb/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm.360p.vp9.webm" src="#skyVideoHolder">
  </a-sky>
  <a-box position="-1 1 -1" color="red"></a-box>
</script>

<script id="secondVideo" type='text/nunjucks'>
  <a-sky id="skyVideo" pausewhenremoved="src: https://upload.wikimedia.org/wikipedia/commons/transcoded/f/fb/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm.360p.vp9.webm" src="#skyVideoHolder">
  </a-sky>
  <a-sphere position="-1 1 -1" color="green"></a-sphere>
</script>

免责声明
示例中使用的360视频来自this website,作者为Jan Ainali

【问题讨论】:

标签: video aframe


【解决方案1】:

我认为你直接遇到了this 问题——一旦a-frame 从内联 URL 创建了一个&lt;video&gt;,它就会忘记它。这是相关的,因为模板组件只是添加和removing 元素。

所以问题的一个很大的简化是 - 删除的视频球仍然播放视频:

setTimeout(e => {
  let sky = document.querySelector("a-sky")
  sky.parentNode.removeChild(sky)
}, 2000)
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<a-scene background="color: #ECECEC">
  <a-sky id="skyVideo" src="https://upload.wikimedia.org/wikipedia/commons/transcoded/f/fb/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm.360p.vp9.webm">
  </a-sky>
</a-scene>

解决此问题的一种方法是创建一个组件,该组件将抓取加载的视频元素,并在实体移除时暂停/移除它:

setTimeout(e => {
  let sky = document.querySelector("a-sky")
  sky.parentNode.removeChild(sky)
}, 2000)
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
  AFRAME.registerComponent("foo", {
    init: function() {
      // once the video data is loaded, store the element
      this.el.addEventListener("materialvideoloadeddata", evt => {
        this.videoEl = evt.detail.src;
      })
    },
    remove() {
      // once i'm removed, the video is going down too
      this.videoEl.pause();
    }
  })
</script>
<a-scene background="color: #ECECEC">
  <a-sky foo id="skyVideo" src="https://upload.wikimedia.org/wikipedia/commons/transcoded/f/fb/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm.360p.vp9.webm">
  </a-sky>
</a-scene>

另一种方法是使用资产管理系统 - 您可以控制视频 HTML 元素:

let video = document.querySelector("#vid");
let sky = document.querySelector("a-sky");

video.addEventListener("play", e => {
  setTimeout(e => {
    video.pause();
    sky.parentNode.removeChild(sky);
  }, 4000);
});
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<a-scene background="color: #ECECEC">
  <a-assets>
    <video id="vid" autoplay loop="true" preload="auto" crossorigin="anonymous" src="https://upload.wikimedia.org/wikipedia/commons/transcoded/f/fb/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm/Hundra_knektars_marsch_p%C3%A5_Forum_Vulgaris.webm.360p.vp9.webm"></video>
  </a-assets>
  <a-sky id="skyVideo" src="#vid"> </a-sky>
</a-scene>

另一方面,如果您想更改/重新启动视频,并将立方体切换为球体,我不会使用 template 组件,只需管理视频 HTML 元素,并更改立方体/球体可见性.

【讨论】:

  • 感谢您的回复。它很有用,因为资产管理系统可能会解决声音问题,但我还不能将其标记为答案,因为如果您有大量资产并且加载它们可能会让用户卡在加载屏幕上,这将不是一个合适的解决方案很长时间。我更新了我的问题
  • @PiotrKolecki 对,我已经更新了我的答案,让我知道现在是否更好
  • 这确实解决了将模板更改为其他模板时的声音问题。但是,如果您尝试重新加载相同的模板(或具有相同视频的不同模板),声音仍在后台从头开始播放(而我仍然可以在 ? 第二个实例上使用暂停/播放?)。我添加了另一个示例。
  • 我会接受你的回答,因为它确实帮助我找到了解决这个问题的方法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-31
  • 2019-09-29
  • 2018-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多