Skip to content

Commit

Permalink
Make canvas ref accessible on AudioVisualizer component (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
samhirtarif authored Jun 17, 2023
1 parent a92eddd commit 7029e72
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 87 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ import { AudioVisualizer } from 'react-audio-visualize';

const Visualizer = () => {
const [blob, setBlob] = useState<Blob>();
const visualizerRef = useRef<HTMLCanvasElement>(null)

// set blob somewhere in code

return (
<div>
{blob && (
<AudioVisualizer
ref={visualizerRef}
blob={blob}
width={500}
height={75}
Expand All @@ -49,6 +51,7 @@ const Visualizer = () => {
| **`barPlayedColor`** | Color for the bars that have been played | `"rgb(160, 198, 255)""` | Yes |
| **`currentTime`** | Current time stamp till which the audio blob has been played. Visualized bars that fall before the current time will have `barPlayerColor`, while that ones that fall after will have `barColor` | N/A | Yes |
| **`style`** | Custom styles that can be passed to the visualization canvas | N/A | Yes |
| **`ref`** | A `ForwardedRef` for the `HTMLCanvasElement` | N/A | Yes |

---

Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-audio-visualize",
"private": false,
"version": "1.0.0",
"version": "1.1.0",
"license": "MIT",
"author": "",
"repository": {
Expand Down Expand Up @@ -65,7 +65,7 @@
"vite-plugin-dts": "^2.3.0"
},
"peerDependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react": ">=16.2.0",
"react-dom": ">=16.2.0"
}
}
193 changes: 113 additions & 80 deletions src/AudioVisualizer/AudioVisualizer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { useRef, useState, type ReactElement } from "react";
import { useEffect } from "react";
import {
useRef,
useState,
forwardRef,
type ForwardedRef,
type ForwardRefExoticComponent,
type RefAttributes,
useImperativeHandle,
useEffect,
} from "react";
import { type dataPoint } from "./types";
import { calculateBarData, draw } from "./utils";

Expand Down Expand Up @@ -45,93 +53,118 @@ interface Props {
* Custome styles that can be passed to the visualization canvas
*/
style?: React.CSSProperties;
/**
* A `ForwardedRef` for the `HTMLCanvasElement`
*/
ref?: React.ForwardedRef<HTMLCanvasElement>;
}

const AudioVisualizer: (props: Props) => ReactElement = ({
blob,
width,
height,
barWidth = 2,
gap = 1,
currentTime,
style,
backgroundColor = "transparent",
barColor = "rgb(184, 184, 184)",
barPlayedColor = "rgb(160, 198, 255)",
}: Props) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const [data, setData] = useState<dataPoint[]>([]);
const [duration, setDuration] = useState<number>(0);

useEffect(() => {
const processBlob = async (): Promise<void> => {
if (!canvasRef.current) return;
const AudioVisualizer: ForwardRefExoticComponent<
Props & RefAttributes<HTMLCanvasElement>
> = forwardRef(
(
{
blob,
width,
height,
barWidth = 2,
gap = 1,
currentTime,
style,
backgroundColor = "transparent",
barColor = "rgb(184, 184, 184)",
barPlayedColor = "rgb(160, 198, 255)",
}: Props,
ref?: ForwardedRef<HTMLCanvasElement>
) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const [data, setData] = useState<dataPoint[]>([]);
const [duration, setDuration] = useState<number>(0);

if (!blob) {
const barsData = Array.from({ length: 100 }, () => ({
max: 0,
min: 0,
}));
draw(
barsData,
canvasRef.current,
barWidth,
gap,
backgroundColor,
barColor,
barPlayedColor
);
return;
}
useImperativeHandle<HTMLCanvasElement | null, HTMLCanvasElement | null>(
ref,
() => canvasRef.current,
[]
);

const audioBuffer = await blob.arrayBuffer();
const audioContext = new AudioContext();
await audioContext.decodeAudioData(audioBuffer, (buffer) => {
useEffect(() => {
const processBlob = async (): Promise<void> => {
if (!canvasRef.current) return;
setDuration(buffer.duration);
const barsData = calculateBarData(buffer, height, width, barWidth, gap);
setData(barsData);
draw(
barsData,
canvasRef.current,
barWidth,
gap,
backgroundColor,
barColor,
barPlayedColor
);
});
};

processBlob();
}, [blob, canvasRef.current]);
if (!blob) {
const barsData = Array.from({ length: 100 }, () => ({
max: 0,
min: 0,
}));
draw(
barsData,
canvasRef.current,
barWidth,
gap,
backgroundColor,
barColor,
barPlayedColor
);
return;
}

useEffect(() => {
if (!canvasRef.current) return;
const audioBuffer = await blob.arrayBuffer();
const audioContext = new AudioContext();
await audioContext.decodeAudioData(audioBuffer, (buffer) => {
if (!canvasRef.current) return;
setDuration(buffer.duration);
const barsData = calculateBarData(
buffer,
height,
width,
barWidth,
gap
);
setData(barsData);
draw(
barsData,
canvasRef.current,
barWidth,
gap,
backgroundColor,
barColor,
barPlayedColor
);
});
};

draw(
data,
canvasRef.current,
barWidth,
gap,
backgroundColor,
barColor,
barPlayedColor,
currentTime,
duration
processBlob();
}, [blob, canvasRef.current]);

useEffect(() => {
if (!canvasRef.current) return;

draw(
data,
canvasRef.current,
barWidth,
gap,
backgroundColor,
barColor,
barPlayedColor,
currentTime,
duration
);
}, [currentTime, duration]);

return (
<canvas
ref={canvasRef}
width={width}
height={height}
style={{
...style,
}}
/>
);
}, [currentTime, duration]);
}
);

return (
<canvas
ref={canvasRef}
width={width}
height={height}
style={{
...style,
}}
/>
);
};
AudioVisualizer.displayName = "AudioVisualizer";

export { AudioVisualizer };

0 comments on commit 7029e72

Please sign in to comment.