简介
鉴于一直找包含了taro+hooks+ts的项目未果,所以自己写了一个,大概花了两周时间,只完成了一些基本的功能。后台用的NeteaseCloudMusicApi开源接口。状态管理redux redux-saga
一、环境准备
搭建网易云后台
git clone git@github.com:Binaryify/NeteaseCloudMusicApi.git
npm install
node app.js
搭建小程序
git clone git@github.com:v1nkon/taro_music.git
npm install
npm run dev:weapp
二、主要功能
- 首页展示
- 歌单列表
- 歌曲播放页面
- 搜索页
三、核心代码
整个项目逻辑比较复杂的部分应该就是歌曲播放的页面咯
播放页面
进入播放页面需要通过songId请求歌曲
let [showLyric, setShowLyric] = useState(false)
useEffect(()=> {
// loadingSongDetail(1446522620)
//如果当前歌曲不存在 且是请求新歌的时候 需要请求
if( notSameSongOrNotExist(this.$router.params.songId) ){
loadingSongDetail(this.$router.params.songId )
}
}, [])
useEffect(()=>{
song && Taro.setNavigationBarTitle({
title: song.name
});
}, [song])
音乐播放组件(Audio)
包含了事件监听,歌词滚动,歌曲拖动等
let { song, preSong, playSongList, isPlay, togglePlay, setShowLyric, showLyric } = props
let [curLong, setCurLong] = useState(0)
let [isChanging, setIsChanging] = useState(false)
let [songLyric, setSongLyric] = useState<Array<LYRIC>>([])
let [viewId, setViewId] = useState("z1")
let [showMusicList, setShowMusicList] = useState(false)
useEffect( () => {
if( song && song.url ){
setSongLyric( formatLyric(song.lyric!) )
innerAudioContext.src = song.url
setCurLong(0)
togglePlay(true)
}else if(song && !song.url){
setCurLong(0)
Taro.showToast({
title:'当前歌曲无权限\r 自动跳到下一首',
icon:'none',
duration:2000
})
setTimeout(()=>{
audioToggleSong(1)
},3000)
}
} , [song, preSong, setCurLong])
let timeUpdate = useCallback(() => {
isChanging || setCurLong( innerAudioContext.currentTime )
}, [setCurLong, isChanging])
useEffect( () => {
onPlay()
innerAudioContext && innerAudioContext.onTimeUpdate( timeUpdate )
return () => {
offPlay()
innerAudioContext && innerAudioContext.offTimeUpdate( timeUpdate )
}
} ,[innerAudioContext, timeUpdate, onPlay, offPlay])
useEffect( () => {
offEnded()
innerAudioContext && innerAudioContext.onEnded( audioToggleSong)
return () => {
innerAudioContext && innerAudioContext.offEnded( audioToggleSong )
onEnded()
}
} , [innerAudioContext, audioToggleSong, offEnded, onEnded])
let slideChange = useCallback( ({detail}) => {
setCurLong( detail.value )
innerAudioContext.stop()
innerAudioContext.seek( detail.value )
setIsChanging(false)
setTimeout(()=>{
innerAudioContext.play()
}, 100)
} , [song, setIsChanging, innerAudioContext])
let slideChanging = useCallback( ({detail}) => {
setCurLong( detail.value )
setIsChanging(true)
} , [song, setIsChanging, innerAudioContext])
useEffect(()=>{
let curId = "z" + Math.floor(curLong)
let exist = songLyric.filter(lyric => {
return lyric.id === curId
}).length
exist && setViewId(curId)
}, [curLong, setViewId, songLyric])
let toggleMusicList = useCallback( () => {
setShowMusicList(pre => !pre)
}, [setShowMusicList] )
innerAudioContext(统一管理音乐播放器的各个事件:切换歌曲)
包含了事件监听,歌词滚动,歌曲拖动等
import Taro from '@tarojs/taro'
import {Song} from "@/constants/interface"
// import { togglePlay, loadingSongDetail } from '@/actions'
import {togglePlay, loadingSongDetail} from "../store/actions"
import store from "../store"
const innerAudioContext = Taro.createInnerAudioContext()
innerAudioContext.autoplay = true
innerAudioContext.onCanplay(() => {
let duration = innerAudioContext.duration;
console.log('可以播放咯')
console.log(duration)
})
export let audioTogglePlay = () => {
let { songReducer } = store.getState()
let {
isPlay
} = songReducer
store.dispatch(togglePlay())
innerAudioContext && (isPlay ? (innerAudioContext.pause() ) : (innerAudioContext.play() ))
}
export let audioToggleSong = (index = 1) => {
let { songReducer, songListReducer, homeReducer } = store.getState()
let {
song,
} = songReducer
let {
playSongListIds
} = homeReducer
audioTogglePlay()
innerAudioContext.stop()
index = isNaN(index) ? 1 : index
let nextId
if( index === 1 || index === -1 ){
let songIndex = playSongListIds.indexOf(song.id) + index,
totalLength = playSongListIds.length
if( songIndex < 0 ){
songIndex = songIndex + totalLength
}else if(songIndex >= totalLength){
songIndex = songIndex - totalLength
}
nextId = playSongListIds.slice(songIndex, songIndex + 1)[0]
}else{
nextId = index
}
store.dispatch(loadingSongDetail(nextId))
}
let audioOnPlay = () => {
let duration = innerAudioContext.duration;
console.log('开始播放')
console.log(duration)
}
let audioOnCanplay = () => {
let duration = innerAudioContext.duration;
console.log('可以播放咯')
console.log(duration)
}
export function onPlay(){
innerAudioContext.onPlay(audioOnPlay)
innerAudioContext.onCanplay(audioOnCanplay)
innerAudioContext.pause()
innerAudioContext.play()
}
export function offPlay(){
// innerAudioContext.pause()
innerAudioContext.offPlay( audioOnPlay )
innerAudioContext.offCanplay( audioOnCanplay )
}
export function onEnded(){
innerAudioContext.onEnded(audioToggleSong)
}
export function offEnded(){
innerAudioContext.offEnded(audioToggleSong)
}
export default innerAudioContext
四、部分页面截屏
首页

歌单列表页

播放页面



搜索页面


参考
总结
hooks真的非常好用,写代码感觉很舒服。