Skip to content

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']">
		<canvas android-layer-type="hardware" :id="canvasId" @click="bindEvents" @touchstart="bindEvents" @touchmove="bindEvents"
			@touchend="bindEvents" @touchcancel="bindEvents" @mousedown="bindEvents" @mousemove="bindEvents"
			@mouseup="bindEvents" <!-- #ifdef WEB -->
			@mouseleave="bindEvents"
			<!-- #endif -->
			style="width: 100%;height: 450px;" ref="canvasDom"
			>
		</canvas>
	</x-sheet>


</template>

<script setup>
	import { ICanvas } from '@/uni_modules/tmx-ui/core/canvas/ICanvas.uts';
	import { CanvasEventType, ICanvasEvent,IShapeVector2d } from '@/uni_modules/tmx-ui/core/canvas/interface.uts';
	import { Easing,EasingFunction } from '@/uni_modules/tmx-ui/core/canvas/lib/animation/tween.uts';

	import { IRect } from '@/uni_modules/tmx-ui/core/canvas/lib/rect.uts';
	import { IText } from '@/uni_modules/tmx-ui/core/canvas/lib/text.uts';
	import { Tween } from '@/uni_modules/tmx-ui/core/canvas/lib/animation/tween.uts';
	import { ImageShape } from '@/uni_modules/tmx-ui/core/canvas/lib/image.uts';
	import { Path2DShape } from '@/uni_modules/tmx-ui/core/canvas/lib/path2d.uts';
	import { IRing } from '@/uni_modules/tmx-ui/core/canvas/lib/ring.uts';
	import { IArc } from '@/uni_modules/tmx-ui/core/canvas/lib/arc.uts';
	import { ISector } from '@/uni_modules/tmx-ui/core/canvas/lib/sector.uts';
	import { IRegularPolygon } from '@/uni_modules/tmx-ui/core/canvas/lib/regularPolygon.uts';
	import { ILinePolygon } from '@/uni_modules/tmx-ui/core/canvas/lib/linePolygon.uts';
	import { ICircle } from '@/uni_modules/tmx-ui/core/canvas/lib/circle.uts';
	import { IEllipse } from '@/uni_modules/tmx-ui/core/canvas/lib/ellipse.uts';
	import { IStar } from '@/uni_modules/tmx-ui/core/canvas/lib/star.uts';
	import { ILine } from '@/uni_modules/tmx-ui/core/canvas/lib/line.uts';
	import { ILayout } from '@/uni_modules/tmx-ui/core/canvas/lib/layout.uts';

	const canvasDom = ref<UniCanvasElement | null>(null)
	const canvasId = "xCanvas-" + Math.random().toString(16).substring(4)
	const proxy = getCurrentInstance()?.proxy ?? null;
	const xcanvas = ref<ICanvas>(new ICanvas({ canvasId: canvasId, component: proxy as any | null }));

	// 存储所有线条和动画
	let voiceLines : ILine[] = []
	let tweens : Tween[] = []

	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 },
				pointEnd: { x: x, y: centerY + initialHeight / 2 },
				stroke: "#00ff88",
				strokeWidth: 2,
				lineCap: 'round'
			}, render)

			voiceLines.push(line)
			render.addShape(line)
			
		}
		
		render.render()
	}

	// 创建波动动画
	const createWaveAnimation = () => {
		if (xcanvas.value == null) return;
		const render = xcanvas.value! as ICanvas

		const baseHeight = 20
		const maxHeight = 120

		voiceLines.forEach((line, index) => {
			// 为每条线创建不同的动画参数
			const animationDuration = 800 + Math.random() * 400 // 800-1200ms
			const delay = index * 50 // 错开动画时间
			const heightVariation = baseHeight + Math.random() * (maxHeight - baseHeight)
		
			// 创建高度变化动画
			const tween = new Tween(line, render)
				.delay(delay)
				.setLoop(-1)
				.onUpdate((progress:number) => {
					// 动态改变颜色
					const progressPec = Math.sin((Date.now()+animationDuration) * 0.005 + index * 0.5) * 0.5 + 0.5
					const colors = [
						'#00ff88',
						'#00ccff', 
						'#ff6b6b',
						'#ffaa00',
						'#aa00ff'
					]
					const colorIndex = Math.floor(progressPec * (colors.length - 1))
					line.setStroke(colors[colorIndex])
					line.setHeight(progressPec*heightVariation)
				})
				.start()

			tweens.push(tween)
		
		})
	}

	// 初始化绘制
	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%']
		}, 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()

		// 启动动画
		createWaveAnimation()
	}

	const oninit = () => {
		xcanvas.value.init()
			.then(() => {
				drawer()
			})
	}

	onReady(() => {
		oninit()
	})

	onBeforeUnmount(() => {
		// 清理所有动画
		tweens.forEach(tween => {
			tween?.destroy()
		})
		tweens = [] as Tween[]
		voiceLines = [] as ILine[]
		xcanvas.value?.destroy()
	})
</script>

<style>

</style>
最近更新