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

news2024/11/19 15:34:49

前面我们聊过 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 就全部讲完了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

友思特 Neuro-R:快速部署实时推理API

科技赋能&#xff0c;视觉技术在缺陷检测方面的应用为生产制造自动化程度带来了重大影响&#xff0c;有效缩短了检测时间&#xff0c;提升了生产效率&#xff0c;创造出更多的经济效益。通过视觉技术的精准分析和快速判断&#xff0c;缺陷检测变得更加准确、高效&#xff0c;无…

Java二十一章 网络通信

1 网络程序设计基础 网络程序设计编写的是与其他计算机进行通信的程序。 局域网与互联网 服务器是指提供信息的计算机或程序&#xff0c;客户机是指请求信息的计算机或程序。网络用于连接服务器与客户机&#xff0c;实现两者间的相互通信。 网络协议 网络协议规定了计算机…

echarts绘制一个柱状折线图

其他echarts&#xff1a; echarts绘制一个环形图 echarts绘制一个饼图 echarts绘制一个环形图2 效果&#xff1a; 代码&#xff1a; <template><div class"wrapper"><!-- 柱状图 --><div v-if"type new_bar" ref"barChar…

2023最新八股文前端面试题

第一章 Css 1.说一下CSS的盒模型。 在HTML页面中的所有元素都可以看成是一个盒子盒子的组成:内容content、内边距padding、边框border、外边距margin盒模型的类型: 标准盒模型 margin border padding content IE盒模型 margin content(border padding) 控制盒模型的模式…

如何选择靠谱的软件测试外包公司?CMA、CNAS软件测试报告获取

作为信息科技产业的代表之一&#xff0c;软件公司受到了越来越多的关注&#xff0c;它们的发展为我国的科技创新提供了强大的战略支撑。软件测试作为提升软件产品质量的后盾&#xff0c;日益成为一个专业化、标准化和规范化的行业&#xff0c;软件测试外包公司就是这种背景下成…

每日汇评:由于非农就业数据可能低迷,黄金恐再次测试2050美元上方

周五早间&#xff0c;金价在2,030美元附近扩大区间&#xff1b; 美元正在企稳&#xff0c;美债收益率坚持复苏收益&#xff1b; 由于美国就业大概率疲软&#xff0c;黄金日线图倾向于看涨&#xff1b; 周五早间&#xff0c;金价连续第四个交易日延续区间波动走势&#xff0c;因…

员工持股平台模式有哪几种?

员工持股平台模式 目前在现有的市场环境下持股平台的模式主要有公司型的持股平台以及有限合伙企业的持股平台。 &#xff08;一&#xff09;公司型员工持股平台 设立公司型的员工持股平台的唯一目的是为了让平台公司受让母公司的股权&#xff0c;从而实现员工间接持有母公司股权…

外贸行业的CRM系统和其它CRM有什么区别?

外贸行业对客户管理的追求日益提高&#xff0c;为了应对客户需求的变化和多元性&#xff0c;外贸企业需要借助CRM管理系统实现智能管理。下面&#xff0c;我们将详细探讨外贸CRM的概念、特点和具体应用。 什么是外贸CRM&#xff1f; 外贸CRM是指针对外贸行业的客户关系管理系…

3-Mybatis

文章目录 Mybatis概述什么是Mybatis&#xff1f;Mybatis导入知识补充数据持久化持久层 第一个Mybatis程序&#xff1a;数据的增删改查查创建环境编写代码1、目录结构2、核心配置文件&#xff1a;resources/mybatis-config.xml3、mybatis工具类&#xff1a;Utils/MybatisUtils4、…

电商行业邮件营销全攻略:解决方案与实施技巧

在电商行业这样多元化且快节奏的行业中&#xff0c;有效、及时的沟通则是其业务发展的基础之一。您需要在客户生命周期的各个阶段与他们进行交互&#xff0c;需要在恰当的时机推送他们需要的信息&#xff0c;更重要的是&#xff0c;您需要根据客户的购买经历迅速制定营销方案&a…

STL(五)(queue篇)

我发现之前一版在电脑上看 常用函数部分 没有问题,由于是手打上去的,在手机上看会发生错位问题,现已将电脑原版 常用函数部分 截图改为图片形式,不会再发生错位问题,非常感谢大家的支持 ### priority_queue优先队列出现频率非常高,尤为重要(是一定要掌握的数据结构) 1.queue队…

vmware虚拟机17 安装macos14过程及问题处理亲测

前期准备 1、可引导可虚拟机安装的macOS Sonoma 14 ISO镜像安装文件 我找到得地址&#xff0c;下载自行解决啦 2、VMware虚拟机应用软件 官网下载就好&#xff0c;搜个码搞定 3、解锁工具macOS Unlocker 开始安装&#xff1a; 1、打开VMware软件&#xff0c;新建一个系统…

[MySQL] MySQL复合查询(多表查询、子查询)

前面我们学习了MySQL简单的单表查询。但是我们发现&#xff0c;在很多情况下单表查询并不能很好的满足我们的查询需求。本篇文章会重点讲解MySQL中的多表查询、子查询和一些复杂查询。希望本篇文章会对你有所帮助。 文章目录 一、基本查询回顾 二、多表查询 2、1 笛卡尔积 2、2…

【Vulnhub 靶场】【Funbox: GaoKao】【简单】【20210606】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/funbox-gaokao,707/ 靶场下载&#xff1a;https://download.vulnhub.com/funbox/FunboxGaoKao.ova 靶场难度&#xff1a;简单 发布日期&#xff1a;2021年06月06日 文件大小&#xff1a;1.3 GB 靶场作者&#…

虚拟化之指令的Trap和仿真

有时,虚拟机监控程序需要在虚拟机(VM)中模拟操作。例如,VM内的软件可能尝试配置与功耗管理或缓存一致性相关的低级处理器控件。通常,您不希望将VM直接访问这些控件,因为它们可能被用于突破隔离,或影响系统中的其他VM。 trap在执行给定操作(例如读取寄存器)时引发异常…

Python零基础入门之详解sort排序使用

文章目录 1.前言2.环境准备3.程序实现4.sort拓展关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠道 1.前言 昨天一…

IBL环境贴图原理及着色器实现【基于图像的照明】

IBL - Image Based Lighting - 也就是基于图像的照明&#xff0c;是一组照亮物体的技术&#xff0c;不是像上一章那样通过直接分析光&#xff0c;而是将周围环境视为一个大光源。 这通常是通过操作立方体贴图环境贴图&#xff08;取自现实世界或从 3D 场景生成&#xff09;来完…

vuepress-----13、分割config

13、分割config config.js const headConfig require(./config/headConfig); const pluginsConfig require(./config/pluginsConfig); const themeConfig require(./config/themeConfig)module.exports {title: "小邵子",description: 小邵子的个人笔记,head: he…

openEuler JDK21 部署 Zookeeper 集群

zookeeper-jdk21 操作系统&#xff1a;openEuler JDK&#xff1a;21 主机名IP地址spark01192.168.171.101spark02192.168.171.102spark03192.168.171.103 安装 1. 升级内核和软件 yum -y update2. 安装常用软件 yum -y install gcc gcc-c autoconf automake cmake make \zl…

tomcat篇---第一篇

系列文章目录 文章目录 系列文章目录前言一、Tomcat的缺省端口是多少,怎么修改?二、tomcat 有哪几种Connector 运行模式(优化)?三、Tomcat有几种部署方式?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文…