1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- /**
- * 音频可视化
- * @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(dataArray, canvasCtx, {
- lineGap: 2,
- canvWidth: width,
- canvHeight: height,
- canvFillColor: "transparent",
- lineColor: "rgba(255, 255, 255, 0.7)"
- })
- if (!isPause) {
- requestAnimationFrameFun()
- }
- })
- }
- let isPause = true
- const playVisualDraw = () => {
- //audioCtx.resume() // 重新更新状态 加了暂停和恢复音频音质发生了变化 所以这里取消了
- isPause = false
- requestAnimationFrameFun()
- }
- const pauseVisualDraw = () => {
- isPause = true
- //audioCtx?.suspend() // 暂停 加了暂停和恢复音频音质发生了变化 所以这里取消了
- // source?.disconnect()
- // analyser?.disconnect()
- }
- return {
- playVisualDraw,
- pauseVisualDraw
- }
- }
|