简体   繁体   English

Web Audio API 循环与 SVG 动画同步

[英]Web Audio API loop synchronize with SVG animation

I have a simple 4 second audio clip that clicks once per second.我有一个简单的 4 秒音频剪辑,每秒点击一次。 I have an SVG image with 4 circles.我有一个带有 4 个圆圈的 SVG 图像。 I'd like to be able to loop the audio indefinitely and have the circles show/hide every second in sync with the audio.我希望能够无限循环播放音频,并让圆圈每秒显示/隐藏与音频同步。 So...所以...

0 seconds, show just the red circle
1 second, show just the green circle
2 seconds, show just the yellow circle
3 seconds, show just the blue circle  (audio loops to the beggining, time is reset to 0) 
0 seconds, show just the red circle
etc...

Everything is working except for I don't know how to capture the timing of the loop.一切正常,除了我不知道如何捕捉循环的时间。 (See below in the javascript where it says "some event listener()"). (请参阅下面的 javascript 中的“某些事件侦听器()”)。 Any ideas?有任何想法吗?

<button id='go'>Go</button>      
<button id='stop'>Stop</button>      
<br/><br/> 
<svg width='500' height='100'>
<rect width="250" height="100" style="fill:Tan;stroke-width:3;stroke:rgb(0,0,0)" />
  <g id='circle1'><circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" /></g> 
  <g id='circle2'><circle cx="100" cy="50" r="40" stroke="black" stroke-width="3" fill="green" /></g> 
  <g id='circle3'><circle cx="150" cy="50" r="40" stroke="black" stroke-width="3" fill="yellow" /></g> 
  <g id='circle4'><circle cx="200" cy="50" r="40" stroke="black" stroke-width="3" fill="blue" /></g> 
</svg>  
var actx = new (AudioContext || webkitAudioContext)(),
    src = "https://smantei.s3-us-west-2.amazonaws.com/Click.wav",
    audioData, srcNode;  

fetch(src, {mode: "cors"}).then(function(resp) {return resp.arrayBuffer()}).then(decode);

function decode(buffer) {
  actx.decodeAudioData(buffer, playLoop);
}

function playLoop(abuffer) {
  if (!audioData) audioData = abuffer;  
  srcNode = actx.createBufferSource();  
  srcNode.buffer = abuffer;             
  srcNode.connect(actx.destination);    
  srcNode.loop = true;                  
  srcNode.start();                      
}

document.querySelector("#go").onclick = function() {
  playLoop(audioData);  
};

document.querySelector("#stop").onclick = function() {
  srcNode.stop();
};

/*
some event listener() {
  if (time >= 0 && time < 1.0) {
    document.querySelector("#circle1").style.display = 'block';
    document.querySelector("#circle2").style.display = 'none';
    document.querySelector("#circle3").style.display = 'none';
    document.querySelector("#circle4").style.display = 'none';
  }
  if (time >= 1.0 && time < 2.0) {
    document.querySelector("#circle1").style.display = 'none';
    document.querySelector("#circle2").style.display = 'block';
    document.querySelector("#circle3").style.display = 'none';
    document.querySelector("#circle4").style.display = 'none';
  }
  if (time >= 2.0 && time < 3.0) {
    document.querySelector("#circle1").style.display = 'none';
    document.querySelector("#circle2").style.display = 'none';
    document.querySelector("#circle3").style.display = 'block';
    document.querySelector("#circle4").style.display = 'none';
  }
  if (time >= 3.0 && time < 4.0) {
    document.querySelector("#circle1").style.display = 'none';
    document.querySelector("#circle2").style.display = 'none';
    document.querySelector("#circle3").style.display = 'none';
    document.querySelector("#circle4").style.display = 'block';
  }  
}
*/

Because you are looping the audio clip, there are no events available to use.因为您正在循环播放音频剪辑,所以没有可用的事件。 However, if you used 4 separate 1-sec clips, you can schedule each one to start every second.但是,如果您使用 4 个单独的 1 秒剪辑,您可以安排每个剪辑每秒开始。 Then you would get an onended event from each clip that could be used to drive your SVG animation.然后,您将从每个剪辑中获得一个可用于驱动 SVG 动画的 onended 事件。

However, this means you'll have to do your own audio looping by constantly scheduling new clips every 4 sec.但是,这意味着您必须通过每 4 秒不断安排新剪辑来完成自己的音频循环。 Be sure the schedule the next round before the current round ends and don't use the onended event to schedule the next clip.确保在当前回合结束之前安排下一轮,不要使用已结束的事件来安排下一个剪辑。 This will slowly drift over time, but maybe that is acceptable for you application.这会随着时间慢慢漂移,但也许这对您的应用程序来说是可以接受的。

A very rough sketch of how it might work.它可能如何工作的一个非常粗略的草图。 Completely untested, but I hope it gives the basic idea:完全未经测试,但我希望它给出了基本的想法:

// Clip is the 4 sec audio clip.
// context is the audioContext
clip = new Array(4);
// Create 4 clips using the same buffer
for (let k = 0; k < clip.length; ++k) {
  clip[k] = new AudioBufferSourceNode(context, {buffer: clip};
  clip[0].connect(context.destiantion);
}

let now = context.currentTime;
// Play out each 1 sec part of the clip consecutively, playing out
// the whole buffer 1 sec at a time.
clip[0].start(now, 0, 1);
clip[1].start(now + 1, 1, 1);
clip[2].start(now + 2, 2, 1);
clip[3].start(now + 3, 3, 1);

clip[0].onended = () => {
  // SVG when first clip ends
};
clip[1].onended = () => {
  // SVG when second clip ends
};
clip[2].onended = () => {
  // SVG when third clip ends
};
clip[3].onended = () => {
  // SVG when fourth clip ends
};

This is the basic structure.这是基本结构。 You'll need to modify it to loop.您需要将其修改为循环。 So perhaps when the third clip ends, create new clip[0] to clip[2] again, with new start times, When the fourth clip ends, create a new clip[3].所以也许当第三个剪辑结束时,再次创建新的剪辑 [0] 到剪辑 [2],使用新的开始时间,当第四个剪辑结束时,创建一个新的剪辑 [3]。 Also be sure to add onended events for each of these new clips.还要确保为这些新剪辑中的每一个添加 onended 事件。

There are other ways, but this is relateively straightforward and makes the animation always stay in sync with the audio (with some lag due to the event handling).还有其他方法,但这是相对简单的,并使动画始终与音频保持同步(由于事件处理而存在一些延迟)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM