今天实现歌曲播放时,歌词随着滚动的效果
网易云原本的歌词是这样的
[00:00.000] 作曲 : 许嵩
[00:01.000] 作词 : 许嵩
[00:22.240]天空好想下雨
[00:24.380]我好想住你隔壁
[00:26.810]傻站在你家楼下
[00:29.500]抬起头数乌云
[00:31.160]如果场景里出现一架钢琴
[00:33.640]我会唱歌给你听
[00:35.900]哪怕好多盆水往下淋
[00:41.060]夏天快要过去}
[00:31.160]如果场景里出现一架钢琴
我们得把这个数据处理一下,转换成这样子的
在utils里面创建一个lrc-parse.js
const parseExp = /\[(\d{2}):(\d{2})\.(\d{2,3})\]/
export function parseLyric(lyricString) {
const lineStrings = lyricString.split("\n");
const lyrics = [];
for (let line of lineStrings) {
if (line) {
const lrcContent = line.replace(parseExp, '').trim();
const timeResult = parseExp.exec(line);
const milliseconds = timeResult[3].length === 3 ? timeResult[3] * 1: timeResult[3]*10
const lrcTime = timeResult[1] * 60 * 1000 + timeResult[2] * 1000 + milliseconds;
lyrics.push({
content: lrcContent,
time: lrcTime
})
}
}
console.log(lyrics)
return lyrics;
}
通过调用这个parseLyric函数,把我们的歌词数组放进去即可
好了,现在开始准备写了
一.先把网络请求部分写好
player.js
export function getLyric(id) {
return request({
url: "/lyric",
params: {
id
}
})
}
二.把我们请求的数据放在redux中
先定义常量constants.js
export const CHANGE_CURRENT_LYRIC_INDEX = "player/CHANGE_CURRENT_LYRIC_INDEX";
然后去定义我们的state,reducer.js
import { Map } from 'immutable';
import * as actionTypes from './constants';
const defaultState = Map({
currentLyrics: [],
currentLyricIndex: -1
});
function reducer(state = defaultState, action) {
switch (action.type) {
case actionTypes.CHANGE_LYRICS:
return state.set("currentLyrics", action.lyrics);
case actionTypes.CHANGE_CURRENT_LYRIC_INDEX:
return state.set("currentLyricIndex", action.index);
default:
return state;
}
}
export default reducer;
再去actionCreators.js中封装方法
import * as actionTypes from './constants';
import { getLyric} from '@/services/player';
export const changeCurrentLyricIndexAction = (index) => ({
type: actionTypes.CHANGE_CURRENT_LYRIC_INDEX,
index
})
const changLyricListAction = (lyricList) => ({
type: actionTypes.CHANGE_LYRIC_LIST,
lyricList
})
export const getLyric = (id) => {
return dispatch => {
getLyric(id).then(res => {
const lyric = res.lrc.lyric;
const lyricList = parseLyric(lyric);
dispatch(changLyricListAction(lyricList));
})
}
}
之后就可以去组件中使用redux中的数据了
index.js
还是分三步
第一步,导入配置
import React, { memo, useRef, useEffect } from 'react';
import { useSelector, shallowEqual } from 'react-redux';
import classNames from 'classnames';
import { scrollTo } from "@/utils/ui-helper";
import { PannelWrapper } from './style';
scrollTo是我们封装的一个滚动js,用于歌词的滚动
export function scrollTo(element, to, duration) {
if (duration <= 0) return;
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function() {
element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop === to) return;
scrollTo(element, to, duration - 10);
}, 10);
}
第二步,逻辑代码
const { currentLyrics, currentLyricIndex } = useSelector(state => ({
currentLyrics: state.getIn(["player", "currentLyrics"]),
currentLyricIndex: state.getIn(["player", "currentLyricIndex"])
}), shallowEqual);
// other hooks
const panelRef = useRef();
useEffect(() => {
if (currentLyricIndex > 0 && currentLyricIndex < 3) return;
scrollTo(panelRef.current, (currentLyricIndex - 3) * 32, 300)
}, [currentLyricIndex]);
第三步,布局
return (
<PannelWrapper ref={panelRef}>
<div className="lrc-content">
{
currentLyrics.map((item, index) => {
return (
<div key={item.time}
className={classNames("lrc-item", { "active": index === currentLyricIndex })}>
{item.content}
</div>
)
})
}
</div>
</PannelWrapper>
)
完事了,over
如果有不懂的,可以去我的github上下载完整代码,做了很多功能