最近,因为业务需要,研究了一波圆形进度条的实现,最终采用了如下这种方式。
先上效果图:
实现思路
- 重点参照了 详解用CSS3制作圆形滚动进度条动画效果 的第三种效果。即采用两个矩形截取完整的两个圆,利用
.progress_wrapper
的overflow: hidden;
属性防止整圆溢出,表现为两个半圆拼接为一个整圆。然后旋转矩形里面的圆,通过改变旋转角度逐渐显示隐藏的圆形部分,表现为音频进度条的变化。 - 另外,这个圆形进度条有点小bug,在两个半圆下方接口处会有一像素的空隙,我采用了一像素背景图配合绝对定位填补这个空隙
WXML代码
核心圆形进度条wxml代码
<view class="circle_container">
<view class="circle_wrapper">
<view class="progress_wrapper circle_right">
<view class="circle_progress right_circle" style="transform: rotate({{rightDeg}});"></view>
</view>
<view class="progress_wrapper circle_left">
<view class="circle_progress left_circle" style="transform: rotate({{leftDeg}});"></view>
</view>
<image src="/images/{{play?'template_play03':'template_play'}}.png" class='play_audio' catchtap='pause_audio'></image>
<view style="visibility: {{visible}}" class="circle_markup"></view>
</view>
</view>
WXSS代码
圆形进度条样式
.circle_container {
position: absolute;
width: 56rpx;
height: 56rpx;
right: 30rpx;
bottom: 20rpx;
}
.circle_wrapper {
position: relative;
width: 56rpx;
height: 56rpx;
}
.progress_wrapper {
width: 28rpx;
height: 56rpx;
position: absolute;
top: 0;
overflow: hidden;
}
.circle_right {
right: 0;
}
.circle_left {
left: 0;
}
.circle_progress {
width: 48rpx;
height: 48rpx;
border: 4rpx solid #fff;
border-radius: 50%;
position: absolute;
top: 0;
transform: rotate(45deg);
}
.right_circle {
border-top: 4rpx solid transparent;
border-right: 4rpx solid transparent;
right: 0;
}
.left_circle {
border-bottom: 4rpx solid transparent;
border-left: 4rpx solid transparent;
left: 0;
}
.circle_markup {
position: absolute;
left: 28rpx;
bottom: 0;
width: 2rpx;
height: 4rpx;
background: #fff;
visibility: hidden;
}
JS代码
使用了WePY框架,其中,formatNumber
格式化时间样式,isPlay
控制音频播放图标,isStop
控制播放悬浮框是否显示,time
表示音频当前播放时间,duration
表示音频总时长,percent
表示音频进度条,play
控制悬浮框音频播放图标,title
表示音频标题,rightDeg
表示右侧半圆旋转角度,leftDeg
表示左侧半圆旋转角度,visible
控制圆形进度条空隙是否显示
import wepy from 'wepy'
import {
formatNumber
} from '@/utils/util'
export default class Index extends wepy.page {
config = {
navigationBarTitleText: '测试'
}
data = {
isPlay: false,
isStop: true,
duration: '',
percent: 0,
play: true,
title: '今日片尾',
time: '',
rightDeg: '',
leftDeg: '',
visible: 'hidden'
}
methods = {
play_audio_func() {
const audio = wx.getBackgroundAudioManager()
audio.src = 'http://audio.heardtech.com/endAudio.mp3'
audio.title = '今日片尾'
}
}
onLoad() {
let leftDeg = ''
let rightDeg = ''
let visible = ''
let currentTime = 0
let duration = 0
const audio = wx.getBackgroundAudioManager()
audio.onPlay(() => {
this.isPlay = true
this.isStop = false
})
audio.onPause(() => {
this.isPlay = false
this.isStop = false
})
audio.onStop(() => {
this.isPlay = false
this.isStop = true
})
audio.onEnded(() => {
// audio.onTimeUpdate(() => {})
this.percent = 0
this.isPlay = false
this.isStop = true
})
audio.onError((e) => {
console.log('音频播放错误', e)
})
audio.onTimeUpdate(() => {
let audioData = this.computePercent(audio)
this.percent = audioData.percent
this.time = audioData.time
currentTime = audioData.currentTime
duration = audioData.duration
this.duration = audioData.dtime
// 右侧半圆在进度超过一半之后要保持旋转225deg状态,未超过一半,左侧半圆保持原始角度45deg
if (currentTime / duration <= 0.5) {
leftDeg = '45deg'
rightDeg = currentTime / duration * 360 + 45 + 'deg'
visible = 'hidden'
} else {
leftDeg = currentTime / duration * 360 + 225 + 'deg'
rightDeg = '225deg'
visible = 'visible'
}
this.rightDeg = rightDeg
this.leftDeg = leftDeg
this.visible = visible
this.$apply()
})
}
computePercent(audio) {
let currentTime = parseInt(audio.currentTime)
let duration = parseInt(audio.duration)
let min = parseInt(currentTime / 60)
let sec = parseInt(currentTime % 60)
let dmin = parseInt(duration / 60)
let dsec = parseInt(duration % 60)
let time = formatNumber(min) + ':' + formatNumber(sec)
let dtime = formatNumber(dmin) + ':' + formatNumber(dsec)
let percent = parseInt(currentTime / duration * 100)
console.log('currentTime:', currentTime, 'percent:', percent, 'duration:', duration)
return {
time,
dtime,
percent,
currentTime,
duration
}
}
}