Skip to content

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

AttributeTypeDefaultDescription
srcStringImage resource address
modeString'scaleToFill'Image cropping and scaling mode
lazy-loadBooleanfalseImage lazy loading, only effective for images under page and scroll-view
fade-showBooleantrueImage display animation effect
webpBooleanfalseDon't parse webP format by default, only supports network resources
show-menu-by-longpressBooleanfalseEnable long press to show mini program code recognition menu
draggableBooleantrueWhether the image can be dragged

Mode Values

ValueDescription
scaleToFillScale image without maintaining aspect ratio to fit completely
aspectFitScale image maintaining aspect ratio so the long side can be displayed completely
aspectFillScale image maintaining aspect ratio, ensuring only the short side can be displayed completely
widthFixWidth unchanged, height auto-changes, maintaining original aspect ratio
heightFixHeight unchanged, width auto-changes, maintaining original aspect ratio
topDon't scale image, only show top area
bottomDon't scale image, only show bottom area
centerDon't scale image, only show center area
leftDon't scale image, only show left area
rightDon't scale image, only show right area
top leftDon't scale image, only show top-left area
top rightDon't scale image, only show top-right area
bottom leftDon't scale image, only show bottom-left area
bottom rightDon't scale image, only show bottom-right area

Events

EventDescriptionReturn Value
@errorTriggered when error occurs, event.detail =
@loadTriggered 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

AttributeTypeDefaultDescription
srcStringVideo resource address to play
autoplayBooleanfalseWhether to autoplay
loopBooleanfalseWhether to loop
mutedBooleanfalseWhether to mute
initial-timeNumber0Initial playback position in seconds
durationNumberVideo duration in seconds
controlsBooleantrueWhether to show default controls
danmu-listArrayDanmu list
danmu-btnBooleanfalseWhether to show danmu button
enable-danmuBooleanfalseWhether to show danmu
directionNumber-1Video direction in fullscreen, auto-determined by aspect ratio if not specified
show-progressBooleantrueWhether to show progress bar
show-fullscreen-btnBooleantrueWhether to show fullscreen button
show-play-btnBooleantrueWhether to show play button in control bar
show-center-play-btnBooleantrueWhether to show center play button
enable-progress-gestureBooleantrueWhether to enable progress control gesture
object-fitStringcontainVideo display mode when size doesn't match container
posterStringVideo poster image URL
show-mute-btnBooleanfalseWhether to show mute button
titleStringVideo title, shown at top in fullscreen
play-btn-positionStringbottomPlay button position
enable-play-gestureBooleanfalseWhether to enable play gesture (double tap to play/pause)
auto-pause-if-navigateBooleantrueAuto pause when navigating to other pages
auto-pause-if-open-nativeBooleantrueAuto pause when opening native pages
vslide-gestureBooleanfalseEnable brightness/volume gesture in non-fullscreen
vslide-gesture-in-fullscreenBooleantrueEnable brightness/volume gesture in fullscreen

Events

EventDescriptionReturn Value
@playTriggered when play/resume
@pauseTriggered when paused
@endedTriggered when playback ends
@timeupdateTriggered when progress changes, event.detail =
@fullscreenchangeTriggered when entering/exiting fullscreen, event.detail =
@waitingTriggered when video buffering
@errorTriggered 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

AttributeTypeDefaultDescription
idStringUnique identifier for audio component
srcStringAudio resource address to play
loopBooleanfalseWhether to loop
controlsBooleanfalseWhether to show default controls
posterStringAudio cover image on default controls
nameStringUnknown AudioAudio name on default controls
authorStringUnknown AuthorAuthor name on default controls
autoplayBooleanfalseWhether to autoplay

Events

EventDescriptionReturn Value
@playTriggered when play/resume
@pauseTriggered when paused
@endedTriggered when playback ends
@timeupdateTriggered when progress changes, event.detail =
@errorTriggered 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

AttributeTypeDefaultDescription
modeStringnormalValid values: normal, scanCode
resolutionStringmediumResolution, valid values: low, medium, high

一次开发,多端部署 - 让跨平台开发更简单