123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- /**
- * 音频可视化
- * @param audioDom
- * @param canvasDom
- * @param fftSize 2的幂数,最小为32
- * 注意 由于ios低版本必须在用户操作之后才能初始化 createMediaElementSource 所以必须在用户操作之后初始化
- */
- export default function audioVisualDraw(audioDom: HTMLAudioElement, canvasDom: HTMLCanvasElement, fftSize = 128) {
- type propsType = { canvWidth: number; canvHeight: number; canvFillColor: string; lineColor: string; lineGap: number }
- // canvas
- const canvasCtx = canvasDom.getContext("2d")!
- const { width, height } = canvasDom.getBoundingClientRect()
- canvasDom.width = width
- canvasDom.height = height
- // audio
- //const audioCtx = new AudioContext()
- //const source = audioCtx.createMediaElementSource(audioDom)
- //const analyser = audioCtx.createAnalyser()
- //analyser.fftSize = fftSize
- //source?.connect(analyser)
- //analyser.connect(audioCtx.destination)
- //const dataArray = new Uint8Array(fftSize / 2)
- const draw = (data: Uint8Array, ctx: CanvasRenderingContext2D, { lineGap, canvWidth, canvHeight, canvFillColor, lineColor }: propsType) => {
- if (!ctx) return
- const w = canvWidth
- const h = canvHeight
- fillCanvasBackground(ctx, w, h, canvFillColor)
- // 可视化
- const dataLen = data.length
- let step = (w / 2 - lineGap * dataLen) / dataLen
- step < 1 && (step = 1)
- const midX = w / 2
- const midY = h / 2
- let xLeft = midX
- for (let i = 0; i < dataLen; i++) {
- const value = data[i]
- const percent = value / 255 // 最大值为255
- const barHeight = percent * midY
- canvasCtx.fillStyle = lineColor
- // 中间加间隙
- if (i === 0) {
- xLeft -= lineGap / 2
- }
- canvasCtx.fillRect(xLeft - step, midY - barHeight, step, barHeight)
- canvasCtx.fillRect(xLeft - step, midY, step, barHeight)
- xLeft -= step + lineGap
- }
- let xRight = midX
- for (let i = 0; i < dataLen; i++) {
- const value = data[i]
- const percent = value / 255 // 最大值为255
- const barHeight = percent * midY
- canvasCtx.fillStyle = lineColor
- if (i === 0) {
- xRight += lineGap / 2
- }
- canvasCtx.fillRect(xRight, midY - barHeight, step, barHeight)
- canvasCtx.fillRect(xRight, midY, step, barHeight)
- xRight += step + lineGap
- }
- }
- const fillCanvasBackground = (ctx: CanvasRenderingContext2D, w: number, h: number, colors: string) => {
- ctx.clearRect(0, 0, w, h)
- ctx.fillStyle = colors
- ctx.fillRect(0, 0, w, h)
- }
- const requestAnimationFrameFun = () => {
- // requestAnimationFrame(() => {
- // //analyser?.getByteFrequencyData(dataArray)
- // draw(generateMixedData(48), canvasCtx, {
- // lineGap: 2,
- // canvWidth: width,
- // canvHeight: height,
- // canvFillColor: "transparent",
- // lineColor: "rgba(255, 255, 255, 0.7)"
- // })
- // if (!isPause) {
- // requestAnimationFrameFun()
- // }
- // })
- const _time = setInterval(() => {
- if (isPause) {
- clearInterval(_time)
- return
- }
- //analyser?.getByteFrequencyData(dataArray)
- draw(generateMixedData(38), canvasCtx, {
- lineGap: 3,
- canvWidth: width,
- canvHeight: height,
- canvFillColor: "transparent",
- lineColor: "rgba(255, 255, 255, 0.7)"
- })
- }, 300);
- }
- let isPause = true
- const playVisualDraw = () => {
- //audioCtx.resume() // 重新更新状态 加了暂停和恢复音频音质发生了变化 所以这里取消了
- isPause = false
- requestAnimationFrameFun()
- }
- const pauseVisualDraw = () => {
- isPause = true
- requestAnimationFrame(()=>{
- canvasCtx.clearRect(0, 0, width, height);
- })
- //audioCtx?.suspend() // 暂停 加了暂停和恢复音频音质发生了变化 所以这里取消了
- // source?.disconnect()
- // analyser?.disconnect()
- }
- return {
- playVisualDraw,
- pauseVisualDraw
- }
- }
- export function generateMixedData(size:number) {
- const dataArray = new Uint8Array(size);
- const baseNoiseAmplitude = 30;
- const minFrequency = 0.01;
- const maxFrequency = 0.2;
- const minAmplitude = 50;
- const maxAmplitude = 150;
- for (let i = 0; i < size; i++) {
- const frequency = minFrequency + Math.random() * (maxFrequency - minFrequency);
- const amplitude = minAmplitude + Math.random() * (maxAmplitude - minAmplitude);
- const wave = amplitude * (0.5 + 0.5 * Math.sin(frequency * i));
- const noise = Math.floor(Math.random() * baseNoiseAmplitude) - baseNoiseAmplitude / 2;
- dataArray[i] = Math.min(255, Math.max(0, Math.floor(wave + noise)));
- }
- return dataArray;
- }
|