前言
最近想搞个酷炫一点的页面,觉得渐变挺不错,决定背景采用渐变了。
但是单纯的静态的渐变还是感觉差点意思,思来想去觉得得搞个动态渐变。
下面是我的实现思路 关于基础的静态渐变,这里不愿意赘述,贴一张官方的链接在这吧,链接是 官方文档-渐变样式。简单而且详细。
说明:以下均以线性渐变举例,变量命名因为是演示所以比较随意。
最开始我的方案是用那个 属性动画,如果说你想实现的效果比较简单。比方说如下,那么是很合适的。

但如果你要实现的比较复杂,例如下面这样

虽然用属性动画仍旧可以大致还原,但是我觉得 关键帧动画这时候可能就更有优势了。
这里也不赘述了,点一下链接可以看到文档。
属性动画实现
先定义两个控制颜色的变量
css
@State gradientOptions : LinearGradientOptions = {
angle:45,
colors:[
["#f09819", 0],
["#edde5d", 1],
]
}
@State flag: number = 1
然后我们在onDidBuild回调里启动动画,具体就是改一下我们定义的gradientOptions的值,就能触发动画了
css
onDidBuild():{
this.gradientOptions = {
angle:45,
colors:[
["#ffee113e", 0],
["#ff1a3864", 1]
]
}
}
下面是组件信息,我们可以通过变量flag来实现更多更丰富的渐变效果,这里不作演示
kotlin
Column()
.width(200)
.height(200)
.linearGradient(this.gradientOptions)
.animation({duration:2000,onFinish:()=>{
if(this.flag){
this.flag = 0
this.gradientOptions =
{
angle:45,
colors:[
["#edde5d", 0],
["#f09819", 1],
]
}
}else {
this.flag = 1
this.gradientOptions = {
angle:45,
colors:[
["#ffee113e", 0],
["#ff1a3864", 1],
]
}
}
}})
这样基本就可以完成我们第一个渐变的效果了。
但是很重要的一个缺点就是,换方向的话是不会有动画的。
我们前面主要是两种颜色变化,并且角度都是45度,如果我们把第二个颜色的角度改成90度,也就是
ini
this.flag = 1
this.gradientOptions = {
angle:90,
colors:[
["#ffee113e", 0],
["#ff1a3864", 1],
]
}
那么就会很不连贯了, 像下面这样,感觉就是属性动画处理不了这样的转变,也不清楚算不算bug。

总之就是,用属性动画的话,就几乎改变不了渐变的方向了,不能提供更为多元的视觉效果。
当然,硬要用属性动画,通过很复杂的调试应该也能做到看起来连贯吧。
关键帧动画实现
实际上这个似乎才是主流的动态渐变背景的实现方案。
大致就是一个框(组件)固定,但是背景图来回移动,从而达到渐变的效果。
例如我前面那个举例的很复杂的例子,实际上是怎样运行的呢?

这里是为了演示所以把周围的显露出来了,并且加了个border,之后加上clip属性并把border删了就可以达到之前的效果了。
具体的代码实现如下
我们定义位置变量pos
css
@State pos : Position = {x: "0%",y:"0%"}
组件信息
sql
Column(){
Column()
.position(this.pos)
.width("500%")
.height("500%")
.linearGradient({
angle:60,
colors:[
["#ffed5d8b", 0],
["#ffb7f019", 0.2],
["#ffae8215", 0.4],
["#ff354bb8", 0.6],
["#ffd3ed5d", 0.8],
["#ff590f86", 1],
]
})
}
.width(200)
.height(200)
.clip(true)
然后最重要的是我们的关键帧动画函数ani
kotlin
ani(){
this.uiContext?.keyframeAnimateTo({iterations:-1},
[
{duration:1,event:()=>{
this.pos.x = "0%"
this.pos.y = "-400%"
}
},
{duration:3000,event:()=>{
this.pos.x = "0%"
this.pos.y = "0%"
}
},
{
duration:3000,event:()=>{
this.pos.x = "-400%"
this.pos.y = "0%"
},
}, {
duration:3000,event:()=>{
this.pos.x = "-400%"
this.pos.y = "-400%"
},
},
{
duration:3000,event:()=>{
this.pos.x = "0%"
this.pos.y = "-400%"
},
}, {
duration:4242,event:()=>{
this.pos.x = "-400%"
this.pos.y = "0%"
}
}, {
duration:3000,event:()=>{
this.pos.x = "0%"
this.pos.y = "0%"
}
}, {
duration:4242,event:()=>{
this.pos.x = "-400%"
this.pos.y = "-400%"
},
}, {
duration:3000,event:()=>{
this.pos.x = "0%"
this.pos.y = "-400%"
},
},
])
}
这里解释一下这个函数啊,这个_iteration: -1_ 指的是无限次播放重复播放这个动画。
然后这里的一次循环的具体的路径参考下图。

关于duration和pos的具体设置,不展开讲了,否则偏离主题了。这部分也完全是个性化的,这里仅供参考。
最后在onDidBuild回调里执行我们的关键帧动画函数就好了。
scss
onDidBuild():{
this.ani()
}
后话
最初的目的是想要完全随机的背景动态变化,但是实际上用关键帧之后,看不出来是不是固定的背景的吧,而且一般估计没人盯着你的背景看是不是重复播放。效果上估计也都是差不多的,完全随机还不可控,说不定会随机出一个很难看的渐变色。虽然可以限定颜色范围。
实现的话也不是没有思路,大概就是在关键帧动画里最后的一步里加个animateTo函数,然后把组件的linearGradient属性的值改成变量,在animateTo函数里修改,应该就行了。