使用react native制作的一款网络音乐播放器
使用react native制作的一款网络音乐播放器
基于第三方库 react-native-video设计
"react-native-video": "^1.0.0"
播放/暂停
快进/快退
循环模式(单曲,随机,列表)
歌词同步
进度条显示
播放时间
基本旋转动画
动画bug
安卓歌词解析失败
其他
使用的数据是百度音乐
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.billboard.billList&type=2&size=10&offset=0 //总列表
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.song.lry&songid=213508 //歌词文件
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.song.play&songid=877578 //播放
更多:http://67zixue.com/home/article/detail/id/22.html
主要代码
把秒数转换为时间类型:
//把秒数转换为时间类型
formatTime(time) {
// 71s -> 01:11
let min = Math.floor(time / 60)
let second = time - min * 60
min = min >= 10 ? min : '0' + min
second = second >= 10 ? second : '0' + second
return min + ':' + second
}
歌词:
[ti:阳光总在风雨后] [ar:许美静] [al:都是夜归人] [00:05.97]阳光总在风雨后 [00:14.31]演唱:许美静......
拿到当前歌曲的歌词后,如上,把这段字符截成一个这样的数组
其算法如下:
let lry = responseJson.lrcContent
let lryAry = lry.split('\n') //按照换行符切数组
lryAry.forEach(function (val, index) {
var obj = {} //用于存放时间
val = val.replace(/(^\s*)|(\s*$)/g, '') //正则,去除前后空格
let indeofLastTime = val.indexOf(']') // ]的下标
let timeStr = val.substring(1, indeofLastTime) //把时间切出来 0:04.19
let minSec = ''
let timeMsIndex = timeStr.indexOf('.') // .的下标
if (timeMsIndex !== -1) {
//存在毫秒 0:04.19
minSec = timeStr.substring(1, val.indexOf('.')) // 0:04.
obj.ms = parseInt(timeStr.substring(timeMsIndex + 1, indeofLastTime)) //毫秒值 19
} else {
//不存在毫秒 0:04
minSec = timeStr
obj.ms = 0
}
let curTime = minSec.split(':') // [0,04]
obj.min = parseInt(curTime[0]) //分钟 0
obj.sec = parseInt(curTime[1]) //秒钟 04
obj.txt = val.substring(indeofLastTime + 1, val.length) //歌词文本: 留下唇印的嘴
obj.txt = obj.txt.replace(/(^\s*)|(\s*$)/g, '')
obj.dis = false
obj.total = obj.min * 60 + obj.sec + obj.ms / 100 //总时间
if (obj.txt.length > 0) {
lyrObj.push(obj)
}
})
歌词显示:
// 歌词
renderItem() {
// 数组
var itemAry = [];
for (var i = 0; i < lyrObj.length; i++) {
var item = lyrObj[i].txt
if (this.state.currentTime.toFixed(2) > lyrObj[i].total) {
//正在唱的歌词
itemAry.push(
<View key={i} style={styles.itemStyle}>
<Text style={{ color: 'blue' }}> {item} </Text>
</View>
);
_scrollView.scrollTo({x: 0,y:(25 * i),animated:false});
}
else {
//所有歌词
itemAry.push(
<View key={i} style={styles.itemStyle}>
<Text style={{ color: 'red' }}> {item} </Text>
</View>
)
}
} return itemAry;
}
其余什么播放/暂停.时间显示,快进/快退,进度条都是根据react-native-video而来.
完整代码:
/**
* Created by shaotingzhou on 2017/4/13.
*/ import React, { Component } from 'react'
import {
AppRegistry,
StyleSheet,
Dimensions,
Text,
Image,
View,
Slider,
TouchableOpacity,
ScrollView,
ActivityIndicator,
Animated,
Easing
} from 'react-native'
var {width,height} = Dimensions.get('window');
import Video from 'react-native-video'
var lyrObj = [] // 存放歌词
var myAnimate;
// http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.billboard.billList&type=2&size=10&offset=0 //总列表
// http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.song.lry&songid=213508 //歌词文件
// http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.song.play&songid=877578 //播放 export default class Main extends Component { constructor(props) {
super(props);
this.spinValue = new Animated.Value(0)
this.state = {
songs: [], //歌曲id数据源
playModel:1, // 播放模式 1:列表循环 2:随机 3:单曲循环
btnModel:require('./image/列表循环.png'), //播放模式按钮背景图
pic_small:'', //小图
pic_big:'', //大图
file_duration:0, //歌曲长度
song_id:'', //歌曲id
title:'', //歌曲名字
author:'', //歌曲作者
file_link:'', //歌曲播放链接
songLyr:[], //当前歌词
sliderValue: 0, //Slide的value
pause:false, //歌曲播放/暂停
currentTime: 0.0, //当前时间
duration: 0.0, //歌曲时间
currentIndex:0, //当前第几首
isplayBtn:require('./image/播放.png') //播放/暂停按钮背景图
}
}
//上一曲
prevAction = (index) =>{
this.recover()
lyrObj = [];
if(index == -1){
index = this.state.songs.length - 1 // 如果是第一首就回到最后一首歌
}
this.setState({
currentIndex:index //更新数据
})
this.loadSongInfo(index) //加载数据
}
//下一曲
nextAction = (index) =>{
this.recover()
lyrObj = [];
if(index == 10){
index = 0 //如果是最后一首就回到第一首
}
this.setState({
currentIndex:index, //更新数据
})
this.loadSongInfo(index) //加载数据
}
//换歌时恢复进度条 和起始时间
recover = () =>{
this.setState({
sliderValue:0,
currentTime: 0.0
})
}
//播放模式 接收传过来的当前播放模式 this.state.playModel
playModel = (playModel) =>{
playModel++;
playModel = playModel == 4 ? 1 : playModel
//重新设置
this.setState({
playModel:playModel
})
//根据设置后的模式重新设置背景图片
if(playModel == 1){
this.setState({
btnModel:require('./image/列表循环.png'),
})
}else if(playModel == 2){
this.setState({
btnModel:require('./image/随机.png'),
})
}else{
this.setState({
btnModel:require('./image/单曲循环.png'),
})
}
}
//播放/暂停
playAction =() => {
this.setState({
pause: !this.state.pause
})
//判断按钮显示什么
if(this.state.pause == true){
this.setState({
isplayBtn:require('./image/播放.png')
})
}else {
this.setState({
isplayBtn:require('./image/暂停.png')
})
} }
//播放器每隔250ms调用一次
onProgress =(data) => {
let val = parseInt(data.currentTime)
this.setState({
sliderValue: val,
currentTime: data.currentTime
}) //如果当前歌曲播放完毕,需要开始下一首
if(val == this.state.file_duration){
if(this.state.playModel == 1){
//列表 就播放下一首
this.nextAction(this.state.currentIndex + 1)
}else if(this.state.playModel == 2){
let last = this.state.songs.length //json 中共有几首歌
let random = Math.floor(Math.random() * last) //取 0~last之间的随机整数
this.nextAction(random) //播放
}else{
//单曲 就再次播放当前这首歌曲
this.refs.video.seek(0) //让video 重新播放
_scrollView.scrollTo({x: 0,y:0,animated:false});
}
} }
//把秒数转换为时间类型
formatTime(time) {
// 71s -> 01:11
let min = Math.floor(time / 60)
let second = time - min * 60
min = min >= 10 ? min : '0' + min
second = second >= 10 ? second : '0' + second
return min + ':' + second
}
// 歌词
renderItem() {
// 数组
var itemAry = [];
for (var i = 0; i < lyrObj.length; i++) {
var item = lyrObj[i].txt
if (this.state.currentTime.toFixed(2) > lyrObj[i].total) {
//正在唱的歌词
itemAry.push(
<View key={i} style={styles.itemStyle}>
<Text style={{ color: 'blue' }}> {item} </Text>
</View>
);
_scrollView.scrollTo({x: 0,y:(25 * i),animated:false});
}
else {
//所有歌词
itemAry.push(
<View key={i} style={styles.itemStyle}>
<Text style={{ color: 'red' }}> {item} </Text>
</View>
)
}
} return itemAry;
}
// 播放器加载好时调用,其中有一些信息带过来
onLoad = (data) => {
this.setState({ duration: data.duration });
} loadSongInfo = (index) => {
//加载歌曲
let songid = this.state.songs[index]
let url = 'http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.song.play&songid=' + songid
fetch(url)
.then((response) => response.json())
.then((responseJson) => {
let songinfo = responseJson.songinfo
let bitrate = responseJson.bitrate
this.setState({
pic_small:songinfo.pic_small, //小图
pic_big:songinfo.pic_big, //大图
title:songinfo.title, //歌曲名
author:songinfo.author, //歌手
file_link:bitrate.file_link, //播放链接
file_duration:bitrate.file_duration //歌曲长度
}) //加载歌词
let url = 'http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.song.lry&songid=' + songid
fetch(url)
.then((response) => response.json())
.then((responseJson) => { let lry = responseJson.lrcContent
let lryAry = lry.split('\n') //按照换行符切数组
lryAry.forEach(function (val, index) {
var obj = {} //用于存放时间
val = val.replace(/(^\s*)|(\s*$)/g, '') //正则,去除前后空格
let indeofLastTime = val.indexOf(']') // ]的下标
let timeStr = val.substring(1, indeofLastTime) //把时间切出来 0:04.19
let minSec = ''
let timeMsIndex = timeStr.indexOf('.') // .的下标
if (timeMsIndex !== -1) {
//存在毫秒 0:04.19
minSec = timeStr.substring(1, val.indexOf('.')) // 0:04.
obj.ms = parseInt(timeStr.substring(timeMsIndex + 1, indeofLastTime)) //毫秒值 19
} else {
//不存在毫秒 0:04
minSec = timeStr
obj.ms = 0
}
let curTime = minSec.split(':') // [0,04]
obj.min = parseInt(curTime[0]) //分钟 0
obj.sec = parseInt(curTime[1]) //秒钟 04
obj.txt = val.substring(indeofLastTime + 1, val.length) //歌词文本: 留下唇印的嘴
obj.txt = obj.txt.replace(/(^\s*)|(\s*$)/g, '')
obj.dis = false
obj.total = obj.min * 60 + obj.sec + obj.ms / 100 //总时间
if (obj.txt.length > 0) {
lyrObj.push(obj)
}
})
}) })
} componentWillMount() {
//先从总列表中获取到song_id保存
fetch('http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.billboard.billList&type=2&size=10&offset=0')
.then((response) => response.json())
.then((responseJson) => {
var listAry = responseJson.song_list
var song_idAry = []; //保存song_id的数组
for(var i = 0;i<listAry.length;i++){
let song_id = listAry[i].song_id
song_idAry.push(song_id)
}
this.setState({
songs:song_idAry
})
this.loadSongInfo(0) //预先加载第一首
}) this.spin() // 启动旋转 } //旋转动画
spin () {
this.spinValue.setValue(0)
myAnimate = Animated.timing(
this.spinValue,
{
toValue: 1,
duration: 4000,
easing: Easing.linear
}
).start(() => this.spin()) } render() {
//如果未加载出来数据 就一直转菊花
if (this.state.file_link.length <= 0 ) {
return(
<ActivityIndicator
animating={this.state.animating}
style={{flex: 1,alignItems: 'center',justifyContent: 'center'}}
size="large" />
)
}else{
const spin = this.spinValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
}) //数据加载出来
return (
<View style={styles.container}>
{/*背景大图*/}
<Image source={{uri:this.state.pic_big}} style={{flex:1}}/>
{/*背景白色透明遮罩*/}
<View style = {{position:'absolute',width: width,height:height,backgroundColor:'white',opacity:0.8}}/> <View style = {{position:'absolute',width: width}}>
{/*胶片光盘*/}
<Image source={require('./image/胶片盘.png')} style={{width:220,height:220,alignSelf:'center'}}/> {/*旋转小图*/}
<Animated.Image
ref = 'myAnimate'
style={{width:140,height:140,marginTop: -180,alignSelf:'center',borderRadius: 140*0.5,transform: [{rotate: spin}]}}
source={{uri: this.state.pic_small}}
/> {/*播放器*/}
<Video
source={{uri: this.state.file_link}}
ref='video'
volume={1.0}
paused={this.state.pause}
onProgress={(e) => this.onProgress(e)}
onLoad={(e) => this.onLoad(e)}
/>
{/*歌曲信息*/}
<View style={styles.playingInfo}>
{/*作者-歌名*/}
<Text>{this.state.author} - {this.state.title}</Text>
{/*时间*/}
<Text>{this.formatTime(Math.floor(this.state.currentTime))} - {this.formatTime(Math.floor(this.state.duration))}</Text>
</View>
{/*播放模式*/}
<View style = {{marginTop: 5,marginBottom:5,marginLeft: 20}}>
<TouchableOpacity onPress={()=>this.playModel(this.state.playModel)}>
<Image source={this.state.btnModel} style={{width:20,height:20}}/>
</TouchableOpacity>
</View>
{/*进度条*/}
<Slider
ref='slider'
style={{ marginLeft: 10, marginRight: 10}}
value={this.state.sliderValue}
maximumValue={this.state.file_duration}
step={1}
minimumTrackTintColor='#FFDB42'
onValueChange={(value) => {
this.setState({
currentTime:value
})
}
}
onSlidingComplete={(value) => {
this.refs.video.seek(value)
}}
/>
{/*歌曲按钮*/}
<View style = {{flexDirection:'row',justifyContent:'space-around'}}>
<TouchableOpacity onPress={()=>this.prevAction(this.state.currentIndex - 1)}>
<Image source={require('./image/上一首.png')} style={{width:30,height:30}}/>
</TouchableOpacity> <TouchableOpacity onPress={()=>this.playAction()}>
<Image source={this.state.isplayBtn} style={{width:30,height:30}}/>
</TouchableOpacity> <TouchableOpacity onPress={()=>this.nextAction(this.state.currentIndex + 1)}>
<Image source={require('./image/下一首.png')} style={{width:30,height:30}}/>
</TouchableOpacity>
</View> {/*歌词*/}
<View style={{height:140,alignItems:'center'}}> <ScrollView style={{position:'relative'}}
ref={(scrollView) => { _scrollView = scrollView}}
>
{this.renderItem()}
</ScrollView>
</View>
</View> </View>
)
} }
} const styles = StyleSheet.create({
container: {
flex: 1,
},
image: {
flex: 1
},
playingControl: {
flexDirection: 'row',
alignItems: 'center',
paddingTop: 10,
paddingLeft: 20,
paddingRight: 20,
paddingBottom: 20
},
playingInfo: {
flexDirection: 'row',
alignItems:'stretch',
justifyContent: 'space-between',
paddingTop: 40,
paddingLeft: 20,
paddingRight: 20,
backgroundColor:'rgba(255,255,255,0.0)'
},
text: {
color: "black",
fontSize: 22
},
modal: {
height: 300,
borderTopLeftRadius: 5,
borderTopRightRadius: 5,
paddingTop: 5,
paddingBottom: 50
},
itemStyle: {
paddingTop: 20,
height:25,
backgroundColor:'rgba(255,255,255,0.0)'
}
})
github地址: https://github.com/pheromone/react-native-videoDemo
使用react native制作的一款网络音乐播放器的更多相关文章
- swift3.0 简单直播和简单网络音乐播放器
本项目采用swift3.0所写,适配iOS9.0+,所有界面均采用代码布局. 第一个tab写的是简单直播,传统MVC模式,第二个tab写的是简单网络音乐播放器.传说MVVM模式(至于血统是否纯正我就不 ...
- 我在 Gitee 上发现了一个简洁又好用的网络音乐播放器!
这几天无聊的时候我想听听歌,但我想要找一个简单快速的网络音乐播放器来用用.这时我在 Gitee 上看见一个看上去不错的开源项目 -- Hi音乐. 项目链接:https://gitee.com/hi-j ...
- 22_Android中的本地音乐播放器和网络音乐播放器的编写,本地视频播放器和网络视频播放器,照相机案例,偷拍案例实现
1 编写以下案例: 当点击了"播放"之后,在手机上的/mnt/sdcard2/natural.mp3就会播放. 2 编写布局文件activity_main.xml <Line ...
- Android实现网络音乐播放器
本文是一个简单的音乐播放器 布局代码 <?xml version="1.0" encoding="utf-8"?> <RelativeLayo ...
- 解决React Native使用Fetch API请求网络报Network request failed
问题来源: 1 . 在测试fetch数据请求时,Xcode9.0以上的无法请求https, 需要在Xcode中加载项目后修改Info.plist的相关配置,具体如下参考 问题及解决方法一模一样,不再重 ...
- 使用 原生js 制作插件 (javaScript音乐播放器)
1.引用页面 index.html <!DOCTYPE html> <html lang="en"> <head> <meta chars ...
- Swift - 制作一个在线流媒体音乐播放器(使用StreamingKit库)
在之前的文章中,我介绍了如何使用 AVPlayer 制作一个简单的音乐播放器(点击查看1.点击查看2).虽然这个播放器也可以播放网络音频,但其实际上是将音频文件下载到本地后再播放的. 本文演示如何使用 ...
- 【.NET 与树莓派】用 MPD 制作数字音乐播放器
树莓派的日常家居玩法多多,制作一台属于自己的数字音乐播放机是其中的一种.严格上说,树莓派是没有声卡的,其板载的 3.5 mm 音频孔实际是通过 PWM 来实现音频输出的(通过算法让PWM信号变成模拟信 ...
- [MFC] MFC音乐播放器 傻瓜级教程 网络 搜索歌曲 下载
>目录< >——————————————————————< 1.建立工程 1.建立一个MFC工程,命名为Tao_Music 2.选择为基本对话框 3.包含Windows So ...
随机推荐
- 【2017-02-24】循环嵌套、跳转语句、异常语句、迭代穷举、while
一.循环嵌套 1.格式 for() { for() { } } 2.执行顺序 先执行外边循环进到循环体发现里面的循环,开始执行里面的循环.等到里面的循环执行完毕,再执行外边的循环. 在外面循环第一次, ...
- MyBatis 的小细节问题
mybatis Result Maps collection already contains value 这是个小功能,我当时没有怎么在意,后来发到了测试环境的时候测试提出了bug,我才慌忙查看原因 ...
- Vuex 学习总结
好在之前接触过 flux,对于理解 vuex 还是很有帮助的.react 学到一半,后来因为太忙,就放弃了,现在也差不多都忘记了.不过感觉 vuex 还是跟 flux 还是有点区别的. 对于很多新手来 ...
- Java IO之File和IO
本系列我们主要总结一下Java中的IO.NIO以及NIO2. java.io.File 学习Java IO,首先让我们来了解File类吧,它是文件和目录路径名的抽象表示形式.因此你千万别误会File类 ...
- lucene倒排索引缓冲池的细节
倒排索引要存哪些信息 提到倒排索引,第一感觉是词到文档列表的映射,实际上,倒排索引需要存储的信息不止词和文档列表.为了使用余弦相似度计算搜索词和文档的相似度,需要计算文档中每个词的TF-IDF值, ...
- 【JS】JavaScript中的闭包
在JavaScript中,闭包指的是有权访问另一个函数作用域中的变量的函数:创建闭包最常见的方式就是在一个函数内创建另一个函数.如下例子: function A(propertyName){ retu ...
- 使用php ajax写省、市、区、三级联动
题目要求: 要求:写一个省市区(或者年月日)的三级联动,实现地区或时间的下拉选择. 实现技术:php ajax 实现:省级下拉变化时市下拉区下拉跟着变化,市级下拉变化时区下拉跟着变化. 使用china ...
- 3298: [USACO 2011Open]cow checkers
3298: [USACO 2011Open]cow checkers Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 65 Solved: 26[Su ...
- 九度OJ题目1080:进制转换(java)使用BigInteger进行进制转换
题目描述: 将M进制的数X转换为N进制的数输出. 输入: 输入的第一行包括两个整数:M和N(2<=M,N<=36). 下面的一行输入一个数X,X是M进制的数,现在要求你将M进制的数X转换成 ...
- Django ORM模型的一点体会
作者:Vamei 出处:http://www.cnblogs.com/vamei 严禁转载. 使用Python的Django模型的话,一般都会用它自带的ORM(Object-relational ma ...