| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- import { CommonConstants } from '../common/constants/CommonConstants'
- import { normalizeHexColor, rgbaFromHex } from './spectrum/SpectrumRenderer'
- //正在播放的动画指示器
- @Component
- export struct PlayingIndicator {
- @Prop @Watch('onActiveChange') isActive: boolean = false
- @Prop indicatorSize: number = 18
- @Prop indicatorColor: string = ''
- @Prop marginRight: number = 0
- @Prop marginTop: number = 0
- @Prop marginBottom: number = 0
- @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR
- private readonly renderSettings: RenderingContextSettings = new RenderingContextSettings(true)
- private readonly canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.renderSettings)
- private timerId: number = -1
- private phase: number = 0
- private canvasWidth: number = 0
- private canvasHeight: number = 0
- aboutToAppear(): void {
- this.updateAnimationState()
- this.drawFrame()
- }
- aboutToDisappear(): void {
- this.stopTimer()
- }
- onActiveChange(): void {
- this.updateAnimationState()
- this.drawFrame()
- }
- private updateAnimationState(): void {
- if (this.isActive) {
- this.startTimer()
- return
- }
- this.stopTimer()
- this.phase = 0
- }
- private startTimer(): void {
- if (this.timerId !== -1) {
- return
- }
- this.timerId = setInterval(() => {
- this.phase += 0.58
- if (this.phase > Math.PI * 2) {
- this.phase -= Math.PI * 2
- }
- this.drawFrame()
- }, 88)
- }
- private stopTimer(): void {
- if (this.timerId === -1) {
- return
- }
- clearInterval(this.timerId)
- this.timerId = -1
- }
- private drawFrame(): void {
- if (this.canvasWidth <= 0 || this.canvasHeight <= 0) {
- return
- }
- const ctx: CanvasRenderingContext2D = this.canvasContext
- ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
- if (!this.isActive) {
- return
- }
- const colorSource: string = this.indicatorColor.length > 0 ? this.indicatorColor : this.themeColor
- const brandColor: string = normalizeHexColor(colorSource, CommonConstants.DEFAULT_THEME_COLOR)
- const barCount: number = 5
- const barWidth: number = Math.max(1.0, Math.min(1.8, this.canvasWidth * 0.075))
- const barGap: number = Math.max(1.0, Math.min(1.9, this.canvasWidth * 0.092))
- const totalWidth: number = barWidth * barCount + barGap * (barCount - 1)
- const startX: number = (this.canvasWidth - totalWidth) / 2
- const baseY: number = this.canvasHeight
- const minBarHeight: number = this.canvasHeight * 0.28
- const maxBarHeight: number = this.canvasHeight * 0.84
- for (let i = 0; i < barCount; i++) {
- const primaryWave: number = 0.5 + 0.5 * Math.sin(this.phase + i * 0.82)
- const secondaryWave: number = 0.5 + 0.5 * Math.sin(this.phase * 0.65 + i * 1.24 + 1.1)
- const level: number = 0.18 + 0.82 * (primaryWave * 0.72 + secondaryWave * 0.28)
- const barHeight: number = minBarHeight + (maxBarHeight - minBarHeight) * level
- const x: number = startX + i * (barWidth + barGap)
- const y: number = baseY - barHeight
- const topColor: string = rgbaFromHex(brandColor, 0.96)
- const bottomColor: string = rgbaFromHex(brandColor, 0.62)
- const glowColor: string = rgbaFromHex(brandColor, 0.12)
- const barGradient: CanvasGradient = ctx.createLinearGradient(x, y, x, baseY)
- barGradient.addColorStop(0, topColor)
- barGradient.addColorStop(1, bottomColor)
- ctx.fillStyle = glowColor
- this.fillRoundedRect(ctx, x - 0.35, y - 0.35, barWidth + 0.7, barHeight + 0.7, barWidth * 0.50)
- ctx.fillStyle = barGradient
- this.fillRoundedRect(ctx, x, y, barWidth, barHeight, barWidth * 0.50)
- }
- }
- private fillRoundedRect(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number,
- radius: number): void {
- const safeRadius: number = Math.min(radius, width * 0.5, height * 0.5)
- ctx.beginPath()
- ctx.moveTo(x + safeRadius, y)
- ctx.lineTo(x + width - safeRadius, y)
- ctx.quadraticCurveTo(x + width, y, x + width, y + safeRadius)
- ctx.lineTo(x + width, y + height - safeRadius)
- ctx.quadraticCurveTo(x + width, y + height, x + width - safeRadius, y + height)
- ctx.lineTo(x + safeRadius, y + height)
- ctx.quadraticCurveTo(x, y + height, x, y + height - safeRadius)
- ctx.lineTo(x, y + safeRadius)
- ctx.quadraticCurveTo(x, y, x + safeRadius, y)
- ctx.closePath()
- ctx.fill()
- }
- build() {
- Row() {
- Canvas(this.canvasContext)
- .width(this.indicatorSize)
- .height(this.indicatorSize)
- .onReady(() => {
- this.canvasContext.imageSmoothingEnabled = true
- this.canvasContext.imageSmoothingQuality = 'medium'
- this.drawFrame()
- })
- .onAreaChange((_oldVal: Area, newVal: Area) => {
- this.canvasWidth = newVal.width as number
- this.canvasHeight = newVal.height as number
- this.drawFrame()
- })
- }
- .width(this.indicatorSize)
- .height(this.indicatorSize)
- .margin({ right: this.marginRight, top: this.marginTop, bottom: this.marginBottom })
- .visibility(this.isActive ? Visibility.Visible : Visibility.None)
- .onAppear(() => {
- this.updateAnimationState()
- this.drawFrame()
- })
- .onDisAppear(() => {
- this.stopTimer()
- })
- }
- }
|