今天實作歌曲播放時,歌詞随着滾動的效果
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2YfNWawNCM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPR1UMBR1T00EVOBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLxEDN0MjNwUTM4EDOwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
網易雲原本的歌詞是這樣的
[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上下載下傳完整代碼,做了很多功能
github項目位址:https://github.com/lsh555/WYY-Music