动画 Canvas 内容更改
Animate Canvas content change
我有一张图片占据了整个屏幕。它就像一个背景。
我正在使用 Canvas
和方法 drawImage(...)
进行渲染,渲染效果很好,符合我的期望。我想在点击后更改 Image
的来源。
如何使这种变化动起来?
我认为 Canvas
没有为任何东西提供动画 API,更不用说图像了。任何类型的动画都可以。交叉淡入淡出,滑动(顺便说一句,首选)。 Canvas 有可能吗?如果不是,那么正确的实现方式是什么?
我的图片绘制代码:
Canvas(modifier = Modifier.fillMaxSize()) {
drawImage(
background,
srcSize = IntSize(background.width, background.height),
dstSize = IntSize(size.width.roundToInt(), size.height.roundToInt())
)
}
希望我的例子能帮助您理解它是如何工作的,您可以使用AnimatedContent
制作您想要的复杂动画。我没有使用 canvas
.
Row {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Add")
}
AnimatedContent(
targetState = count,
transitionSpec = {
// Compare the incoming number with the previous number.
if (targetState > initialState) {
// If the target number is larger, it slides up and fades in
// while the initial (smaller) number slides up and fades out.
slideInVertically({ height -> height }) + fadeIn() with
slideOutVertically({ height -> -height }) + fadeOut()
} else {
// If the target number is smaller, it slides down and fades in
// while the initial number slides down and fades out.
slideInVertically({ height -> -height }) + fadeIn() with
slideOutVertically({ height -> height }) + fadeOut()
}.using(
// Disable clipping since the faded slide-in/out should
// be displayed out of bounds.
SizeTransform(clip = false)
)
}
) { targetCount ->
Image(
imageVector = if (targetCount % 2 == 0) Icons.Filled.ArrowBack else Icons.Filled.AccountBox,
contentDescription = "test"
)
}
}
结果
您可以使用 Animatable
为您在 Canvas
中绘制的任何内容设置动画位置,并像这样绘制两个 old/new 图像:
var currentBackground by remember { mutableStateOf<ImageBitmap?>(null) }
var newBackground by remember { mutableStateOf<ImageBitmap?>(null) }
val offset = remember { Animatable(0f) }
LaunchedEffect(background) {
if (currentBackground == null) {
currentBackground = background
return@LaunchedEffect
}
newBackground = background
offset.animateTo(1f)
currentBackground = background
newBackground = null
offset.snapTo(0f)
}
Canvas(modifier = Modifier.fillMaxSize()) {
fun drawImage(image: ImageBitmap, offset: Float) {
drawImage(
image,
srcSize = IntSize(image.width, image.height),
dstSize = IntSize(size.width.roundToInt(), size.height.roundToInt()),
dstOffset = IntOffset(0, (size.height * offset).roundToInt()),
)
}
clipRect {
currentBackground?.let {
drawImage(it, offset.value)
}
newBackground?.let {
drawImage(it, offset.value - 1)
}
}
}
结果:
我有一张图片占据了整个屏幕。它就像一个背景。
我正在使用 Canvas
和方法 drawImage(...)
进行渲染,渲染效果很好,符合我的期望。我想在点击后更改 Image
的来源。
如何使这种变化动起来?
我认为 Canvas
没有为任何东西提供动画 API,更不用说图像了。任何类型的动画都可以。交叉淡入淡出,滑动(顺便说一句,首选)。 Canvas 有可能吗?如果不是,那么正确的实现方式是什么?
我的图片绘制代码:
Canvas(modifier = Modifier.fillMaxSize()) {
drawImage(
background,
srcSize = IntSize(background.width, background.height),
dstSize = IntSize(size.width.roundToInt(), size.height.roundToInt())
)
}
希望我的例子能帮助您理解它是如何工作的,您可以使用AnimatedContent
制作您想要的复杂动画。我没有使用 canvas
.
Row {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Add")
}
AnimatedContent(
targetState = count,
transitionSpec = {
// Compare the incoming number with the previous number.
if (targetState > initialState) {
// If the target number is larger, it slides up and fades in
// while the initial (smaller) number slides up and fades out.
slideInVertically({ height -> height }) + fadeIn() with
slideOutVertically({ height -> -height }) + fadeOut()
} else {
// If the target number is smaller, it slides down and fades in
// while the initial number slides down and fades out.
slideInVertically({ height -> -height }) + fadeIn() with
slideOutVertically({ height -> height }) + fadeOut()
}.using(
// Disable clipping since the faded slide-in/out should
// be displayed out of bounds.
SizeTransform(clip = false)
)
}
) { targetCount ->
Image(
imageVector = if (targetCount % 2 == 0) Icons.Filled.ArrowBack else Icons.Filled.AccountBox,
contentDescription = "test"
)
}
}
结果
您可以使用 Animatable
为您在 Canvas
中绘制的任何内容设置动画位置,并像这样绘制两个 old/new 图像:
var currentBackground by remember { mutableStateOf<ImageBitmap?>(null) }
var newBackground by remember { mutableStateOf<ImageBitmap?>(null) }
val offset = remember { Animatable(0f) }
LaunchedEffect(background) {
if (currentBackground == null) {
currentBackground = background
return@LaunchedEffect
}
newBackground = background
offset.animateTo(1f)
currentBackground = background
newBackground = null
offset.snapTo(0f)
}
Canvas(modifier = Modifier.fillMaxSize()) {
fun drawImage(image: ImageBitmap, offset: Float) {
drawImage(
image,
srcSize = IntSize(image.width, image.height),
dstSize = IntSize(size.width.roundToInt(), size.height.roundToInt()),
dstOffset = IntOffset(0, (size.height * offset).roundToInt()),
)
}
clipRect {
currentBackground?.let {
drawImage(it, offset.value)
}
newBackground?.let {
drawImage(it, offset.value - 1)
}
}
}
结果: