当前位置: 移动技术网 > IT编程>移动开发>Android > 一步步教你写Slack的Loading动画

一步步教你写Slack的Loading动画

2019年07月24日  | 移动技术网IT编程  | 我要评论

农村电视剧全集,广州公车查询,李铭筱

项目地址:https://github.com/jeasonwong/slackloadingview

老规矩,先上效果。

图好大。。

说下第一眼看到这个动画后的思路:

+两根平行线,要用到直线方程 y=kx+b
+另外两根平行线,与之前两根平行线的斜率相乘为-1,即k1*k2=-1
+线条做圆周运动就是k值的不断变化
+然后就是简单的线条长度变化

我相信很多人第一眼会和我有类似的思路,但是当我上了个厕所后意识到我想复杂了~

说下上完厕所后的思路:

不要想着线条是斜的,就是一个普通的线段,一个lineto搞定(startx和stopx一样,仅y不同)
线条的垂直更容易,直接canvas翻转(转过后再转回)
整个动画的圆周运动也是canvas翻转(转过后不转回)
线条的单度变化依然用属性动画(这是必须的。。)
动画开始前就让整个canvas旋转
这样一来就太容易了。

我把动画分成了四步:

画布旋转及线条变化动画(canvas rotate line change)
画布旋转动画(canvas rotate)
画布旋转圆圈变化动画(canvas rotate circle change)
线条变化动画(line change)

详细说明前先介绍下成员变量和一些初始化

成员变量

//静止状态
 private final int status_still = 0;
 //加载状态
 private final int status_loading = 1;
 //线条最大长度
 private final int max_line_length = dp2px(getcontext(), 120);
 //线条最短长度
 private final int min_line_length = dp2px(getcontext(), 40);
 //最大间隔时长
 private final int max_duration = 3000;
 //最小间隔时长
 private final int min_duration = 500;

 private paint mpaint;
 private int[] mcolors = new int[]{0xb07ecbda, 0xb0e6a92c, 0xb0d6014d, 0xb05aba94};
 private int mwidth, mheight;
 //动画间隔时长
 private int mduration = min_duration;
 //线条总长度
 private int mentirelinelength = min_line_length;
 //圆半径
 private int mcircleradius;
 //所有动画
 private list<animator> manimlist = new arraylist<>();
 //canvas起始旋转角度
 private final int canvas_rotate_angle = 60;
 //动画当前状态
 private int mstatus = status_still;
 //canvas旋转角度
 private int mcanvasangle;
 //线条长度
 private float mlinelength;
 //半圆y轴位置
 private float mcircley;
 //第几部动画
 private int mstep;

初始化

 private void initview() {
  mpaint = new paint();
  mpaint.setantialias(true);
  mpaint.setcolor(mcolors[0]);
 }

 private void initdata() {
  mcanvasangle = canvas_rotate_angle;
  mlinelength = mentirelinelength;
  mcircleradius = mentirelinelength / 5;
  mpaint.setstrokewidth(mcircleradius * 2);
  mstep = 0;
 }

一、画布旋转及线条变化动画(canvas rotate line change)

 /**
  * animation1
  * 动画1
  * canvas rotate line change
  * 画布旋转及线条变化动画
  */
 private void startcrlcanim() {

  collection<animator> animlist = new arraylist<>();

  valueanimator canvasrotateanim = valueanimator.ofint(canvas_rotate_angle + 0, canvas_rotate_angle + 360);
  canvasrotateanim.addupdatelistener(new valueanimator.animatorupdatelistener() {
   @override
   public void onanimationupdate(valueanimator animation) {
    mcanvasangle = (int) animation.getanimatedvalue();
   }
  });

  animlist.add(canvasrotateanim);

  valueanimator linewidthanim = valueanimator.offloat(mentirelinelength, -mentirelinelength);
  linewidthanim.addupdatelistener(new valueanimator.animatorupdatelistener() {
   @override
   public void onanimationupdate(valueanimator animation) {
    mlinelength = (float) animation.getanimatedvalue();
    invalidate();
   }
  });

  animlist.add(linewidthanim);

  animatorset animationset = new animatorset();
  animationset.setduration(mduration);
  animationset.playtogether(animlist);
  animationset.setinterpolator(new linearinterpolator());
  animationset.addlistener(new animatorlistener() {
   @override
   public void onanimationend(animator animation) {
    log.d("@=>", "动画1结束");
    if (mstatus == status_loading) {
     mstep++;
     startcranim();
    }
   }
  });
  animationset.start();

  manimlist.add(animationset);
 }

第一步动画涉及到两个动画同时进行,所以使用了animatorset,这个类很强大,可以让n个动画同时进行(playtogether),也可以让n个动画顺序执行(playsequentially)。

说到这里,其实我的四个动画就是顺序进行的,但是每个动画里又有同时进行的动画,为了讲解方便,我是监听了onanimationend来控制动画执行顺序,其实可以直接使用playsequentially。

上方动画就干了两件事:

1、旋转画布,从canvas_rotate_angle + 0转到canvas_rotate_angle + 360,canvas_rotate_angle是画布初始倾斜角度

2、线条长度变化,从mentirelinelength到-mentirelinelength。

对应的ondraw方法:

 @override
 protected void ondraw(canvas canvas) {
  super.ondraw(canvas);
  switch (mstep % 4) {
   case 0:
    for (int i = 0; i < mcolors.length; i++) {
     mpaint.setcolor(mcolors[i]);
     drawcrlc(canvas, mwidth / 2 - mentirelinelength / 2.2f, mheight / 2 - mlinelength, mwidth / 2 - mentirelinelength / 2.2f, mheight / 2 + mentirelinelength, mpaint, mcanvasangle + i * 90);
    }
    break;
   ...
  }

 }

 ...

 private void drawcrlc(canvas canvas, float startx, float starty, float stopx, float stopy, @nonnull paint paint, int rotate) {
  canvas.rotate(rotate, mwidth / 2, mheight / 2);
  canvas.drawarc(new rectf(startx - mcircleradius, starty - mcircleradius, startx + mcircleradius, starty + mcircleradius), 180, 180, true, mpaint);
  canvas.drawline(startx, starty, stopx, stopy, paint);
  canvas.drawarc(new rectf(stopx - mcircleradius, stopy - mcircleradius, stopx + mcircleradius, stopy + mcircleradius), 0, 180, true, mpaint);
  canvas.rotate(-rotate, mwidth / 2, mheight / 2);
 }

是不是很机智,drawcrlc做了三件事:

1、画布旋转后又旋转回来

2、画半圆(为什么要画半圆?不画整个圆?这里留个思考题。)

3、画线条

这样动画1就完成了。

二、画布旋转动画(canvas rotate)

 /**
  * animation2
  * 动画2
  * canvas rotate
  * 画布旋转动画
  */
 private void startcranim() {
  valueanimator canvasrotateanim = valueanimator.ofint(mcanvasangle, mcanvasangle + 180);
  canvasrotateanim.setduration(mduration / 2);
  canvasrotateanim.setinterpolator(new linearinterpolator());
  canvasrotateanim.addupdatelistener(new valueanimator.animatorupdatelistener() {
   @override
   public void onanimationupdate(valueanimator animation) {
    mcanvasangle = (int) animation.getanimatedvalue();
    invalidate();
   }
  });
  canvasrotateanim.addlistener(new animatorlistener() {
   @override
   public void onanimationend(animator animation) {
    log.d("@=>", "动画2结束");
    if (mstatus == status_loading) {
     mstep++;
     startcrccanim();
    }
   }
  });
  canvasrotateanim.start();

  manimlist.add(canvasrotateanim);
 }

 ...

 @override
 protected void ondraw(canvas canvas) {
  super.ondraw(canvas);
  switch (mstep % 4) {
   ...
   case 1:
    for (int i = 0; i < mcolors.length; i++) {
     mpaint.setcolor(mcolors[i]);
     drawcr(canvas, mwidth / 2 - mentirelinelength / 2.2f, mheight / 2 + mentirelinelength, mpaint, mcanvasangle + i * 90);
    }
    break;
   ...
  }

 }

 ...

 private void drawcr(canvas canvas, float x, float y, @nonnull paint paint, int rotate) {
  canvas.rotate(rotate, mwidth / 2, mheight / 2);
  canvas.drawcircle(x, y, mcircleradius, paint);
  canvas.rotate(-rotate, mwidth / 2, mheight / 2);
 }

有了动画1的底子,那这个就太容易了,只是简单的旋转canvas。

三、画布旋转圆圈变化动画(canvas rotate circle change)

 /**
  * animation3
  * 动画3
  * canvas rotate circle change
  * 画布旋转圆圈变化动画
  */
 private void startcrccanim() {
  collection<animator> animlist = new arraylist<>();

  valueanimator canvasrotateanim = valueanimator.ofint(mcanvasangle, mcanvasangle + 90, mcanvasangle + 180);
  canvasrotateanim.addupdatelistener(new valueanimator.animatorupdatelistener() {
   @override
   public void onanimationupdate(valueanimator animation) {
    mcanvasangle = (int) animation.getanimatedvalue();
   }
  });

  animlist.add(canvasrotateanim);

  valueanimator circleyanim = valueanimator.offloat(mentirelinelength, mentirelinelength / 4, mentirelinelength);
  circleyanim.addupdatelistener(new valueanimator.animatorupdatelistener() {
   @override
   public void onanimationupdate(valueanimator animation) {
    mcircley = (float) animation.getanimatedvalue();
    invalidate();
   }
  });

  animlist.add(circleyanim);

  animatorset animationset = new animatorset();
  animationset.setduration(mduration);
  animationset.playtogether(animlist);
  animationset.setinterpolator(new linearinterpolator());
  animationset.addlistener(new animatorlistener() {
   @override
   public void onanimationend(animator animation) {
    log.d("@=>", "动画3结束");
    if (mstatus == status_loading) {
     mstep++;
     startlcanim();
    }
   }
  });
  animationset.start();

  manimlist.add(animationset);
 }

 ...

 @override
 protected void ondraw(canvas canvas) {
  super.ondraw(canvas);
  switch (mstep % 4) {
   ...
   case 2:
    for (int i = 0; i < mcolors.length; i++) {
     mpaint.setcolor(mcolors[i]);
     drawcrcc(canvas, mwidth / 2 - mentirelinelength / 2.2f, mheight / 2 + mcircley, mpaint, mcanvasangle + i * 90);
    }
    break;
   ...
  }

 }

 ...

 private void drawcrcc(canvas canvas, float x, float y, @nonnull paint paint, int rotate) {
  canvas.rotate(rotate, mwidth / 2, mheight / 2);
  canvas.drawcircle(x, y, mcircleradius, paint);
  canvas.rotate(-rotate, mwidth / 2, mheight / 2);
 }

动画3做了两件事:

1、旋转canvas

2、变化circle的y坐标,达到往里缩的效果

四、线条变化动画(line change)

 /**
  * animation4
  * 动画4
  * line change
  * 线条变化动画
  */
 private void startlcanim() {
  valueanimator linewidthanim = valueanimator.offloat(mentirelinelength, -mentirelinelength);
  linewidthanim.setduration(mduration);
  linewidthanim.setinterpolator(new linearinterpolator());
  linewidthanim.addupdatelistener(new valueanimator.animatorupdatelistener() {
   @override
   public void onanimationupdate(valueanimator animation) {
    mlinelength = (float) animation.getanimatedvalue();
    invalidate();
   }
  });
  linewidthanim.addlistener(new animatorlistener() {
   @override
   public void onanimationend(animator animation) {
    log.d("@=>", "动画4结束");
    if (mstatus == status_loading) {
     mstep++;
     startcrlcanim();
    }
   }
  });
  linewidthanim.start();

  manimlist.add(linewidthanim);
 }

 ...

 @override
 protected void ondraw(canvas canvas) {
  super.ondraw(canvas);
  switch (mstep % 4) {
   ...
   case 3:
    for (int i = 0; i < mcolors.length; i++) {
     mpaint.setcolor(mcolors[i]);
     drawlc(canvas, mwidth / 2 - mentirelinelength / 2.2f, mheight / 2 + mentirelinelength, mwidth / 2 - mentirelinelength / 2.2f, mheight / 2 + mlinelength, mpaint, mcanvasangle + i * 90);
    }
    break;
  }

 }

 ...

 private void drawlc(canvas canvas, float startx, float starty, float stopx, float stopy, @nonnull paint paint, int rotate) {
  canvas.rotate(rotate, mwidth / 2, mheight / 2);
  canvas.drawarc(new rectf(startx - mcircleradius, starty - mcircleradius, startx + mcircleradius, starty + mcircleradius), 0, 180, true, mpaint);
  canvas.drawline(startx, starty, stopx, stopy, paint);
  canvas.drawarc(new rectf(stopx - mcircleradius, stopy - mcircleradius, stopx + mcircleradius, stopy + mcircleradius), 180, 180, true, mpaint);
  canvas.rotate(-rotate, mwidth / 2, mheight / 2);
 }

动画4只做了线条的变化。

这样整个slack的loading动画就完成了,是不是很简单。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网