第8章 8.2 音视频:uni.createVideoContext
上一章我们搞定了地图和定位,学会了在地图上标记位置、获取用户坐标。这一章我们要给 App 装上「眼睛和耳朵」——让它们能看见画面、听见声音。学完这章,你就能做出视频播放器、录音机、甚至简易的拍照录像工具。
你有没有遇到过这种情况:刷短视频时,点一下暂停、再点一下继续,流畅得像变魔术?或者用过录音 App,录完之后立刻就能回放?这些功能的背后,都是音视频 API 在默默工作。
今天我们就来搞定 uniapp 里的音视频开发。学完你能:
- 做一个自己的视频播放器
- 写一个录音并回放的小工具
- 组合做出拍照 + 录像的综合工具
🎯 开场 3 分钟:为什么要学这个?
先问大家一个问题:你手机里装了多少个音视频相关的 App?
抖音、快手、微信语音、网易云音乐……几乎每个 App 都在用音视频。音视频能力已经是现代 App 的标配,不会做音视频,就像开餐厅不会做菜——你其他方面做得再好,客户也觉得少了点啥。
举一个真实的痛点:你想给家里\n\n
\n\n
\n\n老人做个「家庭相册 App」,可以看照片、听老歌、甚至录一段祝福视频发给大家。要是不知道怎么用音视频 API,这功能就做不了。
学完这一章,这个 App 你能做出来。
🧱 基础 25 分钟:核心概念
8.2.1 视频播放:就像去电影院
想象你去电影院看电影:
- 首先你得有个屏幕——这就是
<video>组件,uniapp 里放视频就靠它 - 然后你需要一把钥匙——
uni.createVideoContext就是这把钥匙,它能控制视频的播放、暂停、跳转 - 钥匙插进去才能开灯——创建完 context,你才能真正操控视频
用生活类比:video 组件是电影院的IMAX巨幕,createVideoContext 是放映厅的遥控器。没有遥控器,你只能眼巴巴看着屏幕;有了遥控器,你才能想播就播、想停就停。
8.2.2 创建视频上下文
// wxml 里先放一个 video 组件,记得给它起个 id
// <video id="myVideo" src="{{videoUrl}}" bindended="onVideoEnded"></video>
// js 里创建控制上下文
const videoContext = uni.createVideoContext('myVideo')
uni.createVideoContext('myVideo')中的'myVideo'要和video组件的id一致- 这行代码执行后,
videoContext就是你控制视频的「遥控器」
8.2.3 用遥控器控制视频
创建好 context,你就可以调用各种方法:
// 播放视频
videoContext.play()
// 暂停视频
videoContext.pause()
// 跳到指定位置(单位:秒)
videoContext.seek(30)
// 停止播放
videoContext.stop()
这四个方法就是遥控器的四个按钮,基本涵盖了视频控制的所有场景。
8.2.4 视频组件的属性
光有遥控器不够,视频组件本身也要配置好:
<video
id="myVideo"
src="{{currentVideo}}"
controls="{{true}}"
autoplay="{{false}}"
loop="{{false}}"
muted="{{false}}"
bindplay="onPlay"
bindpause="onPause"
bindended="onEnded"
binderror="onError"
poster="{{posterImage}}"
objectFit="contain"
/>
| 属性 | 作用 | 可选值 |
|---|---|---|
src |
视频地址 | 字符串 |
controls |
是否显示控制条 | true/false |
autoplay |
是否自动播放 | true/false |
loop |
是否循环播放 | true/false |
poster |
视频封面图 | 图片地址 |
objectFit |
视频填充方式 | contain/fill/cover |
举个例子:
objectFit="contain"像是把照片装进相框,完整显示但可能有黑边;objectFit="fill"像是拉伸铺满,可能变形。
8.2.5 音频播放:更轻量的声音控制
视频太重了?如果你只需要播放声音(比如背景音乐、语音播报),用 InnerAudioContext 更轻便:
// 创建音频上下文(相当于一个随身听)
const audioContext = uni.createInnerAudioContext()
// 设置音频源
audioContext.src = 'https://example.com/music.mp3'
// 播放
audioContext.play()
// 暂停
audioContext.pause()
// 监听播放完成
audioContext.onPlay(() => {
console.log('开始播放了')
})
audioContext.onEnded(() => {
console.log('播完了')
})
类比:视频是「电影院」,音频是「随身听」——功能更少,但更轻便省电。
8.2.6 录音功能:给App装上耳朵
录音需要用到 RecorderManager,步骤很简单:
// 获取录音管理器
const recorderManager = uni.getRecorderManager()
// 监听录音开始
recorderManager.onStart(() => {
console.log('开始录音')
})
// 监听录音结束
recorderManager.onStop((res) => {
console.log('录音文件路径:', res.tempFilePath)
// res.tempFilePath 就是录好的音频文件
})
// 开始录音(最长时间 60 秒)
recorderManager.start({
duration: 60000,
format: 'mp3'
})
// 停止录音
recorderManager.stop()
注意:录音功能需要用户授权,在
pages.json里要配置权限描述。
8.2.7 相机拍照:捕捉瞬间
相机功能用 CameraContext:
// 创建相机上下文
const cameraContext = uni.createCameraContext()
// 拍照
cameraContext.takePhoto({
quality: 'high',
success: (res) => {
console.log('照片路径:', res.tempImagePath)
}
})
🔥 实战 35 分钟:3 个递进的小项目
项目 1:5分钟 - 简易视频播放器
目标:点击按钮播放/暂停视频
<!-- video-player.wxml -->
<view class="container">
<video
id="myPlayer"
src="{{videoUrl}}"
controls
bindended="onEnded"
></video>
<view class="btn-group">
<button bindtap="playVideo">▶ 播放</button>
<button bindtap="pauseVideo">⏸ 暂停</button>
<button bindtap="restartVideo">🔄 重播</button>
</view>
<view class="tip">当前状态:{{status}}</view>
</view>
// video-player.js
Page({
data: {
videoUrl: 'https://example.com/demo.mp4',
status: '等待播放'
},
onReady() {
// 页面加载完成后创建视频上下文
this.videoContext = uni.createVideoContext('myPlayer')
},
playVideo() {
this.videoContext.play()
this.setData({ status: '正在播放' })
},
pauseVideo() {
this.videoContext.pause()
this.setData({ status: '已暂停' })
},
restartVideo() {
this.videoContext.seek(0)
this.videoContext.play()
this.setData({ status: '正在播放' })
},
onEnded() {
this.setData({ status: '播放结束' })
}
})
预期输出:点击「播放」按钮,视频开始播放,状态显示"正在播放";点击「暂停」显示"已暂停";点击「重播」从头开始。
一句话解释:创建 VideoContext 后,调用 play()/pause()/seek() 就能控制视频,就像拿到了遥控器。
项目 2:15分钟 - 视频列表切换器
目标:从列表中选择视频播放,模拟一个简易的短视频切换功能
<!-- video-list.wxml -->
<view class="container">
<!-- 当前播放的视频 -->
<video
id="mainPlayer"
src="{{currentVideo.url}}"
controls
autoplay
></video>
<!-- 视频信息 -->
<view class="video-info">
<text class="title">{{currentVideo.title}}</text>
<text class="desc">{{currentVideo.desc}}</text>
</view>
<!-- 视频列表 -->
<view class="video-list">
<text class="list-title">推荐视频:</text>
<view
wx:for="{{videoList}}"
wx:key="id"
class="list-item {{item.id === currentVideo.id ? 'active' : ''}}"
bindtap="switchVideo"
data-id="{{item.id}}"
>
<image src="{{item.thumb}}" mode="aspectFill" />
<text>{{item.title}}</text>
</view>
</view>
</view>
// video-list.js
Page({
data: {
currentVideo: null,
videoList: [
{
id: 1,
title: 'Python 入门第一课',
desc: '5分钟学会变量和循环',
url: 'https://example.com/python1.mp4',
thumb: '/static/thumb/python1.jpg'
},
{
id: 2,
title: 'JavaScript 速通',
desc: '前端工程师必修课',
url: 'https://example.com/js1.mp4',
thumb: '/static/thumb/js1.jpg'
},
{
id: 3,
title: 'uniapp 实战',
desc: '从小程序到App开发',
url: 'https://example.com/uni1.mp4',
thumb: '/static/thumb/uni1.jpg'
}
]
},
onLoad() {
this.videoContext = uni.createVideoContext('mainPlayer')
this.setData({ currentVideo: this.data.videoList[0] })
},
switchVideo(e) {
const id = e.currentTarget.dataset.id
const video = this.data.videoList.find(v => v.id === id)
if (video) {
this.setData({ currentVideo: video })
}
}
})
预期输出:页面加载自动播放第一个视频,点击列表中的其他视频,当前视频切换到新视频并自动播放。
一句话解释:维护一个视频列表,点击时更新 currentVideo 数据,video 组件的 src 绑定会自动触发切换。
项目 3:15分钟 - 录音 + 回放综合工具
目标:录音 -> 显示录音列表 -> 点击回放,完整实现「录-存-播」流程
<!-- voice-recorder.wxml -->
<view class="container">
<!-- 录音按钮区 -->
<view class="record-section">
<button
type="{{isRecording ? 'warn' : 'primary'}}"
bindtap="toggleRecord"
>
{{isRecording ? '⏹ 停止录音' : '🎤 开始录音'}}
</button>
<text class="timer">{{recordTime}}秒</text>
</view>
<!-- 录音列表 -->
<view class="record-list">
<text class="list-title">录音记录:</text>
<view
wx:for="{{recordings}}"
wx:key="id"
class="record-item"
>
<view class="record-info">
<text class="record-name">{{item.name}}</text>
<text class="record-duration">{{item.duration}}秒</text>
</view>
<view class="record-actions">
<button size="mini" bindtap="playRecording" data-id="{{item.id}}">▶ 播放</button>
<button size="mini" type="warn" bindtap="deleteRecording" data-id="{{item.id}}">删除</button>
</view>
</view>
<view wx:if="{{recordings.length === 0}}" class="empty-tip">
还没有录音,试试点击上方按钮
</view>
</view>
</view>
// voice-recorder.js
Page({
data: {
isRecording: false,
recordTime: 0,
recordings: [],
timer: null
},
onLoad() {
this.recorderManager = uni.getRecorderManager()
this.recorderManager.onStart(() => {
this.setData({ isRecording: true })
this.startTimer()
})
this.recorderManager.onStop((res) => {
this.setData({ isRecording: false })
this.stopTimer()
this.addRecording(res.tempFilePath)
})
// 音频实例(用于回放)
this.audioContext = uni.createInnerAudioContext()
},
toggleRecord() {
if (this.data.isRecording) {
this.recorderManager.stop()
} else {
this.recorderManager.start({
duration: 60000,
format: 'mp3'
})
}
},
startTimer() {
this.setData({ recordTime: 0 })
this.timer = setInterval(() => {
this.setData({ recordTime: this.data.recordTime + 1 })
}, 1000)
},
stopTimer() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
},
addRecording(filePath) {
const newRecording = {
id: Date.now(),
name: `录音${this.data.recordings.length + 1}`,
path: filePath,
duration: this.data.recordTime
}
this.setData({
recordings: [...this.data.recordings, newRecording]
})
},
playRecording(e) {
const id = e.currentTarget.dataset.id
const recording = this.data.recordings.find(r => r.id === id)
if (recording) {
this.audioContext.src = recording.path
this.audioContext.play()
}
},
deleteRecording(e) {
const id = e.currentTarget.dataset.id
const recordings = this.data.recordings.filter(r => r.id !== id)
this.setData({ recordings })
},
onUnload() {
this.stopTimer()
this.audioContext.destroy()
}
})
预期输出:点击「开始录音」,计时器开始计时;点击「停止录音」,新录音出现在列表中;点击列表项的「播放」可以回放录音;点击「删除」移除该录音。
一句话解释:RecorderManager 负责录音,InnerAudioContext 负责回放,两者配合实现完整的录音工具。
💪 进阶 20 分钟:常见坑 + 性能小贴士
坑 1:video 组件的 id 和 createVideoContext 对不上
// ❌ 错误:id 写错了
// <video id="myVideo" ...></video>
const ctx = uni.createVideoContext('myVideo1') // 拼写错误!
// ✅ 正确:id 要完全一致
const ctx = uni.createVideoContext('myVideo')
提示:养成习惯,id 统一用驼峰命名,写完检查一遍。
坑 2:视频地址是空的
// ❌ 错误:src 为空时播放会报错
this.setData({ videoUrl: '' })
this.videoContext.play()
// ✅ 正确:先检查 src 是否有值
if (this.data.videoUrl) {
this.videoContext.play()
}
坑 3:录音没配权限
// ❌ 错误:在微信小程序里直接录音,没配置权限
// manifest.json 里没配,pages.json 里也没配
// ✅ 正确:需要在 manifest.json 配置
{
"mp-weixin": {
"permission": {
"scope.record": {
"desc": "用于录音功能"
}
}
}
}
坑 4:autoplay 在有些平台不生效
// ❌ 错误:iOS 微信小程序禁止自动播放
// <video autoplay="{{true}}" ...></video>
// ✅ 正确:iOS 最好让用户主动触发播放
// autoplay 设为 false,点击按钮后再 play()
坑 5:音频播放完成没监听
// ❌ 错误:没写 onEnded 监听,播放完不知道
this.audioContext.play()
// ✅ 正确:监听播放完成事件
this.audioContext.onEnded(() => {
console.log('播放结束了')
// 可以在这里做下一首自动播放等逻辑
})
性能小优化:视频封面用懒加载
// ❌ 一次性加载所有视频封面
const videos = [
{ url: '...', poster: 'https://example.com/p1.jpg' },
{ url: '...', poster: 'https://example.com/p2.jpg' },
// ... 100个
]
// ✅ 滚动到可见区域再加载封面
// 使用 IntersectionObserver 监听元素可见性
调试技巧:善用 bind 事件
<!-- 视频组件绑定所有事件 -->
<video
bindplay="onPlay"
bindpause="onPause"
bindended="onEnded"
binderror="onError"
bindtimeupdate="onTimeUpdate"
/>
onPlay(e) {
console.log('开始播放', e.detail)
},
onTimeUpdate(e) {
// 可以用来更新进度条
const { currentTime, duration } = e.detail
this.setData({ progress: (currentTime / duration) * 100 })
},
onError(e) {
console.error('视频出错了', e.detail)
uni.showToast({ title: '视频加载失败' })
}
✏️ 练习题 + 作业题
练习题(10 分钟)
练习 1(2 分钟):播放控制
- 输入:视频 URL https://example.com/test.mp4
- 预期输出:点击「播放」按钮视频开始播放,点击「暂停」按钮视频暂停
- 提示:直接复用项目 1 的代码,只改一下 videoUrl
练习 2(2 分钟):添加静音功能
- 输入:在项目 1 基础上加一个静音按钮
- 预期输出:点击「静音」按钮,视频切换静音状态
- 提示:videoContext.muted = true/false
练习 3(2 分钟):视频进度条
- 输入:在项目 1 的视频下方显示当前播放进度(如 "00:30 / 03:45")
- 预期输出:播放时实时更新进度文字
- 提示:用 bindtimeupdate 事件获取 currentTime
练习 4(2 分钟):切换到新数据
- 输入:用项目 2 的结构,处理这个视频列表:
const videos = [
{ id: 1, title: '教程1', url: '...', thumb: '...' },
{ id: 2, title: '教程2', url: '...', thumb: '...' }
]
- 预期输出:页面显示两个视频,点击可以切换
- 提示:把 videos 赋值给
videoList即可
练习 5(2 分钟):录音超时处理
- 输入:用户录音超过 30 秒时提示"录音超时"
- 预期输出:录音 30 秒时自动停止并弹出提示
- 提示:在 onStart 回调里设置 setTimeout,30 秒后调用 stop()
作业题(30 分钟 - 2 小时)
作业:做一个「简易视频播放器」
- 需求描述:实现一个带播放列表的视频播放器,可以切换视频、显示进度
- 功能点:
1. 顶部显示当前播放视频的标题
2. 中间是视频播放器,带播放/暂停按钮
3. 下方是视频列表,点击切换播放
4. 显示当前播放进度(如 "01:23 / 05:00") - 加分项:
1. 支持播放/暂停图标切换(▶ ↔ ⏸)
2. 点击进度条可以跳转
3. 视频切换时平滑过渡 - 验收标准:
- 能跑起来,不报错
- 视频列表至少 3 个视频
- 点击列表项能切换视频
- 代码有适当注释
- 提交方式:评论区贴核心代码或 GitHub 链接
📚 总结 + 资源
本文学了 3 个核心点:
- VideoContext 是遥控器 —
uni.createVideoContext创建控制对象,play/pause/seek/stop是四个基本按钮 - InnerAudioContext 是随身听 — 只需要播放声音时用它,更轻量
- RecorderManager 是录音笔 —
start/stop控制录音,onStop拿到录音文件路径
推荐延伸资源:
- uni-app 官方文档 - 媒体组件
- uni-app 官方文档 - 录音管理
- 视频教程:慕课网「uni-app 实战音视频」
互动钩子:
你做过音视频相关的项目吗?遇到过什么坑?是视频加载慢、还是录音权限被拒?评论区聊聊,老粉优先回复!
下章预告:
学会了「眼睛」和「耳朵」,下一章我们要解锁一个新技能——蓝牙和 NFC。想象一下,用手机靠近一下就能开门、刷卡、连接设备,这些功能怎么用代码实现?下一章见!

评论(0)