如何动画渐变? [英] How to animate gradient?
问题描述
如何设置从颜色#1 到颜色#2 的渐变动画?类似的东西
我打算将它用作单位的健康栏(因此,它将是一个以绿色开始并以红色结束的动画)
在谷歌搜索时,我发现了 2 种适用于 android 的方法:
诀窍是创建 LinearGradient
,它比 View.getWidth()
大 colorsCount
倍.之后,您可以在绘制渐变时使用 canvas.translate()
来更改其颜色,因此在 onDraw()
处没有 new
调用全部.
要创建渐变,您需要当前的宽度和高度.我在 onSizeChanged()
中做到了.我也在这里设置了 Shader
.
@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);宽度 = getWidth();高度 = getHeight();LinearGradient 梯度 = 新的 LinearGradient(0,高度/2,宽度*colors.length - 1,高度/2,颜色,空,Shader.TileMode.REPEAT);fillPaint.setShader(渐变);shapePath = getParallelogrammPath(width, height,sidesGap);shapeBorderPath = getParallelogrammPath(width, height,sidesGap);}
我使用路径是因为平行四边形视图,你可以使用任何你想要的.在实现绘图时,您应该注意两件事:您需要在当前偏移上translate()
整个 canvas
和 offset()
您的填充形状:
@Overrideprotected void onDraw(Canvas canvas) {画布.save();canvas.translate(-gradientOffset, 0);shapePath.offset(gradientOffset, 0f, tempPath);canvas.drawPath(tempPath, fillPaint);canvas.restore();canvas.drawPath(shapeBorderPath, borderPaint);super.onDraw(画布);//我的View是FrameLayout,所以需要在之后调用}
你也应该使用 canvas.save()
&canvas.restore()
.它将保存画布内部矩阵堆叠并相应地恢复它.
因此,您需要做的最后一件事是为 gradientOffset
设置动画.你可以使用你想要的一切,比如 ObjectAnimator(属性动画).我使用了 TimeAnimator,因为我需要控制 updateTickcode> 并直接开始偏移.这是我的认识(有点困难和苛刻):
static public final int LIFETIME_DEAFULT = 2300;私人长寿命 = LIFETIME_DEAFULT,updateTickMs = 25,timeElapsed = 0;私人长累加器Ms = 0;私人浮动梯度偏移 = 0f;公共无效 startGradientAnimation() {停止渐变动画();resolveTimeElapsed();final float gradientOffsetCoef = (float) (updateTickMs)/life;final int colorCount = this.colors.length - 1;gradientAnimation.setTimeListener(new TimeAnimator.TimeListener() {@覆盖public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {最终长梯度宽度 = 宽度 * 颜色计数;if (totalTime > (lifetime - timeElapsed)) {动画.取消();渐变偏移 = 渐变宽度;无效();} 别的 {accumulatorMs += deltaTime;最终长gradientOffsetsCount = accumulatorMs/updateTickMs;gradientOffset += (gradientOffsetsCount * gradientWidth) * gradientOffsetCoef;accumulatorMs %= updateTickMs;boolean gradientOffsetChanged = (gradientOffsetsCount > 0) ?真假;if (gradientOffsetChanged) {无效();}}}});gradientAnimation.start();}
完整的查看
代码,您可以在这里
How to animate gradient from color#1 to color#2? Something similar to
I'm planning to use it as health-bar for unit (so, it will be finit animation starting with green and ending with red)
While googling it, I found 2 ways to do it for android: use ShaderFactory or extends View, using new Shader(new LinearGradient())
. Both answers do the same - calling new Shader()
every View.onDraw(Canvas canvas)
method's call. Its really expensive if number of such animated gradients more than ~3.
So I did it another way. I avoided calling new
every onDraw()
, using single precalculated LinearGradient
. That's how it is look like (gif, so animation decayed) :
The trick is to create LinearGradient
which is colorsCount
times larger than View.getWidth()
. After that you can use canvas.translate()
, while drawing gradient, to change its color, so no new
calls in onDraw()
at all.
To create gradient, you need current width and height. I did it in onSizeChanged()
. Also I set Shader
here too.
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = getWidth();
height = getHeight();
LinearGradient gradient = new LinearGradient(
0, height / 2, width * colors.length - 1, height / 2,
colors, null, Shader.TileMode.REPEAT);
fillPaint.setShader(gradient);
shapePath = getParallelogrammPath(width, height, sidesGap);
shapeBorderPath = getParallelogrammPath(width, height, sidesGap);
}
I use path because of parallelogramy views, you can use whatever you want. When implementing drawing, you should notice 2 things: you need to translate()
whole canvas
on current offset and offset()
your filling shape:
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.translate(-gradientOffset, 0);
shapePath.offset(gradientOffset, 0f, tempPath);
canvas.drawPath(tempPath, fillPaint);
canvas.restore();
canvas.drawPath(shapeBorderPath, borderPaint);
super.onDraw(canvas); // my View is FrameLayout, so need to call it after
}
Also you should use canvas.save()
& canvas.restore()
. It will save inner matrix of canvas to stack and restore it correspondingly.
So the last what you need to do is to animate gradientOffset
. You can use everything you want like ObjectAnimator (Property Animation). I used TimeAnimator, because I needed to control updateTick
and starting offset directly. Here is my realisation (a bit difficult and harsh):
static public final int LIFETIME_DEAFULT = 2300;
private long lifetime = LIFETIME_DEAFULT, updateTickMs = 25, timeElapsed = 0;
private long accumulatorMs = 0;
private float gradientOffset = 0f;
public void startGradientAnimation() {
stopGradientAnimation();
resolveTimeElapsed();
final float gradientOffsetCoef = (float) (updateTickMs) / lifetime;
final int colorsCount = this.colors.length - 1;
gradientAnimation.setTimeListener(new TimeAnimator.TimeListener() {
@Override
public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
final long gradientWidth = width * colorsCount;
if (totalTime > (lifetime - timeElapsed)) {
animation.cancel();
gradientOffset = gradientWidth;
invalidate();
} else {
accumulatorMs += deltaTime;
final long gradientOffsetsCount = accumulatorMs / updateTickMs;
gradientOffset += (gradientOffsetsCount * gradientWidth) * gradientOffsetCoef;
accumulatorMs %= updateTickMs;
boolean gradientOffsetChanged = (gradientOffsetsCount > 0) ? true : false;
if (gradientOffsetChanged) {
invalidate();
}
}
}
});
gradientAnimation.start();
}
The full View
code you can find here
这篇关于如何动画渐变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!