Media Components
Media components are used to display and handle multimedia content, including images, audio, video, etc. uni-app provides rich media components to help developers easily implement multimedia features.
image
The image
component is used to display images, supporting JPG, PNG, SVG, WEBP, GIF formats.
Attributes
Attribute | Type | Default | Description |
---|---|---|---|
src | String | Image resource address | |
mode | String | 'scaleToFill' | Image cropping and scaling mode |
lazy-load | Boolean | false | Image lazy loading, only effective for images under page and scroll-view |
fade-show | Boolean | true | Image display animation effect |
webp | Boolean | false | Don't parse webP format by default, only supports network resources |
show-menu-by-longpress | Boolean | false | Enable long press to show mini program code recognition menu |
draggable | Boolean | true | Whether the image can be dragged |
Mode Values
Value | Description |
---|---|
scaleToFill | Scale image without maintaining aspect ratio to fit completely |
aspectFit | Scale image maintaining aspect ratio so the long side can be displayed completely |
aspectFill | Scale image maintaining aspect ratio, ensuring only the short side can be displayed completely |
widthFix | Width unchanged, height auto-changes, maintaining original aspect ratio |
heightFix | Height unchanged, width auto-changes, maintaining original aspect ratio |
top | Don't scale image, only show top area |
bottom | Don't scale image, only show bottom area |
center | Don't scale image, only show center area |
left | Don't scale image, only show left area |
right | Don't scale image, only show right area |
top left | Don't scale image, only show top-left area |
top right | Don't scale image, only show top-right area |
bottom left | Don't scale image, only show bottom-left area |
bottom right | Don't scale image, only show bottom-right area |
Events
Event | Description | Return Value |
---|---|---|
@error | Triggered when error occurs, event.detail = | |
@load | Triggered when image loads completely, event.detail = |
Example Code
vue
<template>
<view class="container">
<!-- Basic usage -->
<image src="/static/images/logo.png" mode="aspectFit"></image>
<!-- Using different scaling modes -->
<view class="image-list">
<view class="image-item" v-for="(mode, index) in modes" :key="index">
<image :src="imageUrl" :mode="mode" @error="imageError"></image>
<text>{{mode}}</text>
</view>
</view>
<!-- Lazy loading -->
<image src="https://example.com/large-image.jpg" lazy-load></image>
<!-- Listen to image load completion -->
<image src="/static/images/banner.jpg" @load="imageLoaded"></image>
</view>
</template>
<script>
export default {
data() {
return {
imageUrl: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/uni-app.png',
modes: ['scaleToFill', 'aspectFit', 'aspectFill', 'widthFix', 'heightFix']
}
},
methods: {
imageError(e) {
console.error('Image load failed', e.detail.errMsg);
},
imageLoaded(e) {
console.log('Image loaded', e.detail.width, e.detail.height);
}
}
}
</script>
<style>
.container {
padding: 15px;
}
.image-list {
display: flex;
flex-wrap: wrap;
}
.image-item {
width: 33.33%;
padding: 5px;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
}
.image-item image {
width: 100%;
height: 100px;
}
.image-item text {
font-size: 12px;
margin-top: 5px;
}
</style>
video
The video
component is used to play videos, supported formats depend on platform.
Attributes
Attribute | Type | Default | Description |
---|---|---|---|
src | String | Video resource address to play | |
autoplay | Boolean | false | Whether to autoplay |
loop | Boolean | false | Whether to loop |
muted | Boolean | false | Whether to mute |
initial-time | Number | 0 | Initial playback position in seconds |
duration | Number | Video duration in seconds | |
controls | Boolean | true | Whether to show default controls |
danmu-list | Array | Danmu list | |
danmu-btn | Boolean | false | Whether to show danmu button |
enable-danmu | Boolean | false | Whether to show danmu |
direction | Number | -1 | Video direction in fullscreen, auto-determined by aspect ratio if not specified |
show-progress | Boolean | true | Whether to show progress bar |
show-fullscreen-btn | Boolean | true | Whether to show fullscreen button |
show-play-btn | Boolean | true | Whether to show play button in control bar |
show-center-play-btn | Boolean | true | Whether to show center play button |
enable-progress-gesture | Boolean | true | Whether to enable progress control gesture |
object-fit | String | contain | Video display mode when size doesn't match container |
poster | String | Video poster image URL | |
show-mute-btn | Boolean | false | Whether to show mute button |
title | String | Video title, shown at top in fullscreen | |
play-btn-position | String | bottom | Play button position |
enable-play-gesture | Boolean | false | Whether to enable play gesture (double tap to play/pause) |
auto-pause-if-navigate | Boolean | true | Auto pause when navigating to other pages |
auto-pause-if-open-native | Boolean | true | Auto pause when opening native pages |
vslide-gesture | Boolean | false | Enable brightness/volume gesture in non-fullscreen |
vslide-gesture-in-fullscreen | Boolean | true | Enable brightness/volume gesture in fullscreen |
Events
Event | Description | Return Value |
---|---|---|
@play | Triggered when play/resume | |
@pause | Triggered when paused | |
@ended | Triggered when playback ends | |
@timeupdate | Triggered when progress changes, event.detail = | |
@fullscreenchange | Triggered when entering/exiting fullscreen, event.detail = | |
@waiting | Triggered when video buffering | |
@error | Triggered when video error occurs |
Example Code
vue
<template>
<view class="container">
<video
id="myVideo"
src="https://example.com/video.mp4"
controls
:autoplay="false"
:loop="false"
:muted="isMuted"
object-fit="cover"
poster="https://example.com/poster.jpg"
@play="videoPlay"
@pause="videoPause"
@ended="videoEnded"
@timeupdate="videoTimeUpdate"
@error="videoError"
></video>
<view class="video-controls">
<button @click="playVideo">Play</button>
<button @click="pauseVideo">Pause</button>
<button @click="toggleMute">{{ isMuted ? 'Unmute' : 'Mute' }}</button>
<button @click="seekVideo">Seek to 30s</button>
</view>
<!-- Video with danmu -->
<video
src="https://example.com/video2.mp4"
controls
danmu-btn
enable-danmu
:danmu-list="danmuList"
></video>
<view class="danmu-controls">
<input v-model="danmuValue" placeholder="Enter danmu content" />
<button @click="sendDanmu">Send Danmu</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
isMuted: false,
videoContext: null,
danmuList: [
{ text: 'Danmu at 1s', color: '#ff0000', time: 1 },
{ text: 'Danmu at 3s', color: '#00ff00', time: 3 }
],
danmuValue: ''
}
},
onReady() {
// Get video context
this.videoContext = uni.createVideoContext('myVideo', this);
},
methods: {
videoPlay() {
console.log('Video started playing');
},
videoPause() {
console.log('Video paused');
},
videoEnded() {
console.log('Video ended');
},
videoTimeUpdate(e) {
console.log('Current time:', e.detail.currentTime);
console.log('Total duration:', e.detail.duration);
},
videoError(e) {
console.error('Video error:', e.detail);
},
playVideo() {
this.videoContext.play();
},
pauseVideo() {
this.videoContext.pause();
},
toggleMute() {
this.isMuted = !this.isMuted;
},
seekVideo() {
this.videoContext.seek(30);
},
sendDanmu() {
if (this.danmuValue) {
this.videoContext.sendDanmu({
text: this.danmuValue,
color: this.getRandomColor()
});
this.danmuValue = '';
}
},
getRandomColor() {
const colors = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff'];
return colors[Math.floor(Math.random() * colors.length)];
}
}
}
</script>
<style>
.container {
padding: 15px;
}
video {
width: 100%;
}
.video-controls, .danmu-controls {
display: flex;
justify-content: space-around;
margin-top: 10px;
margin-bottom: 20px;
}
.danmu-controls {
flex-direction: column;
}
.danmu-controls input {
margin-bottom: 10px;
height: 40px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 0 10px;
}
</style>
audio
The audio
component is used to play audio, supported formats depend on platform.
Attributes
Attribute | Type | Default | Description |
---|---|---|---|
id | String | Unique identifier for audio component | |
src | String | Audio resource address to play | |
loop | Boolean | false | Whether to loop |
controls | Boolean | false | Whether to show default controls |
poster | String | Audio cover image on default controls | |
name | String | Unknown Audio | Audio name on default controls |
author | String | Unknown Author | Author name on default controls |
autoplay | Boolean | false | Whether to autoplay |
Events
Event | Description | Return Value |
---|---|---|
@play | Triggered when play/resume | |
@pause | Triggered when paused | |
@ended | Triggered when playback ends | |
@timeupdate | Triggered when progress changes, event.detail = | |
@error | Triggered when audio error occurs |
Example Code
vue
<template>
<view class="container">
<!-- Basic usage -->
<audio
id="myAudio"
src="https://example.com/audio.mp3"
:controls="true"
:loop="false"
poster="https://example.com/poster.jpg"
name="Sample Audio"
author="Sample Author"
@play="audioPlay"
@pause="audioPause"
@ended="audioEnded"
@timeupdate="audioTimeUpdate"
@error="audioError"
></audio>
<!-- Custom controller -->
<view class="custom-audio">
<view class="audio-info">
<image class="audio-poster" :src="audioPoster" mode="aspectFill"></image>
<view class="audio-text">
<text class="audio-name">{{audioName}}</text>
<text class="audio-author">{{audioAuthor}}</text>
</view>
</view>
<view class="audio-controls">
<text class="audio-time">{{currentTime}}/{{duration}}</text>
<slider
class="audio-slider"
:value="sliderValue"
:max="100"
:min="0"
:step="1"
@change="sliderChange"
@changing="sliderChanging"
></slider>
<view class="audio-buttons">
<text class="iconfont icon-prev" @click="prevAudio"></text>
<text
class="iconfont"
:class="isPlaying ? 'icon-pause' : 'icon-play'"
@click="togglePlay"
></text>
<text class="iconfont icon-next" @click="nextAudio"></text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
audioContext: null,
isPlaying: false,
currentTime: '00:00',
duration: '00:00',
sliderValue: 0,
audioPoster: 'https://example.com/poster.jpg',
audioName: 'Custom Audio Player',
audioAuthor: 'Sample Author',
audioList: [
{
src: 'https://example.com/audio1.mp3',
poster: 'https://example.com/poster1.jpg',
name: 'Audio 1',
author: 'Author 1'
},
{
src: 'https://example.com/audio2.mp3',
poster: 'https://example.com/poster2.jpg',
name: 'Audio 2',
author: 'Author 2'
}
],
currentAudioIndex: 0
}
},
onReady() {
// Get audio context
this.audioContext = uni.createAudioContext('myAudio', this);
// Listen to audio load status
this.audioContext.onCanplay(() => {
this.audioContext.duration();
});
// Listen to audio play status
this.audioContext.onPlay(() => {
this.isPlaying = true;
});
// Listen to audio pause status
this.audioContext.onPause(() => {
this.isPlaying = false;
});
// Listen to audio end status
this.audioContext.onEnded(() => {
this.isPlaying = false;
this.nextAudio();
});
// Listen to audio progress update
this.audioContext.onTimeUpdate(() => {
const currentTime = this.audioContext.currentTime;
const duration = this.audioContext.duration;
this.currentTime = this.formatTime(currentTime);
this.duration = this.formatTime(duration);
// Update progress bar
if (duration > 0) {
this.sliderValue = (currentTime / duration) * 100;
}
});
},
methods: {
audioPlay() {
console.log('Audio started playing');
},
audioPause() {
console.log('Audio paused');
},
audioEnded() {
console.log('Audio ended');
},
audioTimeUpdate(e) {
console.log('Current time:', e.detail.currentTime);
console.log('Total duration:', e.detail.duration);
},
audioError(e) {
console.error('Audio error:', e.detail);
},
togglePlay() {
if (this.isPlaying) {
this.audioContext.pause();
} else {
this.audioContext.play();
}
},
sliderChange(e) {
const value = e.detail.value;
const duration = this.audioContext.duration;
const currentTime = (value / 100) * duration;
this.audioContext.seek(currentTime);
},
sliderChanging(e) {
const value = e.detail.value;
const duration = this.audioContext.duration;
const currentTime = (value / 100) * duration;
this.currentTime = this.formatTime(currentTime);
},
prevAudio() {
this.currentAudioIndex = (this.currentAudioIndex - 1 + this.audioList.length) % this.audioList.length;
this.changeAudio();
},
nextAudio() {
this.currentAudioIndex = (this.currentAudioIndex + 1) % this.audioList.length;
this.changeAudio();
},
changeAudio() {
const audio = this.audioList[this.currentAudioIndex];
this.audioPoster = audio.poster;
this.audioName = audio.name;
this.audioAuthor = audio.author;
this.audioContext.src = audio.src;
this.audioContext.play();
},
formatTime(time) {
time = Math.floor(time);
const minutes = Math.floor(time / 60).toString().padStart(2, '0');
const seconds = Math.floor(time % 60).toString().padStart(2, '0');
return `${minutes}:${seconds}`;
}
}
}
</script>
<style>
.container {
padding: 15px;
}
.custom-audio {
margin-top: 30px;
border: 1px solid #eee;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.audio-info {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.audio-poster {
width: 60px;
height: 60px;
border-radius: 4px;
margin-right: 15px;
}
.audio-text {
flex: 1;
}
.audio-name {
font-size: 16px;
font-weight: bold;
margin-bottom: 5px;
display: block;
}
.audio-author {
font-size: 14px;
color: #666;
display: block;
}
.audio-controls {
position: relative;
}
.audio-time {
font-size: 12px;
color: #999;
margin-bottom: 5px;
display: block;
}
.audio-slider {
margin-bottom: 15px;
}
.audio-buttons {
display: flex;
justify-content: center;
align-items: center;
}
.iconfont {
font-size: 24px;
margin: 0 20px;
}
.icon-play, .icon-pause {
font-size: 32px;
}
</style>
camera
The camera
component is used to call the camera for taking photos or recording videos.
Attributes
Attribute | Type | Default | Description |
---|---|---|---|
mode | String | normal | Valid values: normal, scanCode |
resolution | String | medium | Resolution, valid values: low, medium, high |