Я визуализирую веб-аудио данные с помощью d3. Я постоянно запускаю функцию, которая добавляет данные в d3 с помощью requestanimationframe. Когда я консоль регистрирую данные частоты в рекурсивном вызове функции, я получаю результат, как и ожидалось. Однако, когда я связываю данные с svg и пытаюсь создать элементы с помощью d3, цикл запускается только дважды.
Я пытался передать разные наборы данных в привязку данных, но каждый результат вызывает функцию только дважды. Например:
import createBoomBox from './boombox';
const frequencyData = new Uint8Array(200);
const createFrequency = (analyser) => {
requestAnimationFrame(() => createFrequency(analyser));
analyser.getByteFrequencyData(frequencyData);
createBoomBox(frequencyData);
};
const createAudio = (audioId) => {
console.log('in audio');
const audioCtx = new (window.AudioContext ||
window.webkitAudioContext)();
const audioElement = document.getElementById(audioId);
const audioSrc = audioCtx.createMediaElementSource(audioElement)
|| window.audiosrc;
window.audioSrc = audioSrc;
const analyser = audioCtx.createAnalyser();
audioSrc.connect(analyser);
audioSrc.connect(audioCtx.destination);
createFrequency(analyser);
};
export default createAudio;
Здесь я создаю аудио-контекст и передаю данные частоты в createBoomBox. createBoomBox выглядит следующим образом:
const svgHeight = 200;
const svgWidth = 200;
const boomBox = d3.selectAll('.boom-box');
const svg = boomBox.append('svg')
.attr('height', svgHeight)
.attr('width', svgWidth);
const createBoomBox = (frequencyData) => {
const rangeScale = d3.scaleLinear()
.domain([0, d3.max(frequencyData)])
.range([0, svgHeight]);
const hslScale = d3.scaleLinear()
.domain([0, d3.max(frequencyData)])
.range([0, 360]);
console.log(frequencyData);
svg.selectAll('circle')
.data(frequencyData)
.enter()
.append('circle')
.attr('r', d => rangeScale(d))
.attr('cx', svgWidth / 2)
.attr('cy', svgHeight / 2)
.attr('fill', 'none')
.attr('stroke-width', 4)
.attr('stroke-opacity', 1)
.attr('stroke', d => d3.hsl(hslScale(d), 1, 0.5))
.exit()
.remove();
};
export default createBoomBox;
Проблема возникает в функции createBoomBox, в
svg.selectAll
createBoomBox повторно вызывается с requestAnimationFrame. Когда консоль регистрирует данные частоты прямо над строкой svg.selectAll, я получаю результат, как и ожидалось. А именно, массив в виде Uint8Array с частотными данными.
Тем не менее, когда я консоль журнала данных внутри
svg.selectAll('circle')
.data(frequencyData)
.enter()
.append('circle')
.attr('r', d => rangeScale(d))
.attr('cx', svgWidth / 2)
.attr('cy', svgHeight / 2)
.attr('fill', 'none')
.attr('stroke-width', 4)
.attr('stroke-opacity', 1)
.attr('stroke', d => d3.hsl(hslScale(d), 1, 0.5))
.exit()
.remove();
Итерация в приведенном выше блоке кода происходит только дважды. Таким образом, хотя вся функция неоднократно вызывается через requestAnimationFrame, блок svg.selectAll запускается только дважды. Любая идея относительно того, что вызывает проблему?
Ваш выбор выхода всегда пуст.
Вам необходимо применить шаблон Enter-Update-Exit.
var circles = svg.selectAll('circle')
.data(frequencyData);
circles.exit().remove();
circles.enter()
.append('circle')
.attr('cx', svgWidth / 2)
.attr('cy', svgHeight / 2)
.attr('fill', 'none')
.attr('stroke-width', 4)
.attr('stroke-opacity', 1)
.merge(circles)
.attr('r', d => rangeScale(d))
.attr('stroke', d => d3.hsl(hslScale(d), 1, 0.5));