Updated on 2026-08-14
This commit is contained in:
parent
0df972955a
commit
4356cce18a
11 changed files with 109 additions and 103 deletions
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.tap.features.home.compose.views
|
||||
package com.tangem.tap.common.compose
|
||||
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.Text
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
package com.tangem.tap.features.home.compose.uiTools
|
||||
package com.tangem.tap.common.compose.extensions
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
typealias AnimatedValue = Pair<Float, Float>
|
||||
|
||||
@Composable
|
||||
|
|
@ -24,8 +27,8 @@ fun AnimatedValue.toAnimatable(
|
|||
@Composable
|
||||
fun animatable(
|
||||
values: AnimatedValue,
|
||||
isPaused: Boolean,
|
||||
duration: Int,
|
||||
isPaused: Boolean = false,
|
||||
easing: Easing = LinearEasing,
|
||||
): Animatable<Float, AnimationVector1D> {
|
||||
val animatable = remember { Animatable(values.first) }
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.common.compose.extensions
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -11,4 +12,8 @@ import androidx.compose.ui.unit.Dp
|
|||
fun Dp.toPx(): Float {
|
||||
val currentDp = this
|
||||
return with(LocalDensity.current) { currentDp.toPx() }
|
||||
}
|
||||
}
|
||||
|
||||
fun DpSize.halfWidth(): Dp = this.width / 2
|
||||
|
||||
fun DpSize.halfHeight(): Dp = this.height / 2
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.tap.common.compose.extensions
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
fun asImageBitmap(@DrawableRes drawableId: Int): ImageBitmap {
|
||||
val drawable = AppCompatResources.getDrawable(LocalContext.current, drawableId)
|
||||
?: throw NullPointerException()
|
||||
return drawable.toBitmap().asImageBitmap()
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.tap.common.compose.extensions
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tangem_sdk_new.extensions.dpToPx
|
||||
import com.tangem.tangem_sdk_new.extensions.pxToDp
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
fun Painter.dpSize(): DpSize = DpSize(
|
||||
intrinsicSize.width.pxToDp().dp,
|
||||
intrinsicSize.height.pxToDp().dp,
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun Float.dpToPx(): Float = LocalContext.current.dpToPx(this)
|
||||
|
||||
@Composable
|
||||
private fun Float.pxToDp(): Float = LocalContext.current.pxToDp(this)
|
||||
|
|
@ -1,11 +1,8 @@
|
|||
package com.tangem.tap.features.home.compose.uiTools
|
||||
package com.tangem.tap.features.home.compose
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.absoluteOffset
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.requiredHeight
|
||||
import androidx.compose.foundation.layout.requiredWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -16,16 +13,14 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import com.tangem.tap.common.compose.extensions.AnimatedValue
|
||||
import com.tangem.tap.common.compose.extensions.toAnimatable
|
||||
|
||||
@Composable
|
||||
fun HorizontalSlidingImage(
|
||||
|
|
@ -37,32 +32,20 @@ fun HorizontalSlidingImage(
|
|||
targetOffset: Float,
|
||||
contentDescription: String,
|
||||
) {
|
||||
val offsetX = remember { Animatable(startOffset * -1f) }
|
||||
val translateX = AnimatedValue(startOffset * -1f, (startOffset + targetOffset) * -1f)
|
||||
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.offset { IntOffset(offsetX.value.toInt(), 0) }
|
||||
.requiredWidth(itemSize.width)
|
||||
.requiredHeight(itemSize.height),
|
||||
.requiredHeight(itemSize.height)
|
||||
.graphicsLayer(
|
||||
translationX = translateX.toAnimatable(isPaused = paused, duration = duration).value
|
||||
),
|
||||
alignment = Alignment.TopStart,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
painter = painter,
|
||||
contentDescription = contentDescription,
|
||||
)
|
||||
|
||||
LaunchedEffect(paused) {
|
||||
if (paused) {
|
||||
offsetX.stop()
|
||||
} else {
|
||||
offsetX.animateTo(
|
||||
targetValue = (startOffset + targetOffset) * -1f,
|
||||
animationSpec = tween(
|
||||
durationMillis = duration,
|
||||
easing = LinearEasing,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -109,7 +92,7 @@ fun StoriesTextAnimation(
|
|||
|
||||
@Composable
|
||||
fun StoriesBottomImageAnimation(
|
||||
initialScale: Float = 2f,
|
||||
initialScale: Float = 2.5f,
|
||||
firstStepDuration: Int,
|
||||
totalDuration: Int,
|
||||
content: @Composable (Modifier) -> Unit
|
||||
|
|
@ -120,18 +103,10 @@ fun StoriesBottomImageAnimation(
|
|||
val isFirstStepLaunched = remember { mutableStateOf(false) }
|
||||
val isSecondStepLaunched = remember { mutableStateOf(false) }
|
||||
|
||||
val firstTransition = updateTransition(targetState = isFirstStepLaunched.value, label = "Bottom image animation")
|
||||
val secondTransition = updateTransition(targetState = isSecondStepLaunched.value, label = "Bottom image animation")
|
||||
|
||||
val fadeIn = firstTransition.animateFloat(
|
||||
transitionSpec = {
|
||||
tween(
|
||||
durationMillis = 400,
|
||||
)
|
||||
},
|
||||
label = "Fade in animation"
|
||||
) { value -> if (value) 1f else 0f }
|
||||
|
||||
val firstTransition = updateTransition(
|
||||
targetState = isFirstStepLaunched.value,
|
||||
label = "Image appearing"
|
||||
)
|
||||
val firstScaleStep = firstTransition.animateFloat(
|
||||
transitionSpec = {
|
||||
tween(
|
||||
|
|
@ -139,10 +114,13 @@ fun StoriesBottomImageAnimation(
|
|||
easing = FastOutLinearInEasing,
|
||||
)
|
||||
},
|
||||
label = "Scale"
|
||||
label = "Appearing scale"
|
||||
) { value -> if (value) scaleSwitchBarrier else initialScale }
|
||||
|
||||
|
||||
val secondTransition = updateTransition(
|
||||
targetState = isSecondStepLaunched.value,
|
||||
label = "Image slow outgoing"
|
||||
)
|
||||
val secondScaleStep = secondTransition.animateFloat(
|
||||
transitionSpec = {
|
||||
tween(
|
||||
|
|
@ -150,10 +128,17 @@ fun StoriesBottomImageAnimation(
|
|||
easing = LinearEasing,
|
||||
)
|
||||
},
|
||||
label = "Scale"
|
||||
label = "Outgoing scale"
|
||||
) { value -> if (value) 1f else scaleSwitchBarrier }
|
||||
|
||||
if (firstScaleStep.value == scaleSwitchBarrier) isSecondStepLaunched.value = true
|
||||
val fadeIn = firstTransition.animateFloat(
|
||||
transitionSpec = { tween(durationMillis = 400) },
|
||||
label = "Fade in on start"
|
||||
) { value -> if (value) 1f else 0f }
|
||||
|
||||
if (firstScaleStep.value == scaleSwitchBarrier) {
|
||||
isSecondStepLaunched.value = true
|
||||
}
|
||||
|
||||
val modifier = if (!isSecondStepLaunched.value) {
|
||||
Modifier.scale(firstScaleStep.value)
|
||||
|
|
@ -164,11 +149,4 @@ fun StoriesBottomImageAnimation(
|
|||
content(modifier)
|
||||
|
||||
LaunchedEffect(Unit) { isFirstStepLaunched.value = true }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun asImageBitmap(@DrawableRes drawableId: Int): ImageBitmap {
|
||||
val drawable = AppCompatResources.getDrawable(LocalContext.current, drawableId)
|
||||
?: throw NullPointerException()
|
||||
return drawable.toBitmap().asImageBitmap()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
package com.tangem.tap.features.home.compose.uiTools
|
||||
package com.tangem.tap.features.home.compose
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.tap.features.home.compose.StoriesScreen
|
||||
import com.tangem.tap.features.home.compose.content.*
|
||||
|
||||
/**
|
||||
|
|
@ -26,8 +26,8 @@ import com.tangem.tangem_sdk_new.extensions.dpToPx
|
|||
import com.tangem.tap.common.compose.SpacerH
|
||||
import com.tangem.tap.common.compose.SpacerH16
|
||||
import com.tangem.tap.common.compose.extensions.toAndroidGraphicsColor
|
||||
import com.tangem.tap.features.home.compose.uiTools.StoriesBottomImageAnimation
|
||||
import com.tangem.tap.features.home.compose.uiTools.StoriesTextAnimation
|
||||
import com.tangem.tap.features.home.compose.StoriesBottomImageAnimation
|
||||
import com.tangem.tap.features.home.compose.StoriesTextAnimation
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
|
|
@ -237,7 +237,7 @@ private fun HtmlText(
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun StoriesImage(
|
||||
private fun StoriesImage(
|
||||
modifier: Modifier = Modifier,
|
||||
@DrawableRes drawableResId: Int,
|
||||
isDarkBackground: Boolean,
|
||||
|
|
|
|||
|
|
@ -11,19 +11,17 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tangem_sdk_new.extensions.dpToPx
|
||||
import com.tangem.tangem_sdk_new.extensions.pxToDp
|
||||
import com.tangem.tap.common.compose.SpacerH
|
||||
import com.tangem.tap.common.compose.extensions.dpSize
|
||||
import com.tangem.tap.common.compose.extensions.halfHeight
|
||||
import com.tangem.tap.common.compose.extensions.toPx
|
||||
import com.tangem.tap.common.extensions.isEven
|
||||
import com.tangem.tap.features.home.compose.uiTools.HorizontalSlidingImage
|
||||
import com.tangem.tap.features.home.compose.HorizontalSlidingImage
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
|
|
@ -89,7 +87,7 @@ fun StoriesWeb3Content(
|
|||
}
|
||||
val screenWidth = LocalConfiguration.current.screenWidthDp.dp
|
||||
val decreaseRate = remember { 1f / dappsItemList.size }
|
||||
val designItemHeight = 50.dp
|
||||
val designItemHeight = 75.dp
|
||||
|
||||
LightenBox {
|
||||
Column(modifier = Modifier.graphicsLayer(clip = false)) {
|
||||
|
|
@ -101,7 +99,7 @@ fun StoriesWeb3Content(
|
|||
|
||||
val chessOffset = if (index.isEven()) 0.dp else scaledItemSize.width / 3
|
||||
val animateFrom = chessOffset - moveItemToStartOfScreen
|
||||
val animateTo = 90.dp - (90.dp * index * decreaseRate)
|
||||
val animateTo = 70.dp - (70.dp * index * decreaseRate)
|
||||
|
||||
HorizontalSlidingImage(
|
||||
paused = paused,
|
||||
|
|
@ -112,7 +110,6 @@ fun StoriesWeb3Content(
|
|||
targetOffset = animateTo.toPx(),
|
||||
contentDescription = "Web3 row",
|
||||
)
|
||||
SpacerH(14.dp)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -126,11 +123,13 @@ private fun LightenBox(content: @Composable () -> Unit) {
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(top = 170.dp)
|
||||
.padding(top = 250.dp)
|
||||
.background(
|
||||
brush = Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
Color.White.copy(alpha = 0.3f),
|
||||
Color.White.copy(alpha = 0f),
|
||||
Color.White.copy(alpha = 0.75f),
|
||||
Color.White.copy(alpha = 0.95f),
|
||||
Color.White
|
||||
)
|
||||
)
|
||||
|
|
@ -139,23 +138,7 @@ private fun LightenBox(content: @Composable () -> Unit) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Painter.dpSize(): DpSize = DpSize(
|
||||
intrinsicSize.width.pxToDp().dp,
|
||||
intrinsicSize.height.pxToDp().dp,
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun Float.dpToPx(): Float = LocalContext.current.dpToPx(this)
|
||||
|
||||
@Composable
|
||||
private fun Float.pxToDp(): Float = LocalContext.current.pxToDp(this)
|
||||
|
||||
private fun scaleToDesignSize(itemSize: DpSize, designItemHeight: Dp): DpSize {
|
||||
val scaleRate = itemSize.height / designItemHeight
|
||||
return itemSize / scaleRate
|
||||
}
|
||||
|
||||
private fun DpSize.halfWidth(): Dp = this.width / 2
|
||||
|
||||
private fun DpSize.halfHeight(): Dp = this.height / 2
|
||||
}
|
||||
|
|
@ -19,10 +19,10 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.tap.features.home.compose.uiTools.StoriesBottomImageAnimation
|
||||
import com.tangem.tap.features.home.compose.uiTools.StoriesTextAnimation
|
||||
import com.tangem.tap.features.home.compose.views.FontSizeRange
|
||||
import com.tangem.tap.features.home.compose.views.TextAutoSize
|
||||
import com.tangem.tap.common.compose.FontSizeRange
|
||||
import com.tangem.tap.common.compose.TextAutoSize
|
||||
import com.tangem.tap.features.home.compose.StoriesBottomImageAnimation
|
||||
import com.tangem.tap.features.home.compose.StoriesTextAnimation
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
|
|
@ -113,7 +113,7 @@ fun FirstStoriesContent(
|
|||
) {
|
||||
StoriesBottomImageAnimation(
|
||||
totalDuration = duration,
|
||||
firstStepDuration = 500,
|
||||
firstStepDuration = 400,
|
||||
) { modifier ->
|
||||
Image(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
|
|
@ -128,7 +128,7 @@ fun FirstStoriesContent(
|
|||
}
|
||||
}
|
||||
|
||||
enum class StartingScreenState {
|
||||
private enum class StartingScreenState {
|
||||
INIT, BUY, STORE, SEND, PAY, EXCHANGE, BORROW, LEND, SHOW_CARD, MEET_TANGEM
|
||||
}
|
||||
|
||||
|
|
@ -151,11 +151,6 @@ private fun MutableState<StartingScreenState>.isSplashingTextDisplaying(): Boole
|
|||
this.value != StartingScreenState.SHOW_CARD
|
||||
}
|
||||
|
||||
private fun MutableState<StartingScreenState>.isTangemCardDisplaying(): Boolean {
|
||||
return this.value == StartingScreenState.SHOW_CARD ||
|
||||
this.value == StartingScreenState.MEET_TANGEM
|
||||
}
|
||||
|
||||
private fun MutableState<StartingScreenState>.isMeetTangemDisplaying(): Boolean {
|
||||
return this.value == StartingScreenState.MEET_TANGEM
|
||||
}
|
||||
|
|
@ -6,9 +6,9 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import com.tangem.tap.features.home.compose.uiTools.AnimatedValue
|
||||
import com.tangem.tap.features.home.compose.uiTools.asImageBitmap
|
||||
import com.tangem.tap.features.home.compose.uiTools.toAnimatable
|
||||
import com.tangem.tap.common.compose.extensions.AnimatedValue
|
||||
import com.tangem.tap.common.compose.extensions.asImageBitmap
|
||||
import com.tangem.tap.common.compose.extensions.toAnimatable
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
|
|
@ -84,7 +84,7 @@ private class FloatingCard {
|
|||
|
||||
fun second(): CardValues = CardValues(
|
||||
translateX = 350f to 300f,
|
||||
translateY = 0f to 50f,
|
||||
translateY = -70f to 0f,
|
||||
rotationX = 30f to 48f,
|
||||
rotationY = 0f to 5f,
|
||||
rotationZ = -34f to -42f,
|
||||
|
|
@ -97,7 +97,7 @@ private class FloatingCard {
|
|||
rotationX = 0f to 3f,
|
||||
rotationY = 10f to 10f,
|
||||
rotationZ = -45f to -30f,
|
||||
scale = 0.6f to 0.7f,
|
||||
scale = 0.6f to 0.75f,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue