Jetpack Compose를 사용하여 버튼 배경색을 동적으로 전환하는 애니메이션 구현하기
Column(
modifier = Modifier.padding(16.dp)
) {
Text(text = "Theme Changing", fontSize = 32.sp, fontWeight = FontWeight.Bold)
Spacer(modifier = Modifier.height(12.dp))
LazyRow(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
// Code for LazyRow items
}
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(vertical = 12.dp)
.onGloballyPositioned {
// Code for Box content
}
.clickable { triggerAnimation = true }
.padding(16.dp),
contentAlignment = Alignment.Center
) {
// Code for inner Box
}
}
다음과 같이 선택할 수 있는 컬러 목록과 색상변경이 적용될 버튼을 만들었다.
var triggerAnimation by remember { mutableStateOf(false) }
var width by remember { mutableFloatStateOf(0f) }
var animationProgress by remember { mutableFloatStateOf(0f) }
var newColor by remember { mutableStateOf(Color.Blue) }
var currentColor by remember { mutableStateOf(Color.Gray) }
triggerAnimation
width, animationProgress
newColor, currentColor
LaunchedEffect(triggerAnimation) {
if (triggerAnimation) {
animate(
initialValue = 0f,
targetValue = 1f,
animationSpec = tween(durationMillis = 1000, easing = LinearEasing)
) { value, _ ->
animationProgress = value
}
triggerAnimation = false
currentColor = newColor
animationProgress = 0f
}
}
Box(
modifier = Modifier
.width(50.dp)
.aspectRatio(1f)
.background(color = contents[it], shape = CircleShape)
.clickable {
triggerAnimation = true
newColor = contents[it]
}
)
LaunchedEffect로 triggerAnimation의 값이 변할 때마다 animate로 그라데이션의 위치를 이동시킨다.
사용자가 컬러 박스를 클릭하면 triggerAnimation이 true가 되도록 하였다.
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.background(
Brush.horizontalGradient(
colorStops = arrayOf(
animationProgress to newColor,
1f to currentColor
),
endX = (animationProgress) * width,
tileMode = TileMode.Clamp
),
shape = RoundedCornerShape(12.dp)
),
contentAlignment = Alignment.Center
) {
Text("Button", modifier = Modifier.padding(16.dp))
}
brush를 이용하여 그라데이션을 만들었고 colorStop과 endX를 이용하여 이전 색상과 현재 색상의 구분이 명확히 보이도록 하였다.
@Composable
fun GradientTransition(
) {
val contents = listOf(
Color.Red,
Color.Blue,
Color.Yellow,
Color.Gray,
Color.Magenta
)
var triggerAnimation by remember { mutableStateOf(false) }
var width by remember {
mutableFloatStateOf(0f)
}
var animationProgress by remember { mutableFloatStateOf(0f) }
var newColor by remember {
mutableStateOf(Color.Blue)
}
var currentColor by remember {
mutableStateOf(Color.Gray)
}
LaunchedEffect(triggerAnimation) {
if (triggerAnimation) {
animate(
initialValue = 0f,
targetValue = 1f,
animationSpec = tween(durationMillis = 1000, easing = LinearEasing)
) { value, _ ->
animationProgress = value
}
triggerAnimation = false
currentColor = newColor
animationProgress = 0f
}
}
Column(
modifier = Modifier
.padding(16.dp)
) {
Text(text = "Theme Changing", fontSize = 32.sp, fontWeight = FontWeight.Bold)
Spacer(modifier = Modifier.height(12.dp))
LazyRow(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
items(contents.size) {
Box(
modifier = Modifier
.width(50.dp)
.aspectRatio(1f)
.background(color = contents[it], shape = CircleShape)
.clickable {
triggerAnimation = true
newColor = contents[it]
}
)
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(vertical = 12.dp)
.onGloballyPositioned {
width = it.size.width.toFloat()
}
.clickable { triggerAnimation = true }
.padding(16.dp),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.background(
Brush.horizontalGradient(
colorStops = arrayOf(
animationProgress to newColor,
1f to currentColor
),
endX = (animationProgress) * width,
tileMode = TileMode.Clamp
),
shape = RoundedCornerShape(12.dp)
),
contentAlignment = Alignment.Center
) {
Text("Button", modifier = Modifier.padding(16.dp))
}
}
}
}