Android 使用 Path 实现搜索动态加载动画效果

今天实现一个搜索动态加载数据的动画效果,还是先看效果吧,用文字描述干巴巴的,看图说话什么都明白了,

实现这个就是使用Path中的getSegment()不断的去改变它截取片段的start和stop,再结合动画,今天就分步骤实现它,看完以后你也会觉的不是很难,只是没想到这么实现而已,所以要多见识,所谓眼界决定你的高度,还是延续我写博客的习惯,一步步分析,第一步就是绘制如下图:

如果单纯的绘制这个图很简单很简单的,绘制一个圆,然后再绘制一根线就搞定,但是要考虑这里的效果,就不能这么干了,如果你看了上面的gif图就知道,其实这是2个同心圆,然后前一个path的起点和后一个path的起点相连接就是形成一条直线了,但是path中的图形内容也就是这个圆是怎么绘制出来的呢?如果是绘制圆的话,上面的线起点和终点位置怎么去计算,这是个问题,但是我们绘制圆还可以使用绘制椭圆的形式也是可以绘制达到圆的效果,从45度开始绘制一个圆,是不是这个线的起点搞定了,分析图如下:

那么好,根据上面的分析开始写代码绘制出一个静态的搜索图:

package com.tuya;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**
 * Created by admin on 2016/12/17.
 */
public class DynamicSearchView2 extends View {
 private Paint paint;
 private int width;//view的宽度
 private int height;//view的高度
 private Path searchPath;
 private Path circlePath;
 private float BigCircleRectWidth;//搜索圆对应的外切正方形边长
 private PathMeasure pathMeasure;
 private float[] pos;
 public DynamicSearchView2(Context context) {
  this(context,null);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs) {
  this(context, attrs,0);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 private void init() {
  paint = new Paint();
  paint.setAntiAlias(true);
  paint.setStrokeWidth(3);
  paint.setColor(Color.WHITE);
  paint.setStyle(Paint.Style.STROKE);
 }
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  width = w;
  height = h;
  initPath();
 }
 /**
  * 初始化path
  */
 private void initPath() {
  searchPath = new Path();
  circlePath = new Path();
  if(width>height){//长方形
   BigCircleRectWidth = height;
  }else if(width<height){
   BigCircleRectWidth = width;
  }else{
   BigCircleRectWidth = width;
  }
  float smallbordWidth =BigCircleRectWidth/8;
  RectF searchRect = new RectF(-smallbordWidth,-smallbordWidth,smallbordWidth,smallbordWidth);
  searchPath.addArc(searchRect,45,360);
  float bigBordWidth = smallbordWidth*2;
  RectF circleRect = new RectF(-bigBordWidth,-bigBordWidth,bigBordWidth,bigBordWidth);
  circlePath.addArc(circleRect,45,-360);
  pathMeasure = new PathMeasure(circlePath,false);
  pos = new float[2];
  pathMeasure.getPosTan(0,pos,null);
  searchPath.lineTo(pos[0],pos[1]);
 }
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  canvas.translate(width/2,height/2);//平移画布把这个view的中心点当做原点
  canvas.drawPath(searchPath,paint);
  canvas.drawPath(circlePath,paint);
 }
}

效果图:

本来这个外圆是不需要draw上去的,我在这绘制上去只是告诉你这二个圆是有一定的联系,哪为什么这根线是这样的呢?我们在绘制这个圆的时候是从45度开始绘制360刚好是一周,形成了一个圆,现在做个测试不要360,就写个330度,效果如下:

这个时候你会发现这条线是对的,导致问题其实是这样的,如图分析:

把绘制椭圆的关键代码:

searchPath.addArc(searchRect,45,358);

circlePath.addArc(circleRect,45,-358);

不要写成360,改为358试试,效果图:

发现这线是不是正常了,至于外面的圆还有点缺口,第一你可以把358改成359应该没事了,还有就是我们其实真实的效果并不需要这个外面的圆,所以不改也没事,那么好,第一步算是完成了,现在想想第二步怎么实现,先把第二步的效果用gif展示看下,不然光想没思路,就像你看美女,第一眼看那,是吧,就不多说了!要有画面感,

还是画布分析:

哪我们只要改变startD这个离起始点的位置值就ok,当然有很多种方法,但是Android中基本上都是使用值动画,ok,根据这个思路实现这个第二步逻辑:

package com.tuya;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**
 * Created by admin on 2016/12/17.
 */
public class DynamicSearchView2 extends View {
 private Paint paint;
 private int width;//view的宽度
 private int height;//view的高度
 private Path searchPath;
 private Path circlePath;
 private float BigCircleRectWidth;//搜索圆对应的外切正方形边长
 private PathMeasure pathMeasure;
 private float[] pos;
 private float animPercent;//
 private ValueAnimator serchStartAnim;
 private long animDuration = 2000;//动画时间
 public DynamicSearchView2(Context context) {
  this(context,null);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs) {
  this(context, attrs,0);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 private void init() {
  initPaint();
  initAnim();
  initAnimListener();
  startAnim();
 }
 /**
  * 开始执行动画
  */
 private void startAnim() {
  serchStartAnim.start();
 }
 /**
  * 动画监听
  */
 private void initAnimListener() {
  serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
     //获取动画在单位时间内,每次执行的值
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
 }
 /**
  * 初始化动画
  */
 private void initAnim() {
  serchStartAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration);
 }
 /**
  * 初始化画笔
  */
 private void initPaint() {
  paint = new Paint();
  paint.setAntiAlias(true);
  paint.setStrokeWidth(3);
  paint.setColor(Color.WHITE);
  paint.setStyle(Paint.Style.STROKE);
 }

 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  width = w;
  height = h;
  initPath();
 }
 /**
  * 初始化path
  */
 private void initPath() {
  searchPath = new Path();
  circlePath = new Path();
  if(width>height){//长方形
   BigCircleRectWidth = height;
  }else if(width<height){
   BigCircleRectWidth = width;
  }else{
   BigCircleRectWidth = width;
  }
  float smallbordWidth =BigCircleRectWidth/8;
  RectF searchRect = new RectF(-smallbordWidth,-smallbordWidth,smallbordWidth,smallbordWidth);

  searchPath.addArc(searchRect,45,358);
  float bigBordWidth = smallbordWidth*2;
  RectF circleRect = new RectF(-bigBordWidth,-bigBordWidth,bigBordWidth,bigBordWidth);
  circlePath.addArc(circleRect,45,-358);
  pathMeasure = new PathMeasure(circlePath,false);
  pos = new float[2];
  pathMeasure.getPosTan(0,pos,null);
  searchPath.lineTo(pos[0],pos[1]);
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
   canvas.translate(width/2,height/2);//平移画布把这个view的中心点当做原点
   drawSearch(canvas);
 }
 private void drawSearch(Canvas canvas) {
  Path dst = new Path();
  pathMeasure.setPath(searchPath,false);
  pathMeasure.getSegment(pathMeasure.getLength()*animPercent,pathMeasure.getLength(),dst,true);
  canvas.drawPath(searchPath,paint);
 }
}

效果:

现在还我们效果还差外圆的大圆的效果了,那么大圆是在小圆动画执行完毕后再去做旋转效果的,那好,我们只要监听动画就可以,画图:

package com.tuya;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**
 * Created by admin on 2016/12/17.
 */
public class DynamicSearchView2 extends View {
 private static final String TAG = "DynamicSearchView2";
 private Paint paint;
 private int width;//view的宽度
 private int height;//view的高度
 private Path searchPath;
 private Path circlePath;
 private float BigCircleRectWidth;//搜索圆对应的外切正方形边长
 private PathMeasure pathMeasure;
 private float[] pos;
 private float animPercent;//
 private ValueAnimator serchStartAnim;
 private ValueAnimator bigCircleAnim;//外面大圆运动的动画
 private long animDuration = 2000;//动画时间
 private int drawTag = 1;//区分是绘制搜索框还是外层圆
 public DynamicSearchView2(Context context) {
  this(context,null);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs) {
  this(context, attrs,0);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 private void init() {
  initPaint();
  initAnim();
  initAnimListener();
  startAnim();
 }
 /**
  * 开始执行动画
  */
 private void startAnim() {
  drawTag = 1;
  serchStartAnim.start();
  invalidate();
 }
 /**
  * 开启大圆执行动画
  */
 public void startBigCirCleAnim(){
  serchStartAnim.removeAllUpdateListeners();//把上一个动画监听移除 以免总成诡异的bug
  bigCircleAnim.start();
  drawTag = 2;
 }
 /**
  * 动画监听
  */
 private void initAnimListener() {
  serchStartAnim.addListener(new Animator.AnimatorListener() {
   @Override
   public void onAnimationStart(Animator animator) {
   }
   @Override
   public void onAnimationEnd(Animator animator) {
    startBigCirCleAnim();
   }
   @Override
   public void onAnimationCancel(Animator animator) {
   }
   @Override
   public void onAnimationRepeat(Animator animator) {
   }
  });
  serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    //获取动画在单位时间内,每次执行的值
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
  bigCircleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    //获取动画在单位时间内,每次执行的值
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
 }
 /**
  * 初始化动画
  */
 private void initAnim() {
  bigCircleAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration);
  serchStartAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration);
 }
 /**
  * 初始化画笔
  */
 private void initPaint() {
  paint = new Paint();
  paint.setAntiAlias(true);
  paint.setStrokeWidth(3);
  paint.setColor(Color.WHITE);
  paint.setStyle(Paint.Style.STROKE);
 }
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  width = w;
  height = h;
  initPath();
 }
 /**
  * 初始化path
  */
 private void initPath() {
  searchPath = new Path();
  circlePath = new Path();
  if(width>height){//长方形
   BigCircleRectWidth = height;
  }else if(width<height){
   BigCircleRectWidth = width;
  }else{
   BigCircleRectWidth = width;
  }
  float smallbordWidth =BigCircleRectWidth/8;
  RectF searchRect = new RectF(-smallbordWidth,-smallbordWidth,smallbordWidth,smallbordWidth);

  searchPath.addArc(searchRect,45,358);
  float bigBordWidth = smallbordWidth*2;
  RectF circleRect = new RectF(-bigBordWidth,-bigBordWidth,bigBordWidth,bigBordWidth);
  circlePath.addArc(circleRect,45,-358);
  pathMeasure = new PathMeasure(circlePath,false);
  pos = new float[2];
  pathMeasure.getPosTan(0,pos,null);
  searchPath.lineTo(pos[0],pos[1]);
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  canvas.translate(width/2,height/2);//平移画布把这个view的中心点当做原点
  drawSearch(canvas);
 }
 private void drawSearch(Canvas canvas) {
  if(drawTag==1){
   drawSearchGraph(canvas);
  }else if(drawTag==2){
   drawBigCircleGraph(canvas);
  }
 }
 /**
  * 绘制外层大圆
  * @param canvas
  */
 private void drawBigCircleGraph(Canvas canvas) {
  pathMeasure.setPath(circlePath, false);
  Path dst2 = new Path();
  float stop = pathMeasure.getLength() * animPercent;
  float start = (float) (stop - ((0.5 - Math.abs(animPercent - 0.5)) * 200f));
  pathMeasure.getSegment(start, stop, dst2, true);
  canvas.drawPath(dst2, paint);
 }
 /**
  * 绘制搜索框
  * @param canvas
  */
 private void drawSearchGraph(Canvas canvas) {
  pathMeasure.setPath(searchPath,false);
  Path dst = new Path();
  pathMeasure.getSegment(pathMeasure.getLength()*animPercent,pathMeasure.getLength(),dst,true);
  canvas.drawPath(dst,paint);
 }
}

效果:

发现转一圈就到头了,如果有特定的需求肯定是要控制整个转圈的圈数,如果是网络加载的话,除非网络特别的好,先不管了,因为等下还要写周报,也是很痛苦的

现在还差最后一步就是大圆的运动完后要绘制搜索框出来,其实这个和第一步效果刚好是相关的,

package com.tuya;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**
 * Created by admin on 2016/12/17.
 */
public class DynamicSearchView2 extends View {
 private static final String TAG = "DynamicSearchView2";
 private Paint paint;
 private int width;//view的宽度
 private int height;//view的高度
 private Path searchPath;
 private Path circlePath;
 private float BigCircleRectWidth;//搜索圆对应的外切正方形边长
 private PathMeasure pathMeasure;
 private float[] pos;
 private float animPercent;//
 private ValueAnimator serchStartAnim;
 private ValueAnimator bigCircleAnim;//外面大圆运动的动画
 private ValueAnimator startDrawSearchAnim;//最后一步绘制搜索框
 private long animDuration = 2000;//动画时间
 private int drawTag = 1;//区分是绘制搜索框还是外层圆
 public DynamicSearchView2(Context context) {
  this(context,null);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs) {
  this(context, attrs,0);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 private void init() {
  initPaint();
  initAnim();
  initAnimListener();
  startAnim();
 }
 /**
  * 开始执行动画
  */
 private void startAnim() {
  drawTag = 1;
  serchStartAnim.start();
  invalidate();
 }
 /**
  * 开启大圆执行动画
  */
 public void startBigCirCleAnim(){
  serchStartAnim.removeAllUpdateListeners();//把上一个动画监听移除 以免总成诡异的bug
  bigCircleAnim.start();
  drawTag = 2;
 }
 /**
  * 最后绘制搜索框的动画
  */
 public void drawSearchAanim(){
  bigCircleAnim.removeAllUpdateListeners();//把上一个动画监听移除 以免总成诡异的bug
  startDrawSearchAnim.start();
  drawTag = 3;
 }
 /**
  * 动画监听
  */
 private void initAnimListener() {
  bigCircleAnim.addListener(new Animator.AnimatorListener() {
   @Override
   public void onAnimationStart(Animator animator) {
   }
   @Override
   public void onAnimationEnd(Animator animator) {
    drawSearchAanim();
   }
   @Override
   public void onAnimationCancel(Animator animator) {
   }
   @Override
   public void onAnimationRepeat(Animator animator) {
   }
  });
  serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    //获取动画在单位时间内,每次执行的值
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
  serchStartAnim.addListener(new Animator.AnimatorListener() {
   @Override
   public void onAnimationStart(Animator animator) {
   }
   @Override
   public void onAnimationEnd(Animator animator) {
    startBigCirCleAnim();
   }
   @Override
   public void onAnimationCancel(Animator animator) {
   }
   @Override
   public void onAnimationRepeat(Animator animator) {
   }
  });
  serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    //获取动画在单位时间内,每次执行的值
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
  bigCircleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    //获取动画在单位时间内,每次执行的值
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
  startDrawSearchAnim .addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    //获取动画在单位时间内,每次执行的值
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
 }
 /**
  * 初始化动画
  */
 private void initAnim() {
  bigCircleAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration);
  serchStartAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration);
  startDrawSearchAnim = ValueAnimator.ofFloat(1,0).setDuration(animDuration);
 }
 /**
  * 初始化画笔
  */
 private void initPaint() {
  paint = new Paint();
  paint.setAntiAlias(true);
  paint.setStrokeWidth(6);
  paint.setColor(Color.WHITE);
  paint.setStyle(Paint.Style.STROKE);
 }
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  width = w;
  height = h;
  initPath();
 }
 /**
  * 初始化path
  */
 private void initPath() {
  searchPath = new Path();
  circlePath = new Path();
  if(width>height){//长方形
   BigCircleRectWidth = height;
  }else if(width<height){
   BigCircleRectWidth = width;
  }else{
   BigCircleRectWidth = width;
  }
  float smallbordWidth =BigCircleRectWidth/8;
  RectF searchRect = new RectF(-smallbordWidth,-smallbordWidth,smallbordWidth,smallbordWidth);
  searchPath.addArc(searchRect,45,358);
  float bigBordWidth = smallbordWidth*2;
  RectF circleRect = new RectF(-bigBordWidth,-bigBordWidth,bigBordWidth,bigBordWidth);
  circlePath.addArc(circleRect,45,-358);
  pathMeasure = new PathMeasure(circlePath,false);
  pos = new float[2];
  pathMeasure.getPosTan(0,pos,null);
  searchPath.lineTo(pos[0],pos[1]);
 }
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  canvas.translate(width/2,height/2);//平移画布把这个view的中心点当做原点
  drawSearch(canvas);
 }
 private void drawSearch(Canvas canvas) {
  if(drawTag==1){
   drawSearchGraph(canvas);
  }else if(drawTag==2){
   drawBigCircleGraph(canvas);
  }else if(drawTag==3){
   drawSearchBox(canvas);
  }
 }
 /**
  * 最后一步绘制搜索框 从终点到起点
  * @param canvas
  */
 private void drawSearchBox(Canvas canvas) {
  pathMeasure.setPath(searchPath, false);
  Path dst3 = new Path();
  pathMeasure.getSegment(pathMeasure.getLength() * animPercent, pathMeasure.getLength(), dst3, true);
  canvas.drawPath(dst3, paint);
 }
 /**
  * 绘制外层大圆
  * @param canvas
  */
 private void drawBigCircleGraph(Canvas canvas) {
  pathMeasure.setPath(circlePath, false);
  Path dst2 = new Path();
  float stop = pathMeasure.getLength() * animPercent;
  float start = (float) (stop - ((0.5 - Math.abs(animPercent - 0.5)) * 200f));
  pathMeasure.getSegment(start, stop, dst2, true);
  canvas.drawPath(dst2, paint);
 }
 /**
  * 绘制搜索框
  * @param canvas
  */
 private void drawSearchGraph(Canvas canvas) {
  pathMeasure.setPath(searchPath,false);
  Path dst = new Path();
  pathMeasure.getSegment(pathMeasure.getLength()*animPercent,pathMeasure.getLength(),dst,true);
  canvas.drawPath(dst,paint);
 }
}

效果:

github: https://github.com/zhouguizhi/PathSearch

总结

以上所述是小编给大家介绍的Android 使用 Path 实现搜索动态加载动画效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android 使用 Path 实现搜索动态加载动画效果

    今天实现一个搜索动态加载数据的动画效果,还是先看效果吧,用文字描述干巴巴的,看图说话什么都明白了, 实现这个就是使用Path中的getSegment()不断的去改变它截取片段的start和stop,再结合动画,今天就分步骤实现它,看完以后你也会觉的不是很难,只是没想到这么实现而已,所以要多见识,所谓眼界决定你的高度,还是延续我写博客的习惯,一步步分析,第一步就是绘制如下图: 如果单纯的绘制这个图很简单很简单的,绘制一个圆,然后再绘制一根线就搞定,但是要考虑这里的效果,就不能这么干了,如果你看了上

  • Android插件化之资源动态加载

    Android插件化之资源动态加载 一.概述 Android插件化的一个重要问题就是插件资源访问问题,先列出会面对的问题 1.如何加载插件资源 2.如何处理插件资源与宿主资源的处突:插件化资源问题要做到的效果是,如果我们要获取的资源在插件中找得到,则加载优先加载插件的,如果找不到,则到宿主资源中找.这样能做到动态更新的效果. 3.如何确保插件和宿主使用到的是被修改过的资源. 二.原理分析 在做一件事之前必须先弄清楚原理,所以,这里先要弄清楚Android的资源体系原理. 1.资源链 Contex

  • Android自定义view实现阻尼效果的加载动画

    效果: 需要知识: 1. 二次贝塞尔曲线 2. 动画知识 3. 基础自定义view知识 先来解释下什么叫阻尼运动 阻尼振动是指,由于振动系统受到摩擦和介质阻力或其他能耗而使振幅随时间逐渐衰减的振动,又称减幅振动.衰减振动.[1] 不论是弹簧振子还是单摆由于外界的摩擦和介质阻力总是存在,在振动过程中要不断克服外界阻力做功,消耗能量,振幅就会逐渐减小,经过一段时间,振动就会完全停下来.这种振幅随时间减小的振动称为阻尼振动.因为振幅与振动的能量有关,阻尼振动也就是能量不断减少的振动.阻尼振动是非简谐运

  • Android Flutter绘制有趣的 loading加载动画

    目录 前言 效果1:圆环内滚动的球 效果2:双轨运动 效果3:钟摆运动 总结 前言 在网络速度较慢的场景,一个有趣的加载会提高用户的耐心和对 App 的好感,有些 loading 动效甚至会让用户有想弄清楚整个动效过程到底是怎么样的冲动.然而,大部分的 App的 loading 就是下面这种千篇一律的效果 —— 俗称“转圈”. 本篇我们利用Flutter 的 PathMetric来玩几个有趣的 loading 效果. 效果1:圆环内滚动的球 如上图所示,一个红色的小球在蓝色的圆环内滚动,而且在往

  • Android仿网易一元夺宝客户端下拉加载动画效果(一)

    上上周写的一个demo,仿照网易一元夺宝的下拉刷新效果. 原效果是(第一部分)一个小太阳拉下来,然后松开回弹上去, (第二部分)再掉下来一个硬币进行中轴旋转. 本文实现的效果的是第一部分的,效果演示图如下: Gif图看起来比较卡顿...其实真机演示效果还是很流畅的. 下面分析实现过程: 当时因为时间有限没有写在下拉刷新的组件中,也没有封装成一个单独的组件,只是在主布局后面写了一个View然后实现相应的操作,进行封装并不难,这里就不花时间BB了,下面是布局文件: <RelativeLayout x

  • jQuery Easyui使用(二)之可折叠面板动态加载无效果的解决方法

    在上篇文章给大家介绍了jQuery Easyui使用(一)之可折叠面板的布局手风琴菜单 先上代码: <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title&g

  • JS Canvas定时器模拟动态加载动画

    本文实例为大家分享了Canvas定时器动态加载动画,供大家参考,具体内容如下 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> </style> </head> <body> <canvas id="canva

  • Android实现仿微软系统加载动画效果

    效果图: 实现步骤: 初始化五个圆球分别设置中心点,方便画圆 利用ValueAnimator的值变化来获取旋转角度 onDraw来分别画每个圆 具体代码实现: 1.创建Circle对象 package com.sjl.keeplive.track; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PointF; public class Circle { private

  • jQuery生成假加载动画效果

    在使用PDFObject.js时,由于后台需要转换数据,在前台显示的时候,有很长一段时间显示空白页面,所以想到写一个假的加载动画 script片段: <script type="text/javascript"> var bar = 0; var line = "||" ; var amount ="||" ; function count(){ bar= bar+2 ; amount =amount + line; $("

  • ios基于MJRefresh实现上拉刷新和下拉加载动画效果

    本文介绍了ios基于MJRefresh实现上拉刷新和下拉加载动画效果,分享给大家,具体如下: 目录 1. 头部刷新动画 2.尾部刷新动画 头部刷新动画 #import <MJRefresh/MJRefresh.h> @interface HZNormalHeader : MJRefreshGifHeader @end #import "HZNormalHeader.h" @implementation HZNormalHeader #pragma mark - 重写父类的方

随机推荐