手把手教你写 Compose 动画 -- 讲的不能再细的 AnimationSpec 动画规范

前面我们聊过 animateDpAsStateAnimatable 两种动画 API 的用法,但只是简单了解了,这两个函数内部都有一个共同的核心参数:AnimationSpec。

@Composable
fun animateDpAsState(
    targetValue: Dp,
    animationSpec: AnimationSpec<Dp> = dpDefaultSpring,  // AnimationSpec
    label: String = "DpAnimation",
    finishedListener: ((Dp) -> Unit)? = null
)
class Animatable<T, V : AnimationVector>(
    initialValue: T,
    val typeConverter: TwoWayConverter<T, V>,
    private val visibilityThreshold: T? = null,
    val label: String = "Animatable"
) {
	suspend fun animateTo(
        targetValue: T,
        animationSpec: AnimationSpec<T> = defaultSpringSpec,  // AnimationSpec
        initialVelocity: T = velocity,
        block: (Animatable<T, V>.() -> Unit)? = null
    )
}

这个 AnimationSpec 是什么?有什么用?

官方的说法:你可以通过可选的 AnimationSpec 参数来自定义动画规范(也就是可以实现不同类型的动画效果)。

AnimationSpec 是一个接口,有很多实现类:

在这里插入图片描述

这些 **Spec 就可以帮助你自定义出各种动画效果。

现在再回头看一下 animateDpAsStateAnimatable.animateToAnimationSpec 参数:

animateDpAsState:
    -> animationSpec: AnimationSpec<Dp> = dpDefaultSpring

animateTo:
    -> animationSpec: AnimationSpec<T> = defaultSpringSpec

他们默认都是一个 SpringSpec 的弹簧效果,而且默认是不回弹。

@Immutable
class SpringSpec<T>(
    val dampingRatio: Float = Spring.DampingRatioNoBouncy,
    val stiffness: Float = Spring.StiffnessMedium,
    val visibilityThreshold: T? = null
)

/**
 * Damping ratio for a spring with no bounciness. This damping ratio will create a
 * critically damped spring that returns to equilibrium within the shortest amount of time
 * without oscillating.
 */
const val DampingRatioNoBouncy = 1f

了解到这里,你应该就清楚 AnimationSpec 有什么用了,接下来我们就会挨个研究 AnimationSpec 那么多实现类的具体动画效果,走起~


AnimationSpec


先看下 AnimationSpec 有哪些实现类:

在这里插入图片描述


📓 TweenSpec


TweenSpec:应该算是最简单的一类 Spec 了,它在指定的 durationMillis 内使用缓和曲线在起始值和结束值之间添加动画效果。

先来看一下它的构造函数:

@Immutable
class TweenSpec<T>(
    val durationMillis: Int = DefaultDurationMillis,
    val delay: Int = 0,
    val easing: Easing = FastOutSlowInEasing
)

它有三个参数:

  1. durationMillis: 动画时长,不填的话默认 300ms
  2. delay:动画启动延时,默认不延时
  3. easing:Easing 类型,设置动画曲线,默认是 FastOutSlowInEasing 效果

除了 FastOutSlowInEasing,Compose 还提供了三个默认的动画曲线:

// 默认曲线:先加速再减速
val FastOutSlowInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)
// 全程减速
val LinearOutSlowInEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)
// 全程加速
val FastOutLinearInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 1.0f, 1.0f)
// 线性曲线:匀速运动
val LinearEasing: Easing = Easing { fraction -> fraction }

按个试下效果:

LinearEasing:匀速运动

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var big by mutableStateOf(false)

        setContent {
            val size = remember(big) { if (big) 96.dp else 48.dp }
            val anim = remember { Animatable(size, Dp.VectorConverter) }
            LaunchedEffect(big) {
                anim.animateTo(size, TweenSpec(easing = LinearEasing))
            }

            Box(
                Modifier
                    .size(anim.value)
                    .background(Color.Blue)
                    .clickable {
                        big = !big
                    })
        }
    }
}

在这里插入图片描述

FastOutSlowInEasing:先加速再减速(默认曲线,我们可以不填)

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var big by mutableStateOf(false)

        setContent {
            val size = remember(big) { if (big) 96.dp else 48.dp }
            val anim = remember { Animatable(size, Dp.VectorConverter) }
            LaunchedEffect(big) {
                anim.animateTo(size, TweenSpec())
            }

            Box(
                Modifier
                    .size(anim.value)
                    .background(Color.Blue)
                    .clickable {
                        big = !big
                    })
        }
    }
}

在这里插入图片描述

LinearOutSlowInEasing:全程减速

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var big by mutableStateOf(false)

        setContent {
            val size = remember(big) { if (big) 96.dp else 48.dp }
            val anim = remember { Animatable(size, Dp.VectorConverter) }
            LaunchedEffect(big) {
                anim.animateTo(size, TweenSpec(easing = LinearOutSlowInEasing))
            }

            Box(
                Modifier
                    .size(anim.value)
                    .background(Color.Blue)
                    .clickable {
                        big = !big
                    })
        }
    }
}

在这里插入图片描述

FastOutLinearInEasing:全程加速

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var big by mutableStateOf(false)

        setContent {
            val size = remember(big) { if (big) 96.dp else 48.dp }
            val anim = remember { Animatable(size, Dp.VectorConverter) }
            LaunchedEffect(big) {
                anim.animateTo(size, TweenSpec(easing = FastOutLinearInEasing))
            }

            Box(
                Modifier
                    .size(anim.value)
                    .background(Color.Blue)
                    .clickable {
                        big = !big
                    })
        }
    }
}

在这里插入图片描述

录制的 Gif 图效果真不咋地,这几种效果还是建议你自己试一遍吧。

但有点 low 啊,就这四个默认曲线完全满足不了我的需求,在实际开发中,我们往往需要的动画曲线各种各样,而 UX 设计师也会提供给我们一些设计好的动画曲线,比如:(0.0,0.0,0.1,1.0),(0.2,0.1,0.0,1.0)。

再来看一下之前的动画曲线:

val FastOutSlowInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)
val LinearOutSlowInEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)
val FastOutLinearInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 1.0f, 1.0f)

他们都用一个 CubicBezierEasing 包起来了,它有四个参数,这四个参数就是定义三阶贝塞尔曲线的。

三阶贝塞尔曲线有四个点,但其中有两个点是固定的,分别是:(0, 0) 和 (1, 1),我们只需要给出另外两个点即可定义出三阶贝塞尔曲线。

不懂的话,给你一个网站

在这里插入图片描述

这个就是一个可以看到三阶贝塞尔曲线定制效果的工具网站,其中两个白点就是固定的 (0, 0) 和 (1, 1),而红色和绿色两个点就对应我们动画曲线的四个参数,即 2 个坐标。

在这里插入图片描述

你可以随意拖动红点和蓝点,上方的坐标参数会同步更新,所以,UX 设计师提供给我们的坐标点,就可以在这个网站测试动画效果。

在这里插入图片描述

  1. 横坐标:时间完成度,纵坐标:动画完成读
  2. 曲线越陡峭,速度越快,曲线越平缓,速度越慢。

我们来看下 Compose 提供的几个动画曲线的效果:

// 先加速后减速
val FastOutSlowInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)

在这里插入图片描述

红色就是我们曲线的动画效果,绿色是可以在右边选取做对照用的,我们用的是匀速曲线。这个对比效果还是很明显的。

// 全程减速
val LinearOutSlowInEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)

在这里插入图片描述

// 全程加速
val FastOutLinearInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 1.0f, 1.0f)

在这里插入图片描述

这里的效果要比我们之前 Gif 的图效果要好不少。

TweenSpec 的用法和核心知识点就这么多,我猜你应该知道怎么用 TweenSpec 定制动画了。

最后讲个小的技巧,Compose 提供了一个 tween 函数简化写法,等同于 TweenSpec,它内部就是调用 TweenSpec。

@Stable
fun <T> tween(
    durationMillis: Int = DefaultDurationMillis,
    delayMillis: Int = 0,
    easing: Easing = FastOutSlowInEasing
): TweenSpec<T> = TweenSpec(durationMillis, delayMillis, easing)

所以以下写法随便你写哪个:

anim.animateTo(size, TweenSpec(easing = FastOutLinearInEasing))
anim.animateTo(size, tween(easing = FastOutLinearInEasing))

📓 SnapSpec


讲完 TweenSpec,我们再来看一个靠着它的 SnapSpec。

在这里插入图片描述

snap 是一个特殊的 AnimationSpec,它会立即将值切换到结束值。

我不知道你还有没有印象,在前面讲解 Animatable 的时候,我们提高过一个 snapTo 的函数,它可以指定目标值突变到某一个值,再来回顾下吧:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var big by mutableStateOf(false)

        setContent {
            val size = remember(big) { if (big) 96.dp else 48.dp }
            val anim = remember { Animatable(size, Dp.VectorConverter) }
            LaunchedEffect(big) {
                anim.snapTo(if (big) 192.dp else 0.dp)
                anim.animateTo(size)
            }

            Box(Modifier.size(anim.value)
                .background(Color.Blue)
                .clickable {
                    big = !big
                })
        }
    }
}

我们添加了一句:anim.snapTo(if (big) 192.dp else 0.dp)

  1. Box 由小变大时,size 会瞬间到 0dp,然后从 0 -> 48dp
  2. Box 由大变小时,size 会瞬间到 192.dp,然后从 192.dp -> 96dp

看下效果:
在这里插入图片描述

而 SnapSpec 其实跟它是差不多的意思,就是让动画效果突变,瞬间完成。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var big by mutableStateOf(false)

        setContent {
            val size = remember(big) { if (big) 96.dp else 48.dp }
            val anim = remember { Animatable(size, Dp.VectorConverter) }
            LaunchedEffect(big) {
                anim.animateTo(size, SnapSpec())
            }

            Box(
                Modifier
                    .size(anim.value)
                    .background(Color.Blue)
                    .clickable {
                        big = !big
                    })
        }
    }
}

看下效果:

在这里插入图片描述

但 SnapSpec() 还有个参数,可以指定 delayMillis 来延迟动画播放的开始时间。

class SnapSpec<T>(val delay: Int = 0) : DurationBasedAnimationSpec<T>

比如我们添加一个 2s 的延时:

在这里插入图片描述

tween 一样,Compose 也提供了一个 SnapSpec 的简化写法:snap

anim.animateTo(size, SnapSpec())
anim.animateTo(size, snap())

📓 KeyframesSpec


讲完 TweenSpec、SnapSpec,我们再来看一个靠着它俩的 KeyframesSpec。

在这里插入图片描述

KeyframesSpce 就是关键帧的意思,可以让我们在动画过程中选取几个关键的时间点,并给出这些时间点对应的动画完成度,相当于分段式的 TweenSpec。

在讲解 KeyframesSpce 的使用方法之前,有个细节需要说一下:如果我们用它的构造函数来创建的话会比较麻烦,需要像下面这样写:

anim.animateTo(size, KeyframesSpec<Dp>(KeyframesSpec.KeyframesSpecConfig()).apply { 
    
})

很长很啰嗦,Compose 同样提供了简便的 keyframes 来创建 KeyframesSpec:

@Stable
fun <T> keyframes(
    init: KeyframesSpec.KeyframesSpecConfig<T>.() -> Unit
): KeyframesSpec<T> {
    return KeyframesSpec(KeyframesSpec.KeyframesSpecConfig<T>().apply(init))
}

所以我们通常会这么写:

anim.animateTo(size, keyframes {  })

现在我们可以写内部代码了:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var big by mutableStateOf(false)

        setContent {
            val size = remember(big) { if (big) 96.dp else 48.dp }
            val anim = remember { Animatable(size, Dp.VectorConverter) }
            LaunchedEffect(big) {
                anim.animateTo(size, keyframes {
                    144.dp at 150
                })
            }

            Box(
                Modifier
                    .size(anim.value)
                    .background(Color.Blue)
                    .clickable {
                        big = !big
                    })
        }
    }
}

我们在 keyframes{} 内部加了一个 144.dp at 150,这一行代码是什么含义?注意,这里有个坑。

我之前一直以为是这么个意思:

  1. 放大过程:动画从 0ms 开始,也就是 48.dp 开始慢慢增大,到 150ms 的时候,相当于走了默认时长 300ms 的一半,这个时候瞬间到达144.dp,然后剩下来的 150ms,从 144.dp 降到 96.dp。
  2. 缩小过程:动画从 0ms 开始,也就是 96.dp 开始慢慢减小,到 150ms 的时候,相当于走了默认时长 300ms 的一半,这个时候瞬间回到 144.dp,然后剩下来的 150ms,从 144.dp 降到 48.dp。

这个理解对吗?我们先看下实际效果:

在这里插入图片描述

不对劲啊,感觉动画效果完全不是我刚才说的那个流程。

实际上,KeyframesSpce 添加关键帧,是把动画给拆分了,就像前面说的,它相当于分段式的 TweenSpec。

正确的流程应该是:

  1. 放大过程:0ms - 150ms:48dp -> 144dp,150ms - 300ms: 144dp -> 96dp
  2. 缩小过程:0ms - 150ms:96dp -> 144dp,150ms - 300ms: 144dp -> 48dp

这个流程就和动画是一样的了。

我们再来加一个关键帧:

anim.animateTo(size, keyframes {
    144.dp at 150
    70.dp at 250
})

我们推理一下流程:

  1. 放大过程:0ms - 150ms:48dp -> 144dp,150ms - 250ms: 144dp -> 70dp,250ms:70dp -> 96dp,会是:放大 -> 缩小 -> 放大的效果
  2. 缩小过程:0ms - 150ms:96dp -> 144dp,150ms - 250ms: 144dp -> 70dp,250ms:70dp -> 48dp,会是:放大 -> 缩小 -> 缩小的效果

看下运行效果:

在这里插入图片描述

正如我们推理的流程,但是缩小的动画太不明显了,因为动画总时长就300ms,几乎是一瞬间的事,那如果我把动画时长设置的长一点呢?

anim.animateTo(size, keyframes {
    144.dp at 150
    70.dp at 250
    durationMillis = 1000
})

我们可以设置 durationMillis 参数,手动控制动画时长,再来看一下效果:

在这里插入图片描述

这么看就很明显了,缩小的时候,第一阶段缩小很快,第二阶段缩小很慢。

另外 KeyframesSpce 也可以设置延迟:

anim.animateTo(size, keyframes {
    144.dp at 150
    70.dp at 250
    durationMillis = 1000
    delayMillis = 1500
})

在这里插入图片描述

讲到这里,基本上了解了 KeyframesSpce 怎么用了吧?它跟 TweenSpec 基本上一样,但是我们讲 TweenSpec 的时候,有一个重点知识就是:动画曲线,不同动画曲线速度是不一样的。

那 KeyframesSpce 呢?它能设置动画曲线吗???

也是可以的,比如:

anim.animateTo(size, keyframes {
    144.dp at 150 with FastOutSlowInEasing
    70.dp at 250
    durationMillis = 1000
    delayMillis = 1500
})

KeyframesSpace 是分段动画,可以给每段动画单独指定一个动画曲线,但是问题就来了:

    144.dp at 150 with FastOutSlowInEasing
    70.dp at 250

FastOutSlowInEasing 这个先加速后减速的动画曲线到底是 150ms 之前的动画还是 150ms 之后的动画?- - 之后的动画,也就是 150ms - 250ms 之间的动画。

那 150ms 之前想设置动画曲线怎么办?

anim.animateTo(size, keyframes {
    48.dp at 0 with FastOutLinearInEasing
    144.dp at 150 with FastOutSlowInEasing
    70.dp at 250
    durationMillis = 1000
    delayMillis = 1500
})

就这么简单,再加一个动画从 0ms 开始的动画曲线,不过要注意:这个48dp只是一个示例,因为如果是从放大到缩小,这边就应该写出 96dp,所以根据实际情况,你要写对动画的初始值。


📓 SpringSpec


还记得我们刚开始讲动画的时候提到过 animateTo 有一个默认的动画效果吗?

suspend fun animateTo(
    targetValue: T,
    animationSpec: AnimationSpec<T> = defaultSpringSpec,
    initialVelocity: T = velocity,
    block: (Animatable<T, V>.() -> Unit)? = null
)

internal val defaultSpringSpec: SpringSpec<T> =
    SpringSpec(visibilityThreshold = visibilityThreshold)

SpringSpec 就是默认的动画:弹簧效果。

你要创建一个弹簧效果,代码写起来也很简单:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var big by mutableStateOf(false)

        setContent {
            val size = remember(big) { if (big) 96.dp else 48.dp }
            val anim = remember { Animatable(size, Dp.VectorConverter) }
            LaunchedEffect(big) {
                anim.animateTo(size, spring())
            }

            Box(
                Modifier
                    .size(anim.value)
                    .background(Color.Blue)
                    .clickable {
                        big = !big
                    })
        }
    }
}

如代码里面写的:Compose 也提供了一个 spring 快捷方法,用于创建 SpringSpec。就这样,啥也不用填就可以自定义一个弹窗动画了。

那我如果要定制更复杂一点的弹簧动画呢?那就看看 SpringSpec 构造函数有哪些参数呗:

@Immutable
class SpringSpec<T>(
    val dampingRatio: Float = Spring.DampingRatioNoBouncy,
    val stiffness: Float = Spring.StiffnessMedium,
    val visibilityThreshold: T? = null
)

dampingRatio:阻尼比

**一句话解释:**就是弹簧弹的过程中会受到多大的阻力,默认值是:DampingRatioNoBouncy = 1f,没有任何弹簧效果。

DampingRatioNoBouncy = 1f

anim.animateTo(size, spring())

在这里插入图片描述

DampingRatioNoBouncy = 0.1f

anim.animateTo(size, spring(dampingRatio = 0.1f))

在这里插入图片描述

anim.animateTo(size, spring(dampingRatio = 10f))

在这里插入图片描述

stiffness: 刚度

一句话解释: 弹簧有多硬,越硬回弹越猛,默认值是:StiffnessMedium = 1500f。

DampingRatioNoBouncy = 0.1f, StiffnessMedium = 1500f

anim.animateTo(size, spring(dampingRatio = 0.1f, stiffness = 1500f))

在这里插入图片描述

DampingRatioNoBouncy = 0.1f, StiffnessMedium = 10_000f

anim.animateTo(size, spring(dampingRatio = 0.1f, stiffness = 1500f))

在这里插入图片描述

DampingRatioNoBouncy = 0.1f, StiffnessMedium = 400f

anim.animateTo(size, spring(dampingRatio = 0.1f, stiffness = 1500f))

在这里插入图片描述

visibilityThreshold: 可视阈值

一句话解释: 如果你把 DampingRatioNoBouncy 设置的特别特别小,它会一直弹,那么我们就可以通过设置 visibilityThreshold 阈值,让弹窗到达这个阈值后直接停止。

anim.animateTo(size, spring(dampingRatio = 0.05f, stiffness = 1500f, 10.dp))

在这里插入图片描述

支持,SpringSpec 弹窗动画效果也讲完了。


📓 RepeatableSpec


RepeatableSpec 主要用于反复运行基于时长的动画(例如 tween 或 keyframes),直至达到指定的迭代计数。

看下它的构造函数你就懂了:

@Stable
fun <T> repeatable(
    iterations: Int,
    animation: DurationBasedAnimationSpec<T>,
    repeatMode: RepeatMode = RepeatMode.Restart,
    initialStartOffset: StartOffset = StartOffset(0)
): RepeatableSpec<T> =
    RepeatableSpec(iterations, animation, repeatMode, initialStartOffset)
  1. iterations:迭代参数,就是要重复的次数,填 1 就是不重复
anim.animateTo(size, repeatable(3))
  1. animation:需要传入一个 DurationBasedAnimationSpec 类型的动画,哪些动画是 DurationBasedAnimationSpec 类型的?

在这里插入图片描述

anim.animateTo(size, repeatable(3, tween()))

这样就可以运行了:

在这里插入图片描述

  1. repeatMode:重复模式,重启还是倒放
anim.animateTo(size, repeatable(3, tween(), RepeatMode.Reverse)) // 倒放

在这里插入图片描述

注意:如果你设置了 repeatMode 为倒放,那么 iterations 迭代次数不能设置成双数,只能单数,比如:

anim.animateTo(size, repeatable(2, tween(), RepeatMode.Reverse))

在这里插入图片描述

动画由目标值,比如从小到大,倒放次数为2,会是这样的流程:

动画开始:48.dp -> 96.dp(第一次),96.dp -> 48.dp(倒放),但是目标值是96dp,所以又会瞬间回到 96,这样效果就不对了。

  1. initialStartOffset:初始启动偏移(时间偏移),默认值为0,即不偏移。
anim.animateTo(size, repeatable(3, tween(), RepeatMode.Reverse, StartOffset(1000)))

不过 StartOffset 还有第二个参数,可以设置是“延时型偏移”还是“快进型偏移”。

anim.animateTo(size, repeatable(3, tween(), RepeatMode.Reverse,
	StartOffset(1000, StartOffsetType.Delay)))

效果应该是延时 1s 后再运行动画:(放大)大 -> 小 -> 大,(缩小)小 -> 大 -> 小
在这里插入图片描述

anim.animateTo(size, repeatable(3, tween(), RepeatMode.Reverse,
	StartOffset(1000, StartOffsetType.FastForward)))

效果应该是快进 1s 后再运行动画:(放大) 小 -> 大,(缩小) 大 -> 小

在这里插入图片描述

至此,RepeatableSpec 也讲完了。


📓 InfiniteRepeatableSpec


InfiniteRepeatableSpec 与 RepeatableSpec 类似,唯一的区别就是:它会重复无限次的迭代。

class InfiniteRepeatableSpec<T>(
    val animation: DurationBasedAnimationSpec<T>,
    val repeatMode: RepeatMode = RepeatMode.Restart,
    val initialStartOffset: StartOffset = StartOffset(0)
)
LaunchedEffect(big) {
    anim.animateTo(size, infiniteRepeatable(tween(), RepeatMode.Reverse,
        StartOffset(1000, StartOffsetType.Delay))
    )
}

在这里插入图片描述

当协程取消后,动画即可停止。

至此,所有的 AnimationSpec 就全部讲完了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/228323.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营第45天| 70. 爬楼梯 (进阶) 322. 零钱兑换 279.完全平方数

JAVA代码编写 70. 爬楼梯&#xff08;进阶版) 卡码网&#xff1a;57. 爬楼梯&#xff08;第八期模拟笔试&#xff09; 题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬至多m (1 < m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f…

卷王开启验证码后无法登陆问题解决

问题描述 使用 docker 部署&#xff0c;后台设置开启验证&#xff0c;重启服务器之后&#xff0c;docker重启&#xff0c;再次访问系统&#xff0c;验证码获取失败&#xff0c;导致无法进行验证&#xff0c;也就无法登陆系统。 如果不了解卷王的&#xff0c;可以去官网看下。…

【K8S】微服务不香了?单体化改造悄然兴起!!

微服务一直以来是服务治理的基本盘之一,落地到云原生上,往往是每个 K8s pods 部署一个服务,独立迭代、独立运维。 但是在快速部署的时候,有时候,我们可能需要一些宏服务的优势。有没有一种方法,能够 “既要又要” 呢?本文基于 tRPC-Go 服务,提出并最终实践了一种经验证…

医学图像数据处理流程以及遇到的问题

数据总目录&#xff1a; /home/bavon/datasets/wsi/hsil /home/bavon/datasets/wsi/lsil 1 规整文件命名以及xml拷贝 data_prepare.py 的 align_xml_svs 方法 if __name__ __main__: file_path "/home/bavon/datasets/wsi/lsil"# align_xml_svs(file_path) # b…

程序员的养生指南(生命诚可贵,一人永流传!珍惜生命,从你我做起)

作为程序员&#xff0c;我们经常需要长时间坐在电脑前工作&#xff0c;这对我们的身体健康造成了很大的影响。为了保持健康&#xff0c;我们需要采取一些养生措施来延寿。下面是我个人的一些养生经验和建议&#xff0c;希望能对大家有所帮助。 1、合理安排工作时间&#xff1a;…

Bert-vits2新版本V2.1英文模型本地训练以及中英文混合推理(mix)

中英文混合输出是文本转语音(TTS)项目中很常见的需求场景&#xff0c;尤其在技术文章或者技术视频领域里&#xff0c;其中文文本中一定会夹杂着海量的英文单词&#xff0c;我们当然不希望AI口播只会念中文&#xff0c;Bert-vits2老版本(2.0以下版本)并不支持英文训练和推理&…

多功能智能遥测终端机 5G/4G+北斗多信道 视频采集传输

计讯物联多功能智能遥测终端机&#xff0c;全网通5G/4G无线通信、弱信号地区北斗通信&#xff0c;多信道自动切换保障通信联通&#xff0c;丰富网络接口及行业应用接口&#xff0c;支持水利、环保、工业传感器、控制终端、智能终端接入&#xff0c;模拟量/数字量/信号量采集&am…

一文详解Java反射

文章目录 反射是什么&#xff1f;反射的作用所有方法汇总一、加载Class对象二、加载类的构造器对象三、加载类的成员变量四、加载类的成员方法 反射是什么&#xff1f; 反射就是&#xff1a;加载类&#xff0c;并允许以编程的方式解剖类中的某个成分&#xff08;成员变量&#…

ambari hive on Tez引擎一直卡住

hive on tez使用./bin/hive启动后一直卡住&#xff0c;无法进入命令行 使用TEZ作为Hive默认执行引擎时&#xff0c;需要在调用Hive CLI的时候启动YARN应用&#xff0c;预分配资源&#xff0c;这需要花一些时间&#xff0c;而使用MapReduce作为执行引擎时是在执行语句的时候才会…

微信小程序UI自动化测试实践:Minium+PageObject

小程序架构上分为渲染层和逻辑层&#xff0c;尽管各平台的运行环境十分相似&#xff0c;但是还是有些许的区别&#xff08;如下图&#xff09;&#xff0c;比如说JavaScript 语法和 API 支持不一致&#xff0c;WXSS 渲染表现也有不同&#xff0c;所以不论是手工测试&#xff0c…

Spingboot3详解(全网最详细,新建springboot项目并详解各种组件的用法)

一.Spring Initializr创建向导 1.新建一个空项目 2.在新创建的空项目里&#xff0c;新建Module 3. 选择Spring Initializr 4.选择Spring Boot的版本3以上 5.创建好的一个项目结构 controller包是自己创建的 6.项目结构分析 spingboot主程序 package com.example.boot;impor…

使用hutool工具生成非对称加密公私密钥以及使用案例

1.导入hutool依赖 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.18</version></dependency>2.直接复制代码 package com.common.utils;import cn.hutool.core.codec.Base64; i…

mac苹果电脑清除数据软件CleanMyMac X4.16

在数字时代&#xff0c;保护个人隐私变得越来越重要。当我们出售个人使用的电脑&#xff0c;亦或者离职后需要上交电脑&#xff0c;都需要对存留在电脑的个人信息做彻底的清除。随着越来越多的人选择使用苹果电脑&#xff0c;很多人想要了解苹果电脑清除数据要怎样做才是最彻底…

优秀软件测试工程师必备的“8个能力”

首先要说&#xff0c;做软件测试不难&#xff0c;难的是做好软件测试。 结合自己这些年的工作经验&#xff0c;自己也总结出来8个方面的能力&#xff0c;可能有些方面感觉要求暂时还达不到&#xff0c;但这些确实是做软件测试工作所必备的能力&#xff0c;掌握了这8个方面的能力…

Python---time库

目录 时间获取 时间格式化 程序计时 time库包含三类函数&#xff1a; 时间获取&#xff1a;time() ctime() gmtime() 时间格式化&#xff1a;strtime() strptime() 程序计时&#xff1a;sleep() perf_counter() 下面逐一介绍&#…

【送书活动四期】被GitHub 要求强制开启 2FA 双重身份验证,我该怎么办?

记得是因为fork了OpenZeppelin/openzeppelin-contracts的项目&#xff0c;之后就被GitHub 要求强制开启 2FA 双重身份验证了&#xff0c;一拖再拖&#xff0c;再过几天帐户操作将受到限制了&#xff0c;只能去搞一下了 目录 2FA是什么为什么要开启 2FA 验证GitHub 欲在整个平台…

Html5响应式全开源网站建站源码系统 附带完整的搭建教程

Html5响应式全开源网站建站源码系统是基于Html5、CSS3和JavaScript等技术开发的全开源网站建站系统。它旨在为初学者和小型企业提供一套快速、简便的网站建设解决方案。该系统采用响应式设计&#xff0c;可以自适应不同设备的屏幕大小&#xff0c;提高用户体验。同时&#xff0…

柏林噪声C++

柏林噪声 随机噪声 如上图所示随机噪声没有任何规律可言&#xff0c;我们希望生成有一些意义的局部连续的随机图案 一维柏林噪声 假设希望生成一段局部连续的随机曲线&#xff0c;可以采用插值的方式&#xff1a;在固定点随机分配y值&#xff08;一般是整数点&#xff09;&a…

LeetCode力扣每日一题(Java):20、有效的括号

一、题目 二、解题思路 1、我的思路 我看到题目之后&#xff0c;想着这可能是力扣里唯一一道我能秒杀的题目了 于是一波操作猛如虎写出了如下代码 public boolean isValid(String s) {char[] c s.toCharArray();for(int i0;i<c.length;i){switch (c[i]){case (:if(c[i]…

Kotlin(十五) 高阶函数详解

高阶函数的定义 高阶函数和Lambda的关系是密不可分的。在之前的文章中&#xff0c;我们熟悉了Lambda编程的基础知识&#xff0c;并且掌握了一些与集合相关的函数式API的用法&#xff0c;如map、filter函数等。另外&#xff0c;我们也了解了Kotlin的标准函数&#xff0c;如run、…