-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
264 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import React, { useImperativeHandle, forwardRef, useEffect, useRef } from "react" | ||
|
||
let activeIndex = -1 | ||
let an: Animation | null = null | ||
export default forwardRef<LyricRef, LrcComponentProps>((props, ref) => { | ||
useImperativeHandle(ref, () => ({ | ||
onUpdateTime | ||
})) | ||
|
||
const lyricEl = useRef<HTMLDivElement>(null) | ||
|
||
const onUpdateTime = (val: number) => { | ||
if (~activeIndex && props.lrcContent[activeIndex] && props.lrcContent[activeIndex].start <= val && props.lrcContent[activeIndex].end >= val) { | ||
// do nothing | ||
} else { | ||
activeIndex = -1 | ||
for(let i = 0; i < props.lrcContent.length; i++) { | ||
if (val >= props.lrcContent[i].start && val <= props.lrcContent[i].end) { | ||
activeIndex = i | ||
break | ||
} | ||
} | ||
if (~activeIndex) { | ||
const target = props.lrcContent[activeIndex] | ||
console.log('need run animation', activeIndex, target.text, target.end - target.start) | ||
runLrcAnimation(target.text, target.end - target.start) | ||
} else { | ||
runLrcAnimation('', 0) | ||
} | ||
} | ||
} | ||
|
||
function runLrcAnimation(text: string, duration: number) { | ||
if (!lyricEl.current) { | ||
return | ||
} | ||
lyricEl.current.innerHTML = text | ||
an = lyricEl.current.animate([ | ||
{ 'backgroundSize': '0 100%' }, | ||
{ 'backgroundSize': '100% 100%' } | ||
], { | ||
duration: duration * 1000 | ||
}) | ||
} | ||
|
||
useEffect(() => { | ||
runLrcAnimation('', 0) | ||
an?.pause() | ||
}, [props.lrcContent]) | ||
|
||
useEffect(() => { | ||
if (props.isPlaying) { | ||
an?.play() | ||
} else { | ||
an?.pause() | ||
} | ||
}, [props.isPlaying]) | ||
|
||
return <div ref={lyricEl} className="lyric-content"></div> | ||
}) | ||
|
||
export type LyricRef = { | ||
onUpdateTime: (val: number) => void | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// https://github.com/AdoneZ/lrc-parser/blob/master/index.ts | ||
const EOL = '\n' | ||
|
||
/** | ||
* | ||
* @param {string} data | ||
* @example [length: 03:36] | ||
* @return {<Array>{string}} ['length', '03:06'] | ||
*/ | ||
|
||
function extractInfo(data: string) { | ||
const info = data.trim().slice(1, -1) // remove brackets: length: 03:06 | ||
const firstColonIndex = info.indexOf(':') | ||
|
||
return [ | ||
info.substring(0, firstColonIndex).trim(), | ||
info.substring(firstColonIndex + 1).trim() | ||
] | ||
} | ||
|
||
function lrcParser(data: string) { | ||
if (typeof data !== 'string') { | ||
throw new TypeError('expect first argument to be a string') | ||
} | ||
// split a long string into lines by system's end-of-line marker line \r\n on Windows | ||
// or \n on POSIX | ||
let lines = data.split(EOL) | ||
const timeStart = /\[(\d*\:\d*\.?\d*)\]/ // i.g [00:10.55] | ||
const scriptText = /(.+)/ // Havana ooh na-na (ayy) | ||
const timeEnd = timeStart | ||
const startAndText = new RegExp(timeStart.source + scriptText.source) | ||
|
||
const infos:string[] = [] | ||
const scripts: ScriptItem[] = [] | ||
const result: LrcJsonData = {} | ||
|
||
for(let i = 0; startAndText.test(lines[i]) === false; i++) { | ||
infos.push(lines[i]) | ||
} | ||
|
||
infos.reduce((result, info) => { | ||
const [key, value] = extractInfo(info) | ||
result[key] = value | ||
return result | ||
}, result) | ||
|
||
lines.splice(0, infos.length) // remove all info lines | ||
const qualified = new RegExp(startAndText.source + '|' + timeEnd.source) | ||
lines = lines.filter(line => qualified.test(line)) | ||
|
||
for (let i = 0, l = lines.length; i < l; i++) { | ||
const matches = startAndText.exec(lines[i]) | ||
const timeEndMatches = timeEnd.exec(lines[i + 1]) | ||
if (matches && timeEndMatches) { | ||
const [, start, text] = matches | ||
|
||
let _text = text | ||
let translateText = '' | ||
const tMatch = text.match(/.*\((.*)\)/) | ||
if (tMatch && tMatch.length > 1) { | ||
translateText = tMatch[1] | ||
_text = text.replace(`(${translateText})`, '') | ||
} | ||
|
||
const [, end] = timeEndMatches | ||
scripts.push({ | ||
start: convertTime(start), | ||
text: _text, | ||
translateText, | ||
end: convertTime(end), | ||
}) | ||
} | ||
} | ||
|
||
result.scripts = scripts | ||
return result | ||
} | ||
|
||
// convert time string to seconds | ||
// i.g: [01:09.10] -> 69.10 | ||
function convertTime(string: string) { | ||
const _string = string.split(':'); | ||
const minutes = parseInt(_string[0], 10) | ||
const seconds = parseFloat(_string[1]) | ||
if (minutes > 0) { | ||
const sc = minutes * 60 + seconds | ||
return parseFloat(sc.toFixed(2)) | ||
} | ||
return seconds | ||
} | ||
|
||
export default lrcParser |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
type ScriptItem = {start: number, text: string, translateText: string, end: number} | ||
|
||
interface LrcJsonData { | ||
[key: string]: any | ||
scripts?: ScriptItem[] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters