react-audio-spectrum2
Version:
a component for displaying audio spectrum
211 lines (210 loc) • 10.4 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = __importStar(require("react"));
var defaultProps = {
width: 300,
height: 200,
capColor: '#FFF',
capHeight: 2,
meterWidth: 2,
meterCount: 40 * (2 + 2),
meterColor: [
{ stop: 0, color: '#f00' },
{ stop: 0.5, color: '#0CD7FD' },
{ stop: 1, color: 'red' },
],
gap: 10,
};
function getRandomId(len) {
var str = '1234567890-qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM';
return Array.from({ length: len })
.reduce(function (acc) { return acc.concat(str[Math.floor((Math.random() * str.length))]); }, '');
}
function AudioSpectrum(_a) {
var _b = _a.width, width = _b === void 0 ? defaultProps.width : _b, _c = _a.height, height = _c === void 0 ? defaultProps.height : _c, _d = _a.capColor, capColor = _d === void 0 ? defaultProps.capColor : _d, _e = _a.capHeight, capHeight = _e === void 0 ? defaultProps.capHeight : _e, _f = _a.meterWidth, meterWidth = _f === void 0 ? defaultProps.meterWidth : _f, _g = _a.meterCount, meterCount = _g === void 0 ? defaultProps.meterCount : _g, _h = _a.meterColor, meterColor = _h === void 0 ? defaultProps.meterColor : _h, _j = _a.gap, gap = _j === void 0 ? defaultProps.gap : _j, _k = _a.id, id = _k === void 0 ? getRandomId(50) : _k, propsAudioEl = _a.audioEle, audioId = _a.audioId, restProps = __rest(_a, ["width", "height", "capColor", "capHeight", "meterWidth", "meterCount", "meterColor", "gap", "id", "audioEle", "audioId"]);
var animationId = react_1.useRef(null);
var canvasId = id;
var audioContext = react_1.useRef(null);
var audioCanvas = react_1.useRef(null);
var playStatus = react_1.useRef(null);
var mediaEleSource = react_1.useRef(null);
var analyser = react_1.useRef(null);
var audioEle = react_1.useRef(null);
var canvasRef = react_1.useRef(null);
var prepareElements = react_1.useCallback(function () {
if (!audioId && !propsAudioEl) {
console.error('target audio not found!');
return;
}
if (audioId) {
audioEle.current = document.getElementById(audioId);
}
else if (propsAudioEl) {
audioEle.current = propsAudioEl;
}
audioCanvas.current = canvasRef.current;
}, [audioId, propsAudioEl]);
var drawSpectrum = react_1.useCallback(function (currentAnalyser) {
var cWidth = audioCanvas.current.width;
var cHeight = audioCanvas.current.height - capHeight;
// store the vertical position of hte caps for the previous frame
var capYPositionArray = [];
var ctx = audioCanvas.current.getContext('2d');
var gradient = ctx.createLinearGradient(0, 0, 0, 300);
if (Array.isArray(meterColor)) {
var stops = meterColor;
stops.forEach(function (stop) {
gradient.addColorStop(stop.stop, stop.color);
});
}
else if (typeof meterColor === 'string') {
// gradient = this.props.meterColor
}
var drawMeter = function () {
// item value of array: 0 - 255
var array = new Uint8Array(currentAnalyser.frequencyBinCount);
currentAnalyser.getByteFrequencyData(array);
if (playStatus.current === 'PAUSED') {
// for (let i = array.length - 1; i >= 0; i--) {
// array[i] = 0;
// }
array.fill(0);
var allCapsReachBottom = !capYPositionArray.some(function (cap) { return cap > 0; });
if (allCapsReachBottom) {
ctx.clearRect(0, 0, cWidth, cHeight + capHeight);
// since the sound is top and animation finished,
// stop the requestAnimation to prevent potential memory leak,THIS IS VERY IMPORTANT!
cancelAnimationFrame(animationId.current);
return;
}
}
// sample limited data from the total array
var step = Math.round(array.length / meterCount);
ctx.clearRect(0, 0, cWidth, cHeight + capHeight);
// eslint-disable-next-line no-plusplus
for (var i = 0; i < meterCount; i++) {
var value = array[i * step];
if (capYPositionArray.length < Math.round(meterCount)) {
capYPositionArray.push(value);
}
ctx.fillStyle = capColor;
// draw the cap, with transition effect
if (value < capYPositionArray[i]) {
// let y = cHeight - (--capYPositionArray[i])
// eslint-disable-next-line no-plusplus
var preValue = --capYPositionArray[i];
var y_1 = ((270 - preValue) * cHeight) / 270;
ctx.fillRect(i * (meterWidth + gap), y_1, meterWidth, capHeight);
}
else {
// let y = cHeight - value
var y_2 = ((270 - value) * cHeight) / 270;
ctx.fillRect(i * (meterWidth + gap), y_2, meterWidth, capHeight);
capYPositionArray[i] = value;
}
ctx.fillStyle = gradient; // set the fillStyle to gradient for a better look
// let y = cHeight - value + this.props.capHeight
var y = ((270 - value) * cHeight) / 270 + capHeight;
ctx.fillRect(i * (meterWidth + gap), y, meterWidth, cHeight); // the meter
}
animationId.current = requestAnimationFrame(drawMeter);
};
animationId.current = requestAnimationFrame(drawMeter);
}, [capColor, capHeight, gap, meterColor, meterCount, meterWidth]);
// create analyser and connect media source
var setupAudioNode = react_1.useCallback(function (currentAudioEle) {
if (!currentAudioEle) {
throw new Error('Audio element is not found');
}
if (!analyser.current && audioContext.current) {
analyser.current = audioContext.current.createAnalyser();
analyser.current.smoothingTimeConstant = 0.8;
analyser.current.fftSize = 2048;
}
if (!mediaEleSource.current && audioContext.current && analyser.current) {
mediaEleSource.current = audioContext.current.createMediaElementSource(currentAudioEle);
mediaEleSource.current.connect(analyser.current);
mediaEleSource.current.connect(audioContext.current.destination);
}
return analyser;
}, []);
// create or update audioContext
var prepareAPIs = react_1.useCallback(function () {
// fix browser vender for AudioContext and requestAnimationFrame
// window.AudioContext = window.AudioContext;
// window.AudioContext = window.AudioContext || window.webkitAudioContext
// || window.mozAudioContext || window.msAudioContext;
// window.requestAnimationFrame = window.requestAnimationFrame;
// window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame
// || window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
// window.cancelAnimationFrame = window.cancelAnimationFrame;
// window.cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.msCancelAnimationFrame;
try {
audioContext.current = new window.AudioContext(); // 1.set audioContext
}
catch (e) {
console.error('!Your browser does not support AudioContext');
console.error(e);
}
}, []);
var initAudioEvents = react_1.useCallback(function () {
if (audioEle.current) {
audioEle.current.onpause = function () {
playStatus.current = 'PAUSED';
};
audioEle.current.onplay = function () {
playStatus.current = 'PLAYING';
prepareAPIs();
var currentAnalyser = setupAudioNode(audioEle.current);
drawSpectrum(currentAnalyser.current);
};
}
}, [drawSpectrum, prepareAPIs, setupAudioNode]);
react_1.useEffect(function () {
prepareElements();
initAudioEvents();
}, [prepareElements, initAudioEvents]);
return (react_1.default.createElement("canvas", __assign({ ref: canvasRef, id: canvasId, width: width, height: height }, restProps)));
}
exports.default = AudioSpectrum;