You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Canvas绘制全屏背景图,点击切换图片时如何添加过渡动画

这种需求完全可以在Canvas中实现,不需要更换实现方案。你认为Canvas没有专门动画API是误解,我们可以结合Compose的动画状态API + 调整Canvas绘制参数来实现你要的两种效果,实现逻辑如下:

前置准备

首先定义几个必要的状态变量:

  • 存储当前显示的背景图、待切换的目标背景图
  • 切换动画触发标记
  • 绑定动画的进度值(取值范围0~1,0表示完全显示当前图,1表示完全显示新图)

示例状态代码:

var currentBackground by remember { mutableStateOf(/* 你的初始背景图ImageBitmap */) }
var nextBackground by remember { mutableStateOf<ImageBitmap?>(null) }
var switchTrigger by remember { mutableStateOf(false) }
val switchProgress by animateFloatAsState(
    targetValue = if (switchTrigger) 1f else 0f,
    animationSpec = tween(durationMillis = 300), // 可自定义动画时长、动画曲线
    finishedListener = { progress ->
        if (progress == 1f) {
            // 动画结束后更新当前图,重置状态方便下次切换
            currentBackground = nextBackground!!
            nextBackground = null
            switchTrigger = false
        }
    }
)

方案1:滑动切换效果(优先实现)

实现逻辑:切换过程中,旧图随动画进度向左侧滑出屏幕,新图从右侧随进度滑入屏幕,通过调整两张图的绘制偏移量实现:

Canvas(
    modifier = Modifier
        .fillMaxSize()
        .clickable {
            // 点击时赋值要切换的新图,触发动画
            nextBackground = /* 你的新背景图ImageBitmap */
            switchTrigger = true
        }
) {
    val screenWidth = size.width
    // 绘制旧图
    drawImage(
        currentBackground,
        srcSize = IntSize(currentBackground.width, currentBackground.height),
        dstSize = IntSize(size.width.roundToInt(), size.height.roundToInt()),
        dstOffset = IntOffset((-screenWidth * switchProgress).roundToInt(), 0)
    )
    // 新图存在时绘制
    nextBackground?.let { newBg ->
        drawImage(
            newBg,
            srcSize = IntSize(newBg.width, newBg.height),
            dstSize = IntSize(size.width.roundToInt(), size.height.roundToInt()),
            dstOffset = IntOffset((screenWidth * (1 - switchProgress)).roundToInt(), 0)
        )
    }
}

如果需要上下滑动效果,只需要把dstOffset的x轴参数改为y轴即可。


方案2:淡入淡出交叉效果

实现逻辑:切换过程中,旧图透明度从1降到0,新图透明度从0升到1,两张图重叠绘制实现交叉渐变:

Canvas(
    modifier = Modifier
        .fillMaxSize()
        .clickable {
            nextBackground = /* 你的新背景图ImageBitmap */
            switchTrigger = true
        }
) {
    // 绘制旧图,透明度随进度降低
    drawImage(
        currentBackground,
        srcSize = IntSize(currentBackground.width, currentBackground.height),
        dstSize = IntSize(size.width.roundToInt(), size.height.roundToInt()),
        alpha = 1f - switchProgress
    )
    // 绘制新图,透明度随进度升高
    nextBackground?.let { newBg ->
        drawImage(
            newBg,
            srcSize = IntSize(newBg.width, newBg.height),
            dstSize = IntSize(size.width.roundToInt(), size.height.roundToInt()),
            alpha = switchProgress
        )
    }
}

注意事项

  • 触发切换前要确保新图片已经加载完成,避免切换过程中出现空白
  • 你也可以自定义其他切换效果,只要调整对应绘制参数(缩放比例、裁剪区域等)和动画进度的绑定逻辑即可,不需要修改整体实现框架

内容的提问来源于stack exchange,提问作者Richard Onslow Roper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 13:54:04