Skip to content

Commit

Permalink
[feat] Allow user canvas (#63); constructor accepts options as 1st arg.
Browse files Browse the repository at this point in the history
  • Loading branch information
hvianna committed Jan 24, 2024
1 parent c3bde60 commit 75450cf
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 21 deletions.
30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ import AudioMotionAnalyzer from 'audiomotion-analyzer';

Creates a new instance of **audioMotion-analyzer**.

The analyzer canvas will be appended to the HTML element referenced by `container`, unless you set [`useCanvas: false`](#usecanvas-boolean) in the options.
`container` is the DOM element to which the analyzer canvas should be appended to. If not defined, the document's **body** is used instead.

If `container` is undefined, the document's body will be used instead.
This argument is not necessary if you provide an existing [`canvas`](#canvas-htmlcanvaselement-object) or define [`useCanvas: false`](#usecanvas-boolean) in the options object.

`options` must be an [Options object](#options-object).

Expand All @@ -109,6 +109,8 @@ const audioMotion = new AudioMotionAnalyzer(

This will insert the analyzer canvas inside the *#container* element and start the visualization of audio coming from the *#audio* element.

?> By default, audioMotion will try to use all available container space for the canvas. To prevent it from growing indefinitely, you must either constrain the dimensions of the `container` via CSS or explicitly define [`height`](#height-number) and/or [`width`](#width-number) properties in the constructor [options](#options-object).

### Options object

Valid properties and default values are shown below.
Expand All @@ -122,6 +124,7 @@ options = {<br>
&emsp;&emsp;[audioCtx](#audioctx-audiocontext-object): *undefined*, // constructor only<br>
&emsp;&emsp;[barSpace](#barspace-number): **0.1**,<br>
&emsp;&emsp;[bgAlpha](#bgalpha-number): **0.7**,<br>
&emsp;&emsp;[canvas](#canvas-htmlcanvaselement-object): *undefined*, // constructor only<br>
&emsp;&emsp;[channelLayout](#channellayout-string): **'single'**,<br>
&emsp;&emsp;[colorMode](#colormode-string): **'gradient'**,<br>
&emsp;&emsp;[connectSpeakers](#connectspeakers-boolean): **true**, // constructor only<br>
Expand Down Expand Up @@ -190,6 +193,14 @@ If neither is defined, a new audio context will be created. After instantiation,

See [this live code](https://codesandbox.io/s/9y6qb) and the [multi-instance demo](/demo/multi.html) for more usage examples.

#### `canvas` *HTMLCanvasElement object*

*Available since v4.4.0*

Allows you to provide an existing [*Canvas*](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) where audioMotion should render its visualizations.

If not provided, a new canvas will be created. After instantiation, you can obtain its reference from the [`canvas`](#canvas-htmlcanvaselement-object-read-only) read-only property.

#### `connectSpeakers` *boolean*

*Available since v3.2.0*
Expand Down Expand Up @@ -324,11 +335,13 @@ Defaults to **0.7**.

### `canvas` *HTMLCanvasElement object* *(Read only)*

[*Canvas*](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) element created by audioMotion.
[*Canvas*](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) element where audioMotion renders its visualizations.

See also the [`canvas`](#canvas-htmlcanvaselement-object) constructor option.

### `canvasCtx` *CanvasRenderingContext2D object* *(Read only)*

[2D rendering context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) used for drawing in audioMotion's *Canvas*.
[2D rendering context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) used for drawing in audioMotion's [`canvas`](#canvas-htmlcanvaselement-object-read-only).

### `channelLayout` *string*

Expand Down Expand Up @@ -483,13 +496,14 @@ See also [`gradient`](#gradient-string) and [`splitGradient`](#splitgradient-boo

Nominal dimensions of the analyzer.

If one or both of these are `undefined`, the analyzer will try to adjust to the container's width and/or height.
If the container's width and/or height are 0 (inline elements), a reference size of **640 x 270 pixels** will be used to replace the missing dimension(s).
This should be considered the minimum dimensions for proper visualization of all available modes and effects.
Setting one or both properties to **_undefined_** (default) will trigger the fluid/responsive behavior and the analyzer will try to adjust to the container's height and/or width.
In that case, it's important that you constrain the dimensions of the container via CSS to prevent the canvas from growing indefinitely.

You can set both values at once using the [`setCanvasSize()`](#setcanvassize-width-height-) method.

?> You can read the actual canvas dimensions at any time directly from the [`canvas`](#canvas-htmlcanvaselement-object-read-only) object.
See also [`onCanvasResize`](#oncanvasresize-function) callback.

?> The actual pixel dimensions of the canvas can vary depending on the device's [pixelRatio](#pixelratio-number-read-only), [`loRes`](#lores-boolean) setting and fullscreen status. You can read the actual `height` and `width` values directly from the [`canvas`](#canvas-htmlcanvaselement-object-read-only) object.

### `isAlphaBars` *boolean* *(Read only)*

Expand Down
43 changes: 30 additions & 13 deletions src/audioMotion-analyzer.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ class AudioMotionError extends Error {
// helper function - output deprecation warning message on console
const deprecate = ( name, alternative ) => console.warn( `${name} is deprecated. Use ${alternative} instead.` );

// helper function - check if a given object is empty (also returns `true` on null, undefined or any non-object value)
const isEmpty = obj => {
for ( const p in obj )
return false;
return true;
}

// helper function - validate a given value with an array of strings (by default, all lowercase)
// returns the validated value, or the first element of `list` if `value` is not found in the array
const validateFromList = ( value, list, modifier = 'toLowerCase' ) => list[ Math.max( 0, list.indexOf( ( '' + value )[ modifier ]() ) ) ];
Expand Down Expand Up @@ -204,12 +211,26 @@ export default class AudioMotionAnalyzer {
this._selectedGrads = []; // names of the currently selected gradients for channels 0 and 1
this._sources = []; // input nodes

// Check if options object passed as first argument
if ( ! ( container instanceof Element ) ) {
if ( isEmpty( options ) && ! isEmpty( container ) )
options = container;
container = null;
}

this._ownCanvas = ! ( options.canvas instanceof HTMLCanvasElement );

// Create a new canvas or use the one provided by the user
const canvas = this._ownCanvas ? document.createElement('canvas') : options.canvas;
canvas.style = 'max-width: 100%;';
this._ctx = canvas.getContext('2d');

// Register built-in gradients
for ( const [ name, options ] of GRADIENTS )
this.registerGradient( name, options );

// Set container
this._container = container || document.body;
this._container = container || ( ! this._ownCanvas && canvas.parentElement ) || document.body;

// Make sure we have minimal width and height dimensions in case of an inline container
this._defaultWidth = this._container.clientWidth || 640;
Expand Down Expand Up @@ -277,11 +298,6 @@ export default class AudioMotionAnalyzer {
if ( options.connectSpeakers !== false )
this.connectOutput();

// create analyzer canvas
const canvas = document.createElement('canvas');
canvas.style = 'max-width: 100%;';
this._ctx = canvas.getContext('2d');

// create auxiliary canvases for the X-axis and radial scale labels
for ( const ctx of [ '_scaleX', '_scaleR' ] )
this[ ctx ] = document.createElement('canvas').getContext('2d');
Expand Down Expand Up @@ -362,8 +378,8 @@ export default class AudioMotionAnalyzer {
// Set configuration options and use defaults for any missing properties
this._setProps( options, true );

// add canvas to the container
if ( this.useCanvas )
// Add canvas to the container (only when canvas not provided by user)
if ( this.useCanvas && this._ownCanvas )
this._container.appendChild( canvas );

// Finish canvas setup
Expand Down Expand Up @@ -829,7 +845,7 @@ export default class AudioMotionAnalyzer {
if ( ! this._ready )
return;

const { audioCtx, canvas, _controller, _input, _merger, _observer, _ownContext, _splitter } = this;
const { audioCtx, canvas, _controller, _input, _merger, _observer, _ownCanvas, _ownContext, _splitter } = this;

this._destroyed = true;
this._ready = false;
Expand All @@ -856,8 +872,9 @@ export default class AudioMotionAnalyzer {
if ( _ownContext )
audioCtx.close();

// remove canvas from the DOM
canvas.remove();
// remove canvas from the DOM (if not provided by the user)
if ( _ownCanvas )
canvas.remove();

// reset flags
this._calcBars();
Expand Down Expand Up @@ -2512,8 +2529,8 @@ export default class AudioMotionAnalyzer {
this._fsWidth = screenWidth;
this._fsHeight = screenHeight;

// if canvas dimensions haven't changed, quit
if ( canvas.width == newWidth && canvas.height == newHeight )
// if this is not the constructor call and canvas dimensions haven't changed, quit
if ( reason != REASON_CREATE && canvas.width == newWidth && canvas.height == newHeight )
return;

// apply new dimensions
Expand Down
2 changes: 2 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export interface AnalyzerBarData {

export interface ConstructorOptions extends Options {
audioCtx?: AudioContext;
canvas?: HTMLCanvasElement;
connectSpeakers?: boolean;
fsElement?: HTMLElement;
source?: HTMLMediaElement | AudioNode;
Expand Down Expand Up @@ -114,6 +115,7 @@ export interface LedParameters {

declare class AudioMotionAnalyzer {
constructor(container?: HTMLElement, options?: ConstructorOptions);
constructor(options?: ConstructorOptions);

get alphaBars(): boolean;
set alphaBars(value: boolean);
Expand Down

0 comments on commit 75450cf

Please sign in to comment.