ICanvas - 语音录制波
演示
预览
介绍
这是语音录制绘制线条的动画,主要是演示如何批量的绘制元素并添加动画。
示例源码
uvue
vue
<template>
<!-- #ifdef MP-WEIXIN -->
<page-meta :page-style="`background-color:${xThemeConfigBgColor}`">
<navigation-bar :background-color="xThemeConfigNavBgColor"
:front-color="xThemeConfigNavFontColor"></navigation-bar>
</page-meta>
<!-- #endif -->
<x-sheet>
<x-text font-size="18" class=" text-weight-b mb-8">我正在听</x-text>
<x-text color="#999999" class="mb-8">声音录制波场</x-text>
</x-sheet>
<x-sheet color="#000" dark-color="#000" :padding="['0']">
<view class="xICanvasHost" :id="canvasId" :style="{height: canvasH + 'px'}">
<x-skia-u ref="skiaRef" :width="canvasW" :height="canvasH" @ready="onSkiaReady"></x-skia-u>
<view class="xICanvasHit"
@click="bindEvents" @touchstart="bindEvents" @touchmove.stop="bindEvents"
@touchend="bindEvents" @touchcancel="bindEvents" @mousedown="bindEvents"
@mousemove="bindEvents" @mouseup="bindEvents"
<!-- #ifdef WEB -->
@mouseleave="bindEvents"
<!-- #endif -->
></view>
</view>
</x-sheet>
</template>
<script setup>
import { ICanvas, ICanvasOptional, ILine, IShapeOptional,
IRect, IText, IShapeVector2d
} from '@/uni_modules/tmx-ui';
const canvasId = "xCanvas-" + Math.random().toString(16).substring(4)
const canvasW = Math.max(1, Math.floor(uni.getWindowInfo().windowWidth))
const canvasH = 450
const skiaRef = ref(null)
const proxy = getCurrentInstance()?.proxy ?? null;
const xcanvas = shallowRef<ICanvas>(new ICanvas({ canvasId: canvasId, component: proxy as any | null } as ICanvasOptional));
let voiceLines : ILine[] = []
let waveTickId : number = -1
const waveColors = ['#00ff88', '#00ccff', '#ff6b6b', '#ffaa00', '#aa00ff'] as string[]
const bindEvents = (evt : UniTouchEvent | UniPointerEvent | UniMouseEvent) => {
xcanvas.value.bindEvent(evt)
}
// 创建语音波动线条
const createVoiceLines = () => {
if (xcanvas.value == null) return;
const render = xcanvas.value! as ICanvas
const centerX = render.boxWidth / 2
const centerY = render.boxHeight / 2
const lineCount = 30 // 线条数量
const lineSpacing = 10
const baseHeight = 20 // 基础高度
const maxHeight = 120 // 最大高度
// 计算起始X位置,使线条居中
const startX = centerX - (lineCount - 1) * lineSpacing / 2
// 创建多条垂直线条
for (let i = 0; i < lineCount; i++) {
const x = startX + i * lineSpacing
// 为每条线设置不同的初始高度
const initialHeight = baseHeight + Math.random() * (maxHeight - baseHeight)
const line = new ILine({
pointStart: { x: x, y: centerY - initialHeight / 2 } as IShapeVector2d,
pointEnd: { x: x, y: centerY + initialHeight / 2 } as IShapeVector2d,
stroke: "#00ff88",
strokeWidth: 2,
lineCap: 'round'
} as IShapeOptional, render)
voiceLines.push(line)
render.addShape(line)
}
}
const startWave = () => {
if (xcanvas.value == null) return;
const render = xcanvas.value! as ICanvas
const baseHeight = 20
const maxHeight = 120
const phases : number[] = []
const speeds : number[] = []
const amps : number[] = []
const n = voiceLines.length
for (let i = 0; i < n; i++) {
phases.push(i * 0.5)
speeds.push(0.004 + Math.random() * 0.003)
amps.push(baseHeight + Math.random() * (maxHeight - baseHeight))
}
if (waveTickId >= 0) {
render.unregisterTick(waveTickId)
waveTickId = -1
}
waveTickId = render.registerTick((time : number) : boolean => {
const colorLen = waveColors.length
for (let i = 0; i < n; i++) {
const pec = Math.sin(time * speeds[i] + phases[i]) * 0.5 + 0.5
let colorIndex = Math.floor(pec * (colorLen - 1))
if (colorIndex < 0) colorIndex = 0
if (colorIndex >= colorLen) colorIndex = colorLen - 1
voiceLines[i].setStroke(waveColors[colorIndex])
voiceLines[i].setHeight(pec * amps[i])
}
return true
})
}
// 初始化绘制
const drawer = () => {
if (xcanvas.value == null) return;
const render = xcanvas.value! as ICanvas
// 设置背景
const bgRect = new IRect({
x: 0,
y: 0,
width: render.boxWidth,
height: render.boxHeight,
fill: "#000",
fillGradient: ['45deg', '#1a1a2e 0%', '#16213e 50%', '#0f3460 100%']
} as IShapeOptional, render)
render.addShape(bgRect)
// 添加标题文字
const titleText = new IText({
text: "正在录制...",
fontSize: 24,
textAlign: 'center',
x: 0,
y: 80,
width: render.boxWidth,
fill: "#00ff88",
}, render)
render.addShape(titleText)
// 添加提示文字
const hintText = new IText({
text: "语音波形实时显示",
fontSize: 16,
textAlign: 'center',
x: 0,
y: render.boxHeight - 80,
width: render.boxWidth,
fill: "#ffffff",
opacity: 0.7
}, render)
render.addShape(hintText)
createVoiceLines()
startWave()
render.render()
}
const onSkiaReady = () => {
const ctx = skiaRef.value?.getContext("2d")
if (ctx == null) return
xcanvas.value.onSurfaceReady(ctx, drawer)
}
onBeforeUnmount(() => {
if (waveTickId >= 0 && xcanvas.value != null) {
xcanvas.value.unregisterTick(waveTickId)
waveTickId = -1
}
voiceLines = [] as ILine[]
xcanvas.value?.destroy()
})
</script>
<style>
.xICanvasHost { position: relative; width: 100%; overflow: hidden; }
.xICanvasHit { position: absolute; left: 0; top: 0; right: 0; bottom: 0; }
</style>