200行代码打造直播间点赞、飘心效果
2017-09-01 17:02
183 查看
先上效果图:
用到了二阶贝塞尔曲线,先来学习学习。
原理: 由 P0 至 P1 的连续点 Q0,描述一条线段。
由 P1 至 P2 的连续点 Q1,描述一条线段。
由 Q0 至 Q1 的连续点 B(t),描述一条二次贝塞尔曲线。
按照我自己的理解就是,P0和P2分别为起点和终点,P1为控制点,来控制曲线的轨迹。
因为是很多个心,所以我用HasMap来装,因为在动画结束之后我们要把心在界面移除,所以用key来删除当前的心。因为是自定义View,所以我把曲线和动画及更新的位置都放在了内部类里面,让自行操作。了解了二阶贝塞尔曲线的原理之后,我把贝塞尔曲线的控制点放在了下图中圈中的位置,控制点是随机的。起点是最下面的中点,终点是最上面x轴随机的位置。
下面上代码,基本都有注释。
classBezierEvaluatorimplementsTypeEvaluator<Point>,ValueAnimator.AnimatorUpdateListener{ privatePointrandomPoint; c4e1 //随机控制点 privateintx;//每次更新的曲线的x坐标 privateinty;//每次更新的曲线的y坐标 publicinti;//随机选择的图片样式 publicStringkey;//用来删除动画结束的心 privateBitmapbitmap;//要画心的bitmap publicPaintpaint;//画笔 publicBezierEvaluator(Stringkey){ this.key=key; paint=newPaint(); paint.setAntiAlias(true); i=newRandom().nextInt(5); getImg(i); //获得随机的bitmap randomPoint=newPoint(newRandom().nextInt(mWidth/2)+mWidth/4,newRandom().nextInt(mHeight/2)); //获得随机控制点 PointstartP=newPoint(mWidth/2,mHeight-50); //设置起点位置,向上偏移50 PointendP=newPoint(newRandom().nextInt(mWidth/2)+mWidth/4,0); //设置终点位置,让终点在上图的随机位置 ValueAnimatoranim=ValueAnimator.ofObject(this,startP,endP); //设置动画 anim.addUpdateListener(this); anim.setDuration(5000); anim.addListener(newAnimatorListenerAdapter(){ @Override publicvoidonAnimationEnd(Animatoranimation){ super.onAnimationEnd(animation); hashMap.remove(BezierEvaluator.this.key); //在hashmap中把当前删除 randomIs.remove(BezierEvaluator.this.key); //删除当前key值 bitmap.recycle(); //回收bitmap bitmap=null; //滞空bitmap } }); anim.setInterpolator(newLinearInterpolator()); //为动画加入插值器 anim.start(); } privatevoidgetImg(inti){ switch(i){ case0: bitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.heart1).copy(Bitmap.Config.ARGB_8888,true); break; case1: bitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.heart2).copy(Bitmap.Config.ARGB_8888,true); break; case2: bitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.heart3).copy(Bitmap.Config.ARGB_8888,true); break; case3: bitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.heart4).copy(Bitmap.Config.ARGB_8888,true); break; case4: bitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.heart5).copy(Bitmap.Config.ARGB_8888,true); break; } } @Override publicPointevaluate(floatt,PointstartValue,PointendValue){ intx=(int)((1-t)*(1-t)*startValue.x+2*t*(1-t)*randomPoint.x+t*t*endValue.x); inty=(int)((1-t)*(1-t)*startValue.y+2*t*(1-t)*randomPoint.y+t*t*endValue.y); //根据二阶贝塞尔曲线公式获得在屏幕运动中的x,y坐标 returnnewPoint(x,y); } @Override publicvoidonAnimationUpdate(ValueAnimatoranimation){ //根据动画的更新来获得坐标来重绘onDraw Pointpoint=(Point)animation.getAnimatedValue(); this.x=point.x; this.y=point.y; intalpha=point.y/4; paint.setAlpha(alpha); //为画笔设置透明度 invalidate(); } }
下面是自定义view中点击事件添加心以及对外开放添加心的方法。
@Override publicvoidonClick(Viewv){ keys.add(String.valueOf(key)); hashMap.put(String.valueOf(key),newBezierEvaluator(String.valueOf(key))); key++; } publicvoidaddHeart(intsize){ addSize=size; if(size>0){ newHandler().postDelayed(newRunnable(){ @Override publicvoidrun(){ keys.add(String.valueOf(key)); hashMap.put(String.valueOf(key),newBezierEvaluator(String.valueOf(key))); key++; addSize--; addHeart(addSize); } },100); }else{ return; } }使用:
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.yogee.mycustom.view.HeartView android:id="@+id/heartView" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
privateHeartViewheartView; @Override protectedvoidonCreate(@NullableBundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_six); heartView=(HeartView)findViewById(R.id.heartView); heartView.addHeart(500); }
完整版代码:
packagecom.yogee.mycustom.view; importandroid.animation.Animator; importandroid.animation.AnimatorListenerAdapter; importandroid.animation.TypeEvaluator; importandroid.animation.ValueAnimator; importandroid.content.Context; importandroid.graphics.Bitmap; importandroid.graphics.BitmapFactory; importandroid.graphics.Canvas; importandroid.graphics.Paint; importandroid.graphics.Point; importandroid.os.Handler; importandroid.support.annotation.Nullable; importandroid.util.AttributeSet; importandroid.view.View; importandroid.view.animation.LinearInterpolator; importcom.yogee.mycustom.R; importjava.util.ArrayList; importjava.util.HashMap; importjava.util.List; importjava.util.Random; publicclassHeartViewextendsViewimplementsView.OnClickListener{ privateintmWidth; privateintmHeight; privateintkey=0; privateList<String>keys=newArrayList<>();//key值集合 privateHashMap<String,BezierEvaluator>hashMap=newHashMap<>(); privateintaddSize; publicHeartView(Contextcontext){ super(context); setOnClickListener(this); } publicHeartView(Contextcontext,@NullableAttributeSetattrs){ super(context,attrs); setOnClickListener(this); } publicHeartView(Contextcontext,@NullableAttributeSetattrs,intdefStyleAttr){ super(context,attrs,defStyleAttr); setOnClickListener(this); } @Override protectedvoidonDraw(Canvascanvas){ super.onDraw(canvas); if(hashMap.size()>0) for(Stringi:keys){ canvas.drawBitmap(hashMap.get(i).bitmap,hashMap.get(i).x,hashMap.get(i).y,hashMap.get(i).paint); } } @Override protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){ super.onMeasure(widthMeasureSpec,heightMeasureSpec); mWidth=MeasureSpec.getSize(widthMeasureSpec); mHeight=MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(mWidth,mHeight); } @Override publicvoidonClick(Viewv){ keys.add(String.valueOf(key)); hashMap.put(String.valueOf(key),newBezierEvaluator(String.valueOf(key))); key++; } publicvoidaddHeart(intsize){ addSize=size; if(size>0){ newHandler().postDelayed(newRunnable(){ @Override publicvoidrun(){ keys.add(String.valueOf(key)); hashMap.put(String.valueOf(key),newBezierEvaluator(String.valueOf(key))); key++; addSize--; addHeart(addSize); } },100); }else{ return; } } classBezierEvaluatorimplementsTypeEvaluator<Point>,ValueAnimator.AnimatorUpdateListener{ privatePointrandomPoint;//随机控制点 privateintx;//每次更新的曲线的x坐标 privateinty;//每次更新的曲线的y坐标 publicinti;//随机选择的图片样式 publicStringkey;//用来删除动画结束的心 privateBitmapbitmap;//要画心的bitmap publicPaintpaint;//画笔 publicBezierEvaluator(Stringkey){ this.key=key; paint=newPaint(); paint.setAntiAlias(true); i=newRandom().nextInt(5); getImg(i); //获得随机的bitmap randomPoint=newPoint(newRandom().nextInt(mWidth/2)+mWidth/4,newRandom().nextInt(mHeight/2)); //获得随机控制点 PointstartP=newPoint(mWidth/2,mHeight-50); //设置起点位置,向上偏移50 PointendP=newPoint(newRandom().nextInt(mWidth/2)+mWidth/4,0); //设置终点位置,让终点在上图的随机位置 ValueAnimatoranim=ValueAnimator.ofObject(this,startP,endP); //设置动画 anim.addUpdateListener(this); anim.setDuration(5000); anim.addListener(newAnimatorListenerAdapter(){ @Override publicvoidonAnimationEnd(Animatoranimation){ super.onAnimationEnd(animation); hashMap.remove(BezierEvaluator.this.key); //在hashmap中把当前删除 keys.remove(BezierEvaluator.this.key); //删除当前key值 bitmap.recycle(); //回收bitmap bitmap=null; //滞空bitmap } }); anim.setInterpolator(newLinearInterpolator()); //为动画加入插值器 anim.start(); } privatevoidgetImg(inti){ switch(i){ case0: bitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.heart1).copy(Bitmap.Config.ARGB_8888,true); break; case1: bitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.heart2).copy(Bitmap.Config.ARGB_8888,true); break; case2: bitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.heart3).copy(Bitmap.Config.ARGB_8888,true); break; case3: bitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.heart4).copy(Bitmap.Config.ARGB_8888,true); break; case4: bitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.heart5).copy(Bitmap.Config.ARGB_8888,true); break; } } @Override publicPointevaluate(floatt,PointstartValue,PointendValue){ intx=(int)((1-t)*(1-t)*startValue.x+2*t*(1-t)*randomPoint.x+t*t*endValue.x); inty=(int)((1-t)*(1-t)*startValue.y+2*t*(1-t)*randomPoint.y+t*t*endValue.y); //根据二阶贝塞尔曲线公式获得在屏幕运动中的x,y坐标 returnnewPoint(x,y); } @Override publicvoidonAnimationUpdate(ValueAnimatoranimation){ //根据动画的更新来获得坐标来重绘onDraw Pointpoint=(Point)animation.getAnimatedValue(); this.x=point.x; this.y=point.y; intalpha=point.y/4; paint.setAlpha(alpha); //为画笔设置透明度 invalidate(); } } }
完成。
相关文章推荐
- JavaScript直播评论发弹幕切图功能点集合效果代码
- 十分钟打造AutoComplete自动完成效果代码
- Android自定义View——贝塞尔曲线实现直播点赞效果
- Android 自定义 HorizontalScrollView 打造多图片OOM 的横向滑动效果(实例代码)
- 仿某直播平台的点赞效果
- 使用surfaceview实现直播中的点赞效果
- Android直播app送礼物连击动画效果(实例代码)
- 用jQuery打造TabPanel效果代码
- 高级UI特效仿直播点赞效果—一个优美炫酷的点赞动画
- 纯php打造的tab选项卡效果代码(不用js)
- 打造超酷的PHP数据饼图效果实现代码
- 用jQuery打造TabPanel效果代码
- 使用三阶贝塞尔曲线实现直播中点赞效果
- 微信小程序小组件:仿直播点赞气泡效果,基于Canvas
- android 飘心动画(直播点赞)效果(二)---贝塞尔曲线的实现
- android实现直播点赞飘心动画效果
- 打造超酷的PHP数据饼图效果实现代码
- android 飘心动画(直播点赞)效果
- Android仿直播特效之点赞飘心效果
- Android/安卓仿淘宝直播点赞效果/qq空间点赞效果动画