|
@@ -1,4 +1,4 @@
|
|
|
-import { duration2text, printW } from '../extensions/Extension';
|
|
|
|
|
|
|
+import { duration2text } from '../extensions/Extension';
|
|
|
import { LyricController } from '../LyricController';
|
|
import { LyricController } from '../LyricController';
|
|
|
import { Lyric } from '../bean/Lyric';
|
|
import { Lyric } from '../bean/Lyric';
|
|
|
import { ListAdapter } from '../extensions/ListAdapter';
|
|
import { ListAdapter } from '../extensions/ListAdapter';
|
|
@@ -78,6 +78,13 @@ export struct LyricView2 {
|
|
|
private readonly positionUpdateThrottleMs: number = 33
|
|
private readonly positionUpdateThrottleMs: number = 33
|
|
|
private readonly minPositionDeltaMs: number = 8
|
|
private readonly minPositionDeltaMs: number = 8
|
|
|
private readonly karaokeTransitionWidth: number = 0.12
|
|
private readonly karaokeTransitionWidth: number = 0.12
|
|
|
|
|
+ @State highlightFontProgress: number = 1
|
|
|
|
|
+ @State highlightFontActiveIndex: number = -1
|
|
|
|
|
+ private highlightFontToken: number = 0
|
|
|
|
|
+ private highlightFontStartTimeout = -1
|
|
|
|
|
+ private readonly highlightFontStartDelayMs: number = 100
|
|
|
|
|
+ private readonly highlightFontDurationMs: number = 180
|
|
|
|
|
+ private readonly areaReloadThresholdPx: number = 1
|
|
|
|
|
|
|
|
private onDataChangedListener = (lyric: Lyric | null) => {
|
|
private onDataChangedListener = (lyric: Lyric | null) => {
|
|
|
clearTimeout(this.loadTimeout)
|
|
clearTimeout(this.loadTimeout)
|
|
@@ -112,6 +119,10 @@ export struct LyricView2 {
|
|
|
|
|
|
|
|
private loadData(lyric: Lyric | null) {
|
|
private loadData(lyric: Lyric | null) {
|
|
|
this.currentLyric = lyric;
|
|
this.currentLyric = lyric;
|
|
|
|
|
+ clearTimeout(this.highlightFontStartTimeout)
|
|
|
|
|
+ this.highlightFontToken += 1
|
|
|
|
|
+ this.highlightFontActiveIndex = -1
|
|
|
|
|
+ this.highlightFontProgress = 1
|
|
|
if (this.w > 0 && this.h > 0) {
|
|
if (this.w > 0 && this.h > 0) {
|
|
|
if (this.currentLyric) {
|
|
if (this.currentLyric) {
|
|
|
this.listAdapter.clear(false);
|
|
this.listAdapter.clear(false);
|
|
@@ -332,8 +343,7 @@ export struct LyricView2 {
|
|
|
NormalLyricLine(item: LyricLine, index: number) {
|
|
NormalLyricLine(item: LyricLine, index: number) {
|
|
|
Column(){
|
|
Column(){
|
|
|
Text(item.text)
|
|
Text(item.text)
|
|
|
- .fontSize(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ?
|
|
|
|
|
- this.textHighlightSize : this.textSize)
|
|
|
|
|
|
|
+ .fontSize(this.getAnimatedLyricFontSize(index))
|
|
|
.textAlign(this.alignMode == 'center' ? TextAlign.Center : TextAlign.Start)
|
|
.textAlign(this.alignMode == 'center' ? TextAlign.Center : TextAlign.Start)
|
|
|
.fontColor(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ? this.textHighlightColor : this.textColor)
|
|
.fontColor(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ? this.textHighlightColor : this.textColor)
|
|
|
.fontWeight(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) && this.isHighlightBold ? FontWeight.Bold : this.textWeight)
|
|
.fontWeight(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) && this.isHighlightBold ? FontWeight.Bold : this.textWeight)
|
|
@@ -351,8 +361,7 @@ export struct LyricView2 {
|
|
|
if (item.translation) {
|
|
if (item.translation) {
|
|
|
|
|
|
|
|
Text(item.translation)
|
|
Text(item.translation)
|
|
|
- .fontSize(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ?
|
|
|
|
|
- this.textHighlightSize : this.textSize)
|
|
|
|
|
|
|
+ .fontSize(this.getAnimatedLyricFontSize(index))
|
|
|
.fontColor(this.currentMediaPosition >= item.beginTime ?
|
|
.fontColor(this.currentMediaPosition >= item.beginTime ?
|
|
|
index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ? this.textHighlightColor : this.textColor : this.textColor)
|
|
index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ? this.textHighlightColor : this.textColor : this.textColor)
|
|
|
.fontWeight(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) && this.isHighlightBold ? FontWeight.Bold : this.textWeight)
|
|
.fontWeight(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) && this.isHighlightBold ? FontWeight.Bold : this.textWeight)
|
|
@@ -506,11 +515,10 @@ export struct LyricView2 {
|
|
|
@Builder
|
|
@Builder
|
|
|
WordByWordLyric(item: LyricLine, index: number) {
|
|
WordByWordLyric(item: LyricLine, index: number) {
|
|
|
Column() {
|
|
Column() {
|
|
|
- Row({ space: 0 }) {
|
|
|
|
|
|
|
+ Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap, justifyContent: this.alignMode === 'center' ? FlexAlign.Center : FlexAlign.Start }) {
|
|
|
ForEach(item.words, (word: LyricWord, wordIndex: number) => {
|
|
ForEach(item.words, (word: LyricWord, wordIndex: number) => {
|
|
|
Text(word.word)
|
|
Text(word.word)
|
|
|
- .fontSize(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ?
|
|
|
|
|
- this.textHighlightSize : this.textSize)
|
|
|
|
|
|
|
+ .fontSize(this.getAnimatedLyricFontSize(index))
|
|
|
.fontColor(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) &&
|
|
.fontColor(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) &&
|
|
|
this.getWordPlaybackState(word) == 2 ? this.textHighlightColor : this.textColor)
|
|
this.getWordPlaybackState(word) == 2 ? this.textHighlightColor : this.textColor)
|
|
|
.fontWeight(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ?
|
|
.fontWeight(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ?
|
|
@@ -527,20 +535,20 @@ export struct LyricView2 {
|
|
|
}:undefined)
|
|
}:undefined)
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
- .justifyContent(this.alignMode === 'center' ? FlexAlign.Center : FlexAlign.Start)
|
|
|
|
|
.width(this.alignMode == 'center' ? '100%' :index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ?'95%': '85%')
|
|
.width(this.alignMode == 'center' ? '100%' :index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ?'95%': '85%')
|
|
|
|
|
|
|
|
// 中文翻译(整行显示)
|
|
// 中文翻译(整行显示)
|
|
|
if (item.translation) {
|
|
if (item.translation) {
|
|
|
Row({ space: 0 }) {
|
|
Row({ space: 0 }) {
|
|
|
Text(item.translation)
|
|
Text(item.translation)
|
|
|
- .fontSize(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ?
|
|
|
|
|
- this.textHighlightSize : this.textSize)
|
|
|
|
|
|
|
+ .fontSize(this.getAnimatedLyricFontSize(index))
|
|
|
.fontColor(this.currentMediaPosition >= item.beginTime ?
|
|
.fontColor(this.currentMediaPosition >= item.beginTime ?
|
|
|
index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ? this.textHighlightColor : this.textColor : this.textColor)
|
|
index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText) ? this.textHighlightColor : this.textColor : this.textColor)
|
|
|
.fontWeight(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText)? FontWeight.Bold : this.textWeight)
|
|
.fontWeight(index == this.currentIndex && !(this.currentLyric && this.currentLyric.isPlainText)? FontWeight.Bold : this.textWeight)
|
|
|
.padding({ bottom:5 })
|
|
.padding({ bottom:5 })
|
|
|
.padding(this.isSingleLine?{ bottom:2 }:{ bottom:5 })
|
|
.padding(this.isSingleLine?{ bottom:2 }:{ bottom:5 })
|
|
|
|
|
+ .textAlign(this.alignMode == 'center' ? TextAlign.Center : TextAlign.Start)
|
|
|
|
|
+ .width('100%')
|
|
|
.visibility(this.isSingleLine?
|
|
.visibility(this.isSingleLine?
|
|
|
(index >= this.currentIndex && index <= this.currentIndex + 1 ? Visibility.Visible : Visibility.None)
|
|
(index >= this.currentIndex && index <= this.currentIndex + 1 ? Visibility.Visible : Visibility.None)
|
|
|
: Visibility.Visible)
|
|
: Visibility.Visible)
|
|
@@ -680,11 +688,22 @@ export struct LyricView2 {
|
|
|
.width('100%')
|
|
.width('100%')
|
|
|
.height('100%')
|
|
.height('100%')
|
|
|
.onAreaChange((_, newSize) => {
|
|
.onAreaChange((_, newSize) => {
|
|
|
- printW('onSizeChanged: ' + JSON.stringify(newSize))
|
|
|
|
|
- this.h = newSize.height as number
|
|
|
|
|
- this.w = newSize.width as number
|
|
|
|
|
|
|
+ const nextHeight = Number(newSize.height) || 0
|
|
|
|
|
+ const nextWidth = Number(newSize.width) || 0
|
|
|
|
|
+ const isFirstMeasure = this.h <= 0 || this.w <= 0
|
|
|
|
|
+ const isSizeChanged = Math.abs(nextHeight - this.h) >= this.areaReloadThresholdPx ||
|
|
|
|
|
+ Math.abs(nextWidth - this.w) >= this.areaReloadThresholdPx
|
|
|
|
|
+ if (!isFirstMeasure && !isSizeChanged) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ this.h = nextHeight
|
|
|
|
|
+ this.w = nextWidth
|
|
|
if (this.currentLyric) {
|
|
if (this.currentLyric) {
|
|
|
- this.loadData(this.currentLyric)
|
|
|
|
|
|
|
+ if (this.listAdapter.isEmpty()) {
|
|
|
|
|
+ this.loadData(this.currentLyric)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.scrollToIndexImmediately(this.currentIndex)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
@@ -692,6 +711,7 @@ export struct LyricView2 {
|
|
|
aboutToDisappear() {
|
|
aboutToDisappear() {
|
|
|
clearTimeout(this.loadTimeout)
|
|
clearTimeout(this.loadTimeout)
|
|
|
clearTimeout(this.seekUiHideTimeout)
|
|
clearTimeout(this.seekUiHideTimeout)
|
|
|
|
|
+ clearTimeout(this.highlightFontStartTimeout)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private getIndex(position: number): number {
|
|
private getIndex(position: number): number {
|
|
@@ -736,10 +756,12 @@ export struct LyricView2 {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
const safeIndex = Math.max(0, Math.min(count - 1, index))
|
|
const safeIndex = Math.max(0, Math.min(count - 1, index))
|
|
|
|
|
+ const previousIndex = this.currentIndex
|
|
|
this.currentIndex = safeIndex
|
|
this.currentIndex = safeIndex
|
|
|
if (this.isUserTouching) {
|
|
if (this.isUserTouching) {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
+ this.playHighlightFontAnimation(previousIndex, safeIndex)
|
|
|
if(this.isHightLightCenter){
|
|
if(this.isHightLightCenter){
|
|
|
this.scroller.scrollToIndex(safeIndex, true, ScrollAlign.CENTER)
|
|
this.scroller.scrollToIndex(safeIndex, true, ScrollAlign.CENTER)
|
|
|
}else{
|
|
}else{
|
|
@@ -751,6 +773,53 @@ export struct LyricView2 {
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private shouldApplyHighlightFontAnimation(index: number): boolean {
|
|
|
|
|
+ return index === this.currentIndex
|
|
|
|
|
+ && index === this.highlightFontActiveIndex
|
|
|
|
|
+ && !(this.currentLyric && this.currentLyric.isPlainText)
|
|
|
|
|
+ && !this.isUserTouching
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private getAnimatedLyricFontSize(index: number): number {
|
|
|
|
|
+ if (!(index === this.currentIndex) || (this.currentLyric && this.currentLyric.isPlainText)) {
|
|
|
|
|
+ return this.textSize
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!this.shouldApplyHighlightFontAnimation(index)) {
|
|
|
|
|
+ return this.textHighlightSize
|
|
|
|
|
+ }
|
|
|
|
|
+ const progress = Math.max(0, Math.min(1, this.highlightFontProgress))
|
|
|
|
|
+ return this.textSize + (this.textHighlightSize - this.textSize) * progress
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private playHighlightFontAnimation(previousIndex: number, nextIndex: number): void {
|
|
|
|
|
+ if (previousIndex === nextIndex) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.currentLyric && this.currentLyric.isPlainText) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ clearTimeout(this.highlightFontStartTimeout)
|
|
|
|
|
+ this.highlightFontToken += 1
|
|
|
|
|
+ const token = this.highlightFontToken
|
|
|
|
|
+ this.highlightFontActiveIndex = nextIndex
|
|
|
|
|
+ this.highlightFontProgress = 0
|
|
|
|
|
+ this.highlightFontStartTimeout = setTimeout(() => {
|
|
|
|
|
+ if (token !== this.highlightFontToken) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ animateTo({
|
|
|
|
|
+ duration: this.highlightFontDurationMs,
|
|
|
|
|
+ curve: Curve.EaseOut
|
|
|
|
|
+ }, () => {
|
|
|
|
|
+ if (token !== this.highlightFontToken) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ this.highlightFontProgress = 1
|
|
|
|
|
+ this.highlightFontActiveIndex = -1
|
|
|
|
|
+ })
|
|
|
|
|
+ }, this.highlightFontStartDelayMs)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
|
|
|
private onPositionChanged(mediaPosition: number) {
|
|
private onPositionChanged(mediaPosition: number) {
|
|
|
if (this.isLyricEmpty) {
|
|
if (this.isLyricEmpty) {
|