mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-09-18 15:32:35 +00:00
Further fixes to TargetTApLoaderView.
This commit is contained in:
parent
cc247ce634
commit
83f520ca44
1 changed files with 82 additions and 24 deletions
|
@ -3,6 +3,7 @@ package com.futo.platformplayer.views
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.*
|
import android.graphics.*
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import android.util.TypedValue
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -19,6 +20,9 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
private var startTime: Long = 0L
|
private var startTime: Long = 0L
|
||||||
private var loaderFinished = false
|
private var loaderFinished = false
|
||||||
private var forceIndeterminate = false
|
private var forceIndeterminate = false
|
||||||
|
private var spinnerShader: SweepGradient? = null
|
||||||
|
private var lastFrameTime = System.currentTimeMillis()
|
||||||
|
private val bounceInterpolator = android.view.animation.OvershootInterpolator(2f)
|
||||||
|
|
||||||
private val isIndeterminate: Boolean
|
private val isIndeterminate: Boolean
|
||||||
get() = forceIndeterminate || expectedDurationMs == null || expectedDurationMs == 0L
|
get() = forceIndeterminate || expectedDurationMs == null || expectedDurationMs == 0L
|
||||||
|
@ -29,7 +33,9 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
|
|
||||||
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
color = Color.WHITE
|
color = Color.WHITE
|
||||||
textSize = 48f
|
textSize = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_SP, 18f, resources.displayMetrics
|
||||||
|
)
|
||||||
textAlign = Paint.Align.CENTER
|
textAlign = Paint.Align.CENTER
|
||||||
setShadowLayer(4f, 0f, 0f, Color.BLACK)
|
setShadowLayer(4f, 0f, 0f, Color.BLACK)
|
||||||
typeface = Typeface.DEFAULT_BOLD
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
|
@ -58,6 +64,10 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
private val shadowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
private val shadowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
color = Color.argb(50, 0, 0, 0)
|
color = Color.argb(50, 0, 0, 0)
|
||||||
}
|
}
|
||||||
|
private val glowPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||||
|
private val particlePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.YELLOW
|
||||||
|
}
|
||||||
private val backgroundPaint = Paint()
|
private val backgroundPaint = Paint()
|
||||||
|
|
||||||
private var spinnerAngle = 0f
|
private var spinnerAngle = 0f
|
||||||
|
@ -79,7 +89,17 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startLoader(durationMs: Long? = null) {
|
fun startLoader(durationMs: Long? = null) {
|
||||||
expectedDurationMs = durationMs?.takeIf { it > 0L }
|
val isAlreadyRunning = !loaderFinished
|
||||||
|
|
||||||
|
val newDuration = durationMs?.takeIf { it > 0L }
|
||||||
|
|
||||||
|
if (isAlreadyRunning && newDuration == null) {
|
||||||
|
forceIndeterminate = true
|
||||||
|
startTime = System.currentTimeMillis()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedDurationMs = newDuration
|
||||||
forceIndeterminate = expectedDurationMs == null
|
forceIndeterminate = expectedDurationMs == null
|
||||||
loaderFinished = false
|
loaderFinished = false
|
||||||
startTime = System.currentTimeMillis()
|
startTime = System.currentTimeMillis()
|
||||||
|
@ -103,6 +123,7 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
|
|
||||||
fun finishLoader() {
|
fun finishLoader() {
|
||||||
loaderFinished = true
|
loaderFinished = true
|
||||||
|
particles.clear()
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,15 +141,22 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
if (hitIndex >= 0) {
|
if (hitIndex >= 0) {
|
||||||
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
|
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
|
||||||
val target = targets[hitIndex]
|
val target = targets[hitIndex]
|
||||||
|
if (!target.hit) {
|
||||||
target.hit = true
|
target.hit = true
|
||||||
target.hitTime = now
|
target.hitTime = now
|
||||||
score += if (!isIndeterminate) 10 else 5
|
score += if (!isIndeterminate) 10 else 5
|
||||||
spawnParticles(target.x, target.y, target.radius)
|
spawnParticles(target.x, target.y, target.radius)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun spawnTarget() {
|
private fun spawnTarget() {
|
||||||
if (loaderFinished) return
|
if (loaderFinished) return
|
||||||
|
if (width <= 0 || height <= 0) {
|
||||||
|
post { spawnTarget() }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val radius = Random.nextInt(40, 80).toFloat()
|
val radius = Random.nextInt(40, 80).toFloat()
|
||||||
val x = Random.nextFloat() * (width - 2 * radius) + radius
|
val x = Random.nextFloat() * (width - 2 * radius) + radius
|
||||||
val y = Random.nextFloat() * (height - 2 * radius - 60f) + radius
|
val y = Random.nextFloat() * (height - 2 * radius - 60f) + radius
|
||||||
|
@ -144,7 +172,7 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
val speed = Random.nextFloat() * 5f + 2f
|
val speed = Random.nextFloat() * 5f + 2f
|
||||||
val vx = cos(angle) * speed
|
val vx = cos(angle) * speed
|
||||||
val vy = sin(angle) * speed
|
val vy = sin(angle) * speed
|
||||||
particles.add(Particle(cx, cy, vx, vy, 255, System.currentTimeMillis()))
|
particles.add(Particle(cx, cy, vx, vy, System.currentTimeMillis()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,11 +181,14 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
drawBackground(canvas)
|
drawBackground(canvas)
|
||||||
|
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
|
val deltaMs = now - lastFrameTime
|
||||||
|
lastFrameTime = now
|
||||||
|
|
||||||
drawTargets(canvas, now)
|
drawTargets(canvas, now)
|
||||||
drawParticles(canvas, now)
|
drawParticles(canvas, now)
|
||||||
|
|
||||||
if (!loaderFinished) {
|
if (!loaderFinished) {
|
||||||
if (isIndeterminate) drawIndeterminateSpinner(canvas)
|
if (isIndeterminate) drawIndeterminateSpinner(canvas, deltaMs)
|
||||||
else drawDeterministicProgressBar(canvas, now)
|
else drawDeterministicProgressBar(canvas, now)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,17 +215,37 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
targets.removeAll { !it.hit && now - it.spawnTime > expireMs }
|
targets.removeAll { !it.hit && now - it.spawnTime > expireMs }
|
||||||
|
|
||||||
for (t in targets) {
|
for (t in targets) {
|
||||||
val scale = if (t.hit) 1f - ((now - t.hitTime) / 300f).coerceIn(0f, 1f) else 1f
|
val scale = when {
|
||||||
|
t.hit -> {
|
||||||
|
1f - ((now - t.hitTime) / 300f).coerceIn(0f, 1f)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val spawnElapsed = now - t.spawnAnimationStartTime
|
||||||
|
if (spawnElapsed < 300L) {
|
||||||
|
val animProgress = spawnElapsed / 300f
|
||||||
|
bounceInterpolator.getInterpolation(animProgress)
|
||||||
|
} else {
|
||||||
|
val pulseTime = ((now - t.spawnAnimationStartTime) / 1000f) * 2f * PI.toFloat() + t.idlePulseOffset
|
||||||
|
1f + 0.02f * sin(pulseTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val alpha = if (t.hit) ((1f - scale) * 255).toInt().coerceAtMost(255) else 255
|
val alpha = if (t.hit) ((1f - scale) * 255).toInt().coerceAtMost(255) else 255
|
||||||
val safeRadius = (t.radius * scale).coerceAtLeast(1f)
|
val safeRadius = (t.radius * scale).coerceAtLeast(1f)
|
||||||
val glowPaint = Paint().apply {
|
|
||||||
shader = RadialGradient(t.x, t.y, safeRadius, Color.YELLOW, Color.TRANSPARENT, Shader.TileMode.CLAMP)
|
glowPaint.shader = RadialGradient(
|
||||||
}
|
t.x, t.y, safeRadius,
|
||||||
|
Color.YELLOW, Color.TRANSPARENT,
|
||||||
|
Shader.TileMode.CLAMP
|
||||||
|
)
|
||||||
canvas.drawCircle(t.x, t.y, safeRadius * 1.2f, glowPaint)
|
canvas.drawCircle(t.x, t.y, safeRadius * 1.2f, glowPaint)
|
||||||
canvas.drawCircle(t.x + 4f, t.y + 4f, safeRadius, shadowPaint)
|
canvas.drawCircle(t.x + 4f, t.y + 4f, safeRadius, shadowPaint)
|
||||||
|
|
||||||
outerRingPaint.alpha = alpha
|
outerRingPaint.alpha = alpha
|
||||||
middleRingPaint.alpha = alpha
|
middleRingPaint.alpha = alpha
|
||||||
centerDotPaint.alpha = alpha
|
centerDotPaint.alpha = alpha
|
||||||
|
|
||||||
canvas.drawCircle(t.x, t.y, safeRadius, outerRingPaint)
|
canvas.drawCircle(t.x, t.y, safeRadius, outerRingPaint)
|
||||||
canvas.drawCircle(t.x, t.y, safeRadius * 0.66f, middleRingPaint)
|
canvas.drawCircle(t.x, t.y, safeRadius * 0.66f, middleRingPaint)
|
||||||
canvas.drawCircle(t.x, t.y, safeRadius * 0.33f, centerDotPaint)
|
canvas.drawCircle(t.x, t.y, safeRadius * 0.33f, centerDotPaint)
|
||||||
|
@ -212,13 +263,10 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val alpha = ((1f - (age / lifespan.toFloat())) * 255).toInt()
|
val alpha = ((1f - (age / lifespan.toFloat())) * 255).toInt()
|
||||||
val paint = Paint().apply {
|
|
||||||
color = Color.YELLOW
|
|
||||||
this.alpha = alpha
|
|
||||||
}
|
|
||||||
p.x += p.vx
|
p.x += p.vx
|
||||||
p.y += p.vy
|
p.y += p.vy
|
||||||
canvas.drawCircle(p.x, p.y, 6f, paint)
|
particlePaint.alpha = alpha
|
||||||
|
canvas.drawCircle(p.x, p.y, 6f, particlePaint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,20 +283,29 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
canvas.drawRoundRect(rect, barRadius, barRadius, progressBarPaint)
|
canvas.drawRoundRect(rect, barRadius, barRadius, progressBarPaint)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun drawIndeterminateSpinner(canvas: Canvas) {
|
|
||||||
spinnerAngle = (spinnerAngle + 6f) % 360f
|
private fun drawIndeterminateSpinner(canvas: Canvas, deltaMs: Long) {
|
||||||
val cx = width / 2f
|
val cx = width / 2f
|
||||||
val cy = height / 2f
|
val cy = height / 2f
|
||||||
val radius = min(width, height) / 6f
|
val radius = min(width, height) / 6f
|
||||||
val sweepAngle = 270f
|
val sweepAngle = 270f
|
||||||
|
|
||||||
|
spinnerAngle = (spinnerAngle + 0.25f * deltaMs) % 360f
|
||||||
|
|
||||||
|
if (spinnerShader == null) {
|
||||||
|
spinnerShader = SweepGradient(
|
||||||
|
cx, cy,
|
||||||
|
intArrayOf(Color.TRANSPARENT, Color.WHITE, Color.TRANSPARENT),
|
||||||
|
floatArrayOf(0f, 0.5f, 1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
spinnerPaint.shader = spinnerShader
|
||||||
|
|
||||||
val glowPaint = Paint(spinnerPaint).apply {
|
val glowPaint = Paint(spinnerPaint).apply {
|
||||||
maskFilter = BlurMaskFilter(15f, BlurMaskFilter.Blur.SOLID)
|
maskFilter = BlurMaskFilter(15f, BlurMaskFilter.Blur.SOLID)
|
||||||
}
|
}
|
||||||
|
|
||||||
val shader = SweepGradient(cx, cy, intArrayOf(Color.TRANSPARENT, Color.WHITE, Color.TRANSPARENT), floatArrayOf(0f, 0.5f, 1f))
|
|
||||||
spinnerPaint.shader = shader
|
|
||||||
|
|
||||||
canvas.drawArc(cx - radius, cy - radius, cx + radius, cy + radius, spinnerAngle, sweepAngle, false, glowPaint)
|
canvas.drawArc(cx - radius, cy - radius, cx + radius, cy + radius, spinnerAngle, sweepAngle, false, glowPaint)
|
||||||
canvas.drawArc(cx - radius, cy - radius, cx + radius, cy + radius, spinnerAngle, sweepAngle, false, spinnerPaint)
|
canvas.drawArc(cx - radius, cy - radius, cx + radius, cy + radius, spinnerAngle, sweepAngle, false, spinnerPaint)
|
||||||
}
|
}
|
||||||
|
@ -259,7 +316,9 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
val radius: Float,
|
val radius: Float,
|
||||||
val spawnTime: Long,
|
val spawnTime: Long,
|
||||||
var hit: Boolean = false,
|
var hit: Boolean = false,
|
||||||
var hitTime: Long = 0L
|
var hitTime: Long = 0L,
|
||||||
|
val spawnAnimationStartTime: Long = System.currentTimeMillis(),
|
||||||
|
val idlePulseOffset: Float = Random.nextFloat() * 2f * PI.toFloat()
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class Particle(
|
private data class Particle(
|
||||||
|
@ -267,7 +326,6 @@ class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
var y: Float,
|
var y: Float,
|
||||||
val vx: Float,
|
val vx: Float,
|
||||||
val vy: Float,
|
val vy: Float,
|
||||||
var alpha: Int,
|
|
||||||
val startTime: Long
|
val startTime: Long
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue