Lit — современный фреймворк для создания веб-компонентов, который позволяет писать компактный, декларативный код с реактивным обновлением. Web Audio API предоставляет мощный интерфейс для работы с аудио в браузере, включая генерацию звуков, обработку сигналов и создание интерактивных аудиоэффектов. Совмещение этих технологий открывает возможности для создания динамических аудиокомпонентов.
Каждый компонент, работающий с Web Audio API, требует создания
AudioContext. В Lit это удобно делать в методе
connectedCallback, который вызывается при добавлении
элемента в DOM:
import { LitElement, html, css } from 'lit';
import { property, state } from 'lit/decorators.js';
class AudioPlayer extends LitElement {
static styles = css`
button { margin: 5px; padding: 10px; }
`;
@state() playing = false;
constructor() {
super();
this.audioContext = null;
this.oscillator = null;
}
connectedCallback() {
super.connectedCallback();
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
}
render() {
return html`
<button @click=${this.toggleAudio}>
${this.playing ? 'Стоп' : 'Играть'}
</button>
`;
}
toggleAudio() {
if (this.playing) {
this.stopAudio();
} else {
this.startAudio();
}
}
startAudio() {
this.oscillator = this.audioContext.createOscillator();
this.oscillator.type = 'sine';
this.oscillator.frequency.setValueAtTime(440, this.audioContext.currentTime);
this.oscillator.connect(this.audioContext.destination);
this.oscillator.start();
this.playing = true;
}
stopAudio() {
this.oscillator.stop();
this.oscillator.disconnect();
this.oscillator = null;
this.playing = false;
}
}
customElements.define('audio-player', AudioPlayer);
Ключевые моменты:
@state() используется для реактивного свойства
состояния компонента.Web Audio API предоставляет узлы обработки сигналов, такие как
GainNode, BiquadFilterNode,
AnalyserNode. В Lit их легко интегрировать с реактивным
состоянием:
startAudio() {
this.oscillator = this.audioContext.createOscillator();
this.gainNode = this.audioContext.createGain();
this.filterNode = this.audioContext.createBiquadFilter();
this.oscillator.type = 'square';
this.oscillator.frequency.setValueAtTime(220, this.audioContext.currentTime);
this.gainNode.gain.setValueAtTime(0.5, this.audioContext.currentTime);
this.filterNode.type = 'lowpass';
this.filterNode.frequency.setValueAtTime(1000, this.audioContext.currentTime);
this.oscillator.connect(this.filterNode);
this.filterNode.connect(this.gainNode);
this.gainNode.connect(this.audioContext.destination);
this.oscillator.start();
this.playing = true;
}
Выделенные моменты:
setValueAtTime и анимации сигналов.Для визуализации звукового сигнала используется
AnalyserNode, который позволяет получить данные частотного
спектра:
startAudio() {
this.oscillator = this.audioContext.createOscillator();
this.analyser = this.audioContext.createAnalyser();
this.oscillator.connect(this.analyser);
this.analyser.connect(this.audioContext.destination);
this.oscillator.start();
this.dataArray = new Uint8Array(this.analyser.frequencyBinCount);
this.animate();
}
animate() {
this.analyser.getByteFrequencyData(this.dataArray);
// данные можно использовать для отрисовки графика
requestAnimationFrame(() => this.animate());
}
Особенности:
AnalyserNode предоставляет массив данных
dataArray в реальном времени.requestAnimationFrame обеспечивает плавную анимацию
спектра.<canvas> внутри компонента
Lit для визуальных эффектов.Lit позволяет автоматически обновлять интерфейс при изменении свойств. Например, частоту или громкость можно привязать к слайдерам:
render() {
return html`
<input type="range" min="100" max="1000" .value=${this.frequency} @input=${this.updateFrequency}>
<input type="range" min="0" max="1" step="0.01" .value=${this.volume} @input=${this.updateVolume}>
<button @click=${this.toggleAudio}>${this.playing ? 'Стоп' : 'Играть'}</button>
`;
}
updateFrequency(e) {
this.frequency = e.target.value;
if (this.oscillator) {
this.oscillator.frequency.setValueAtTime(this.frequency, this.audioContext.currentTime);
}
}
updateVolume(e) {
this.volume = e.target.value;
if (this.gainNode) {
this.gainNode.gain.setValueAtTime(this.volume, this.audioContext.currentTime);
}
}
Принципы:
Для сложного звукового дизайна создаются несколько параллельных
осцилляторов, которые объединяются через GainNode или
StereoPannerNode:
startAudio() {
this.oscillators = [];
this.gainNode = this.audioContext.createGain();
this.gainNode.gain.setValueAtTime(0.5, this.audioContext.currentTime);
this.gainNode.connect(this.audioContext.destination);
[220, 440, 880].forEach(freq => {
const osc = this.audioContext.createOscillator();
osc.frequency.setValueAtTime(freq, this.audioContext.currentTime);
osc.connect(this.gainNode);
osc.start();
this.oscillators.push(osc);
});
this.playing = true;
}
Особенности:
GainNode упрощает
регулировку громкости.connectedCallback и disconnectedCallback —
ключевые методы для инициализации и очистки аудиоресурсов.stop() и disconnect()
для всех узлов при удалении компонента, чтобы избежать утечек
памяти.Этот подход позволяет создавать полноценные аудиокомпоненты на Lit с поддержкой интерактивности, эффектов и визуализации в реальном времени, полностью управляемые через реактивные свойства.