Cómo construir una caja de resonancia con JavaScript

Cómo construir una caja de resonancia en JavaScript mediante la creación de una clase SoundPlayer que inyecta reproductores dinámicamente y facilita la asignación de su reproducción a un evento DOM.

Primeros pasos

Para este tutorial, vamos a utilizar CheatCode Next.js Boilerplate como punto de partida para nuestro trabajo. Para empezar, clonemos una copia:

Terminal

git clone https://github.com/cheatcode/nextjs-boilerplate

A continuación, cd en el proyecto e instalar sus dependencias:

Terminal

cd nextjs-boilerplate && npm install

Finalmente, inicie el servidor de desarrollo:

Terminal

npm run dev

Con todo eso, estamos listos para comenzar.

Construyendo un reproductor de sonido

Para poder reproducir los sonidos en nuestra caja de resonancia, queremos una manera fácil de crear reproductores de audio sobre la marcha. Para hacer eso, vamos a comenzar conectando una clase de JavaScript que manejará la creación del <audio></audio> elementos que reproducirán nuestros sonidos y automatizarán la inyección de esos elementos en el DOM.

/lib/reproductordesonido.js

class SoundPlayer {
  constructor() {
    this.sounds = [];
  }

  // We'll implement the API for our class here...
}

export default SoundPlayer;

Para empezar, aquí estamos creando un esqueleto para nuestro SoundPlayer clase que nos ayudará a cargar sonidos en el DOM así como a reproducir esos sonidos Aquí, configuramos un JavaScript básico class y exportarlo como predeterminado desde /lib/soundPlayer.js .

Dentro del class , agregamos el constructor (esto es lo que se llama justo cuando JavaScript carga nuestra clase en la memoria) e inicializa el sounds propiedad en la clase, estableciéndola en un [] vacío formación. Aquí, this se refiere a la instancia de clase actual de SoundPlayer . Estamos creando una matriz aquí, ya que queremos una forma de realizar un seguimiento de todos los sonidos que hemos cargado en el DOM.

Terminal

class SoundPlayer {
  constructor() {
    this.sounds = [];
  }

  load(name, path) {
    this.sounds = [...this.sounds, { name, path }];
    this.injectPlayerIntoPage(name, path);
  }

  injectPlayerIntoPage(name, path) {
    const player = document.createElement("audio");
    player.id = name;
    player.src = path;
    player.volume = 0.5;
    player.type = "audio/mpeg";
    document.body.appendChild(player);
  }
}

export default SoundPlayer;

A continuación, necesitamos una API simple (interfaz de programación de aplicaciones, aquí utilizada coloquialmente para referirse a "la implementación del reproductor") para cargar sonidos en el DOM. Para hacerlo, arriba, estamos agregando dos métodos a nuestra clase:load() y injectPlayerIntoPage() . La primera será una función expuesta públicamente que llamaremos desde nuestra interfaz de usuario para decir "carga este sonido en el DOM".

Dentro de esa función, podemos ver que suceden dos cosas. Primero, como insinuamos anteriormente, queremos realizar un seguimiento de los sonidos que estamos cargando. Tomando un name argumento (un nombre fácil de recordar para "etiquetar" nuestro sonido) y un path (la ruta literal al archivo de sonido en nuestra aplicación), sobrescribimos el this.sounds propiedad en nuestra clase para que sea igual al valor actual de this.sounds , concatenado con un nuevo objeto que contiene el name y path pasado a load() .

Aquí, ...this.sounds está "desempaquetando" la totalidad del this.sounds existente matriz (ya sea que contenga algo o no). El ... part se conoce como el operador de propagación en JavaScript ("extiende" el contenido del valor inmediatamente después del ... ).

A continuación, con nuestro this.sounds matriz actualizada, necesitamos crear dinámicamente el <audio></audio> elemento del que hablamos anteriormente. Para hacerlo, agregaremos un método separado injectPlayerIntoPage() que toma los mismos dos argumentos de load() , name y path .

Dentro de esa función, lo primero que debemos hacer es crear el <audio></audio> elemento en la memoria. Para hacerlo, ejecutamos document.createElement('audio') para indicar a JavaScript que cree una copia en memoria (es decir, que aún no se ha agregado a la pantalla/DOM) de nuestro <audio></audio> elemento. Almacenamos el resultado de eso (el nodo DOM en memoria para nuestro <audio></audio> elemento) en la variable const player .

Hacemos esto para modificar más fácilmente los atributos del jugador y luego agregarlo al DOM. Específicamente, establecemos cuatro propiedades para nuestro player antes de agregarlo al DOM:

  1. id que se establece en el name pasamos por nuestro sonido.
  2. src que se establece en el path al archivo en la computadora para el sonido.
  3. volume que se establece en 0.5 o el 50 % para garantizar que no rompamos los tímpanos de nuestros usuarios.
  4. type que se establece en el tipo de archivo que esperamos para nuestros archivos (para nuestro ejemplo, estamos usando .mp3 archivos, así que usamos el audio/mpeg Tipo MIME:encuentre otros aquí).

Una vez que hemos establecido todas estas propiedades, finalmente, usamos appendChild en document.body para agregar nuestro reproductor de audio al DOM (la ubicación física de esto en el DOM es irrelevante como veremos a continuación).

/lib/reproductordesonido.js

class SoundPlayer {
  constructor() {
    this.sounds = [];
  }

  load(name, path) {
    this.sounds = [...this.sounds, { name, path }];
    this.injectPlayerIntoPage(name, path);
  }

  injectPlayerIntoPage(name, path) {
    const player = document.createElement("audio");
    player.id = name;
    player.src = path;
    player.volume = 0.5;
    player.type = "audio/mpeg";
    document.body.appendChild(player);
  }

  play(name) {
    const player = document.getElementById(name);
    if (player) {
      player.pause();
      player.currentTime = 0;
      player.play();
    }
  }
}

export default SoundPlayer;

Para concluir nuestro SoundPlayer clase, necesitamos agregar un método más:play() . Como sugiere el nombre, esto reproducirá un sonido para nosotros. Para hacerlo, primero tomamos un name argumento (uno que habríamos pasado a load() anteriormente) e intente encontrar un elemento en la página con un id atributo que coincide con ese nombre.

Recuerda que arriba configuramos el .id en nuestro <audio></audio> etiqueta al name pasamos. Esto debería encontrar una coincidencia en el DOM. Si es así, primero .pause() el reproductor (en caso de que ya estemos en la mitad de la reproducción), fuerce el .currentTime atributo en el jugador a 0 (es decir, el comienzo de nuestro sonido), y luego .play() eso.

Eso lo hace por nuestro SoundPlayer clase. A continuación, ¡conectémoslo y empecemos a reproducir algunos sonidos!

Agregar un componente de página React para probar nuestro reproductor

Debido a que nuestro modelo estándar se basa en Next.js, ahora vamos a crear una nueva página en nuestra aplicación usando un componente React.js donde podemos probar nuestro SoundPlayer .

/páginas/caja de resonancia/index.js

import React from "react";
import SoundPlayer from "../../lib/soundPlayer";

class Soundboard extends React.Component {
  state = {
    sounds: [
      { name: "Kick", file: "/sounds/kick.mp3" },
      { name: "Snare", file: "/sounds/snare.mp3" },
      { name: "Hi-Hat", file: "/sounds/hihat.mp3" },
      { name: "Tom", file: "/sounds/tom.mp3" },
      { name: "Crash", file: "/sounds/crash.mp3" },
    ],
  };

  componentDidMount() {
    const { sounds } = this.state;
    this.player = new SoundPlayer();

    sounds.forEach(({ name, file }) => {
      this.player.load(name, file);
    });
  }

  render() {
    const { sounds } = this.state;

    return (
      <div>
        {sounds.map(({ name, file }) => {
          return (
            <button
              className="btn btn-primary btn-lg"
              style={{ marginRight: "15px" }}
              onClick={() => this.player.play(name)}
            >
              {name}
            </button>
          );
        })}
      </div>
    );
  }
}

Soundboard.propTypes = {};

export default Soundboard;

En Next.js, las rutas o URL en nuestra aplicación son creadas automáticamente por el marco basado en el contenido del /pages carpeta en la raíz de nuestra aplicación. Aquí, para crear la ruta /soundboard (Esto finalmente será accesible a través de http://localhost:5000/soundboard en el navegador), creamos la carpeta /pages/soundboard y pon un index.js archivo en esa carpeta donde vivirá el componente React que representa nuestra página.

Debido a que nuestro componente de prueba es tan simple, arriba hemos generado todo el contenido. Repasémoslo para comprender cómo encaja todo esto.

Primero, arriba importamos nuestro SoundPlayer clase de nuestro /lib/soundPlayer.js archivo.

A continuación, definimos un componente React utilizando el método basado en clases (esto facilita el trabajo con nuestro reproductor y evita problemas de rendimiento). La primera parte que queremos llamar la atención es el state propiedad que estamos agregando a la clase y el sounds propiedad que hemos establecido en una matriz de objetos allí.

Esto debería estar empezando a tener algún sentido. Aquí, estamos creando todos los sonidos que queremos cargar en el DOM usando el load() método que escribimos anteriormente en nuestro SoundPlayer clase. Recuerda, esa función toma un name y un file argumento que estamos definiendo aquí.

Hacemos esto como una matriz de objetos para que sea más fácil repetir y cargar todos nuestros sonidos a la vez, lo que hacemos en el componentDidMount() en nuestro componente React. Allí, usamos la desestructuración de objetos de JavaScript para "arrancar" el sounds propiedad que acabamos de definir en state (accesible en los métodos de nuestro componente como this.state ) y luego crea una instancia de nuestro SoundPlayer clase con new SoundPlayer() y luego asigne esa instancia de nuevo a this.player en nuestro Soundboard clase de componente (esto será útil pronto).

Luego, usando ese sounds matriz que definimos en el estado, lo recorremos con un .forEach() , nuevamente usando la desestructuración de JavaScript para "arrancar" el name y file propiedades de cada objeto en la matriz a medida que los recorremos. Con estos valores llamamos a this.player.load() , pasándolos a la función. Como aprendimos anteriormente, esperamos que esto agregue cada uno de los sounds en nuestra matriz al this.sounds matriz en nuestro SoundPlayer class' y luego agregue un elemento DOM para el <audio></audio> de ese sonido jugador.

Donde todo esto se une es en el render() método en nuestra clase de componente. Aquí, de nuevo "arrancamos" el sounds matriz de this.state , esta vez usando un JavaScript .map() para recorrer la matriz, lo que nos permite devolver algunas marcas que queremos que React represente para cada iteración (cada sonido) de nuestra matriz.

Como estamos construyendo una caja de resonancia, agregamos un <button></button> para cada sonido con un onClick atributo establecido en una función que llama a this.player.play() pasando el name atributo del objeto del sonido en el this.state.sounds formación. ¡Con esto, tenemos una caja de resonancia!

Ahora, cuando hacemos clic en un botón, deberíamos escuchar el sonido asociado en la reproducción del archivo.

¡Eso es todo! Si desea agregar sus propios sonidos personalizados, solo asegúrese de agregarlos al /public/sounds carpeta en su aplicación y luego actualice el sounds matriz en estado.

Terminando

En este tutorial, aprendimos cómo crear una caja de resonancia usando JavaScript. Para hacerlo, comenzamos creando una clase de JavaScript que nos ayudó a crear dinámicamente reproductores de audio a los que podíamos hacer referencia con un nombre único. En esa clase, también agregamos un .play() método para agilizar la reproducción de nuestros sonidos.

Para crear la interfaz de usuario de nuestra caja de resonancia, definimos un componente React que creaba una instancia de nuestra clase de caja de resonancia, se cargaba en nuestra lista de sonidos preferida y luego generaba una lista de botones, cada uno con una llamada al .play() método para el sonido representado por ese botón.