您的位置:首页 > 移动开发 > Android开发

如何实现Siri中的波纹动画

2015-08-18 22:04 537 查看
苹果手机上的siri语音波纹动画很有意思,本文将带领大家研究如何实现生动画效果。

本文中代码是基于android的Java,其他平台上可以参考算法自行实现。本文假定读者会实现自定义View,不会的同学可以自行百度,文中将不会赘述。

绘制正弦波

Siri波纹的基本型是正弦波,所以首先我们要能够绘制一条正弦波。

一切以代码说话:

protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

float period = 2.0f;// 区域内,正弦波的周期

// 将绘图原点移动到区域中心
int width = getWidth();
int height = getHeight();
float midWidth = width / 2.0f;
float midHeight = height / 2.0f;
canvas.translate(midWidth, midHeight);

// 初始化画笔
Paint paint = new Paint();
paint.setStrokeWidth(1);// 画线宽度
paint.setStyle(Style.STROKE);//空心效果
paint.setAntiAlias(true);//抗锯齿
paint.setColor(Color.BLACK);

// 初始化线条
Path sinPath = new Path();
sinPath.moveTo(-midWidth, 0);

// 计算线条
for (float x = -midWidth; x < midWidth; x++) {
double sine = Math.sin(2 * Math.PI * period * (x / width));//计算该点上的正弦值
float y = (float) (midHeight * sine);// 将正弦值限定到绘图区的高度上
sinPath.lineTo(x, y);
}

canvas.drawPath(sinPath, paint);//绘制线条

canvas.restore();
}


简单起见,我把所有的有效代码都放到了View.onDraw方法里面了。

这里,关键代码是这两句

double sine = Math.sin(2 * Math.PI * period * (x / width));

float y = (float) (midHeight * sine);

结合上下文,这两句话计算了x轴上对应的y值,即:

y=midHeight∗sin(2π∗period∗xwidth)

其中,坐标原点是绘图区的中心;period是x坐标上的正弦周期数,这里是2。

下图是这段代码绘制出的线条效果(图片做了缩小处理,会有不连贯效果):



振幅的抛物线修正

Siri中,波纹中间和两边的波动振幅是不同的,明显是两边小,中间大。那么我们可以定义一个函数,使得波纹振幅按照该函数变化。这里我定义的是二次函数如下,当然也可以自行设计合适的函数比如半个周期的正弦波。

scaling=1−(xmidWidth)2

修改之后的关键代码如下,加粗的部分是新添加的内容:

double scaling = 1 - Math.pow(x / midWidth, 2);

double sine = Math.sin(2 * Math.PI * period * (x / width));

float y = (float) (midHeight * sine * scaling);

如此修正后的正弦波纹看起来就像siri中的样子了,为了更明显的表示scaling的作用,我将两条抛物线画在了上面。



附抛物线代码:

Path parabola1 = new Path();
parabola1.moveTo(-midWidth, 0);
Path parabola2 = new Path();
parabola2.moveTo(-midWidth, 0);

// 计算线条
for (float x = -midWidth; x < midWidth; x++) {
double scaling = 1 - Math.pow(x / midWidth, 2);
double sine = Math.sin(2 * Math.PI * period * (x / width));//计算该点上的正弦值
float y = (float) (midHeight * sine * scaling);// 将正弦值限定到绘图区的高度上
sinPath.lineTo(x, y);
parabola1.lineTo(x, (float) scaling * midHeight);
parabola2.lineTo(x, -(float) scaling * midHeight);
}

canvas.drawPath(sinPath, paint);//绘制正弦线canvas.drawPath(parabola1, paint);//绘制抛物线1
canvas.drawPath(parabola2, paint);//绘制抛物线2


副波纹

Siri的波纹由多个线条组成的,下面我们就来添加几条副波纹。副波纹的条数和相关属性可以依个人爱好自行调整,这里我添加4条副波纹,包括主波纹一共是5条正弦波纹。

首先我们来定义一下几条波纹的宽度。在之前的绘图中,定义的画笔宽度一直是一个像素,这里我们给他改一下。

这里我定义了每条波纹的宽度,并依据屏幕像素密度进行修改,以减小不同设备上的视觉区别

float[] waveWidth = {3, 2, 2, 1, 1};
DisplayMetrics metric = getResources().getDisplayMetrics();
float density = metric.density; // 屏幕密度
for (int i = 0; i < waveWidth.length; i++) {
waveWidth[i] = waveWidth[i] * density / 2;
}


接下来定义一下波纹的透明度,透明度可以使波纹看起来有层次感^_^。

float[] waveAlpha = {1.0f, 0.9f, 0.7f, 0.4f, 0.2f};


接下来要修改副波纹的振幅,这样几条波纹才会区别开来

float[] waveAmplitude = {1.0f, 0.7f, 0.4f, 0.1f, -0.2f};


下面,我要把这些属性都应用到几条波纹上:

for (int i = 0; i < waveWidth.length; i++) {
paint.setStrokeWidth(waveWidth[i]);//画笔宽度
paint.setAlpha((int) (waveAlpha[i] * 255));//画笔透明度

sinPath.reset();//重置线条
sinPath.moveTo(-midWidth, 0);

// 计算线条
for (float x = -midWidth; x < midWidth; x++) {
double scaling = 1 - Math.pow(1 / midWidth * x, 2);
double sine = Math.sin(2 * Math.PI * period * (x / width));//计算该点上的正弦值
float y = (float) (midHeight// 将正弦值限定到绘图区的高度上
* sine   // 正弦值
* scaling// 振幅修正 - 距离中心越远,振幅越小
* waveAmplitude[i]// 副波纹振幅修正
);
sinPath.lineTo(x, y);
}

canvas.drawPath(sinPath, paint);//绘制线条
}


这段代码的绘制效果如图:



现在有点样子了,不过几条波纹在 x 轴上有个很明显的焦点,想当难看,我们给几条副波纹来个偏移

int[] wavePhase = {0, 6, -9, 15, -21};



double sine = Math.sin(2 * Math.PI * period * ((x + wavePhase[i]) / width));//计算该点上的正弦值

现在的效果就成这个样子了:



波纹移动

接下来就是让波纹移动了。很简单,在绘制正弦波纹的时候添加一个x轴上的偏移即可。

比如我们可以定义这样一个方法让波纹右移 n 个像素:

private float phase;
public void nextPhase(float n) {
phase -= n;
invalidate();
}


然后在绘图时,将 phase 添加到偏移里面:

double sine = Math.sin(2 * Math.PI * period * ((x + phase + wavePhase[i]) / width));//计算该点上的正弦值

做成动画的效果就是这样的:



按照音量调整振幅

Siri语音动画效果是依据环境音量调整振幅的,不过对此本文就不多做介绍了,感兴趣的同学可以去研究一下源码: Siri动画完整源码文件(Android版)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Siri动画 android