转载请注明出处 http://blog.csdn.net/u011453163/article/details/52424328
直播功能现在已经是一个很热门的功能了,很多应用都会涉及到直播模块,比如 花椒 NOW 还有辣妈帮。。等,最近因为项目需要也加入了直播功能。直播中有一个点赞的效果 ,今天我也按照自己的思路实现了一个这样的点赞功能,效果如下:
简单描述一下效果
1.产生一颗心
2.由下至上的曲线运动
3.开头有一段放大效果
4.透明度渐变效果
实现以上效果涉及到的知识点
曲线轨迹 :属性动画+贝赛尔曲线算法
由于点赞效果在直播中的持续时间是比较长的
所以这里使用的是surfaceview 可以在工作线程中绘制ui
ui绘制:surfaceview
该效果的轨迹原型图
轨迹坐标点是通过属性动画 TypeEvaluator 生成的
private class BezierEvaluator implements TypeEvaluator<Point> {
private Point centerPoint;
public BezierEvaluator(Point centerPoint) {
this.centerPoint = centerPoint;
}
@Override
public Point evaluate(float t, Point startValue, Point endValue) {
int x = (int) ((1 - t) * (1 - t) * startValue.x + 2 * t * (1 - t) * centerPoint.x + t * t * endValue.x);
int y = (int) ((1 - t) * (1 - t) * startValue.y + 2 * t * (1 - t) * centerPoint.y + t * t * endValue.y);
return new Point(x, y);
}
}
接下来分享一下代码的实现思路
由两个类构成的 Zanbean ZanView
public class ZanBean {
/**心的当前坐标*/
public Point point;
/**移动动画*/
private ValueAnimator moveAnim;
/**放大动画*/
private ValueAnimator zoomAnim;
/**透明度*/
public int alpha=255;//
/**心图*/
private Bitmap bitmap;
/**绘制bitmap的矩阵 用来做缩放和移动的*/
private Matrix matrix = new Matrix();
/**缩放系数*/
private float sf=0;
/**产生随机数*/
private Random random;
public boolean isEnd=false;//是否结束
public ZanBean(Context context,int resId,ZanView zanView) {
random=new Random();
bitmap= BitmapFactory.decodeResource(context.getResources(),resId);
init(new Point(zanView.getWidth() / 2, zanView.getHeight()), new Point((random.nextInt(zanView.getWidth())), 0));
}
public ZanBean(Context context,Bitmap bitmap,ZanView zanView) {
random=new Random();
this.bitmap= bitmap;
init(new Point(zanView.getWidth() / 2, zanView.getHeight()), new Point((random.nextInt(zanView.getWidth())), 0));
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void init(final Point startPoint, Point endPoint){
moveAnim =ValueAnimator.ofObject(new BezierEvaluator(new Point(random.nextInt(startPoint.x*2),Math.abs(endPoint.y-startPoint.y)/2)),startPoint,endPoint);
moveAnim.setDuration(2500);
moveAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
point= (Point) animation.getAnimatedValue();
alpha= (int) ((float)point.y/(float)startPoint.y*255);
}
});
moveAnim.start();
zoomAnim =ValueAnimator.ofFloat(0,1f).setDuration(700);
zoomAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Float f= (Float) animation.getAnimatedValue();
sf=f.floatValue();
}
});
zoomAnim.start();
}
public void pause(){
if(moveAnim !=null&& moveAnim.isRunning()){
moveAnim.pause();
}
if(zoomAnim !=null&& zoomAnim.isRunning()){
zoomAnim.pause();
}
}
public void resume(){
if(moveAnim !=null&& moveAnim.isPaused()){
moveAnim.resume();
}
if(zoomAnim !=null&& zoomAnim.isPaused()){
zoomAnim.resume();
}
}
/**主要绘制函数*/
public void draw(Canvas canvas, Paint p){
if(bitmap!=null&&alpha>0) {
p.setAlpha(alpha);
matrix.setScale(sf,sf,bitmap.getWidth()/2,bitmap.getHeight()/2);
matrix.postTranslate(point.x-bitmap.getWidth()/2,point.y-bitmap.getHeight()/2);
canvas.drawBitmap(bitmap, matrix, p);
}else {
isEnd=true;
}
}
/**
* 二次贝塞尔曲线
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private class BezierEvaluator implements TypeEvaluator<Point> {
private Point centerPoint;
public BezierEvaluator(Point centerPoint) {
this.centerPoint = centerPoint;
}
@Override
public Point evaluate(float t, Point startValue, Point endValue) {
int x = (int) ((1 - t) * (1 - t) * startValue.x + 2 * t * (1 - t) * centerPoint.x + t * t * endValue.x);
int y = (int) ((1 - t) * (1 - t) * startValue.y + 2 * t * (1 - t) * centerPoint.y + t * t * endValue.y);
return new Point(x, y);
}
}
}
Zanbean
用来记录心的轨迹 和 绘制的工作
这里简单介绍一下init方法
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void init(final Point startPoint, Point endPoint){
moveAnim =ValueAnimator.ofObject(new BezierEvaluator(new Point(random.nextInt(startPoint.x*2),Math.abs(endPoint.y-startPoint.y)/2)),startPoint,endPoint);
moveAnim.setDuration(2500);
moveAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
point= (Point) animation.getAnimatedValue();
alpha= (int) ((float)point.y/(float)startPoint.y*255);
}
});
moveAnim.start();
zoomAnim =ValueAnimator.ofFloat(0,1f).setDuration(700);
zoomAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Float f= (Float) animation.getAnimatedValue();
sf=f.floatValue();
}
});
zoomAnim.start();
}
在创建对象的时候 属性动画会直接启动。更符合场景,相当每个心丢进画面就会自动跑起来,每个心都是独立的个体
然后是ZanView 画面绘制和呈现的主体
public class ZanView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder surfaceHolder;
/**心的个数*/
private ArrayList<ZanBean> zanBeen = new ArrayList<>();
private Paint p;
/**负责绘制的工作线程*/
private DrawThread drawThread;
public ZanView(Context context) {
this(context, null);
}
public ZanView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ZanView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.setZOrderOnTop(true);
/**设置画布 背景透明*/
this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
p = new Paint();
p.setAntiAlias(true);
drawThread = new DrawThread();
}
/**点赞动作 添加心的函数 控制画面最大心的个数*/
public void addZanXin(ZanBean zanBean){
zanBeen.add(zanBean);
if(zanBeen.size()>40){
zanBeen.remove(0);
}
start();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if(drawThread==null){
drawThread=new DrawThread();
}
drawThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if(drawThread!=null){
drawThread.isRun = false;
drawThread=null;
}
}
class DrawThread extends Thread {
boolean isRun = true;
@Override
public void run() {
super.run();
/**绘制的线程 死循环 不断的跑动*/
while (isRun) {
Canvas canvas = null;
try {
synchronized (surfaceHolder) {
canvas = surfaceHolder.lockCanvas();
/**清除画面*/
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
boolean isEnd=true;
/**对所有心进行遍历绘制*/
for (int i = 0; i < zanBeen.size(); i++) {
isEnd=zanBeen.get(i).isEnd;
zanBeen.get(i).draw(canvas, p);
}
/**这里做一个性能优化的动作,由于线程是死循环的 在没有心需要的绘制的时候会结束线程*/
if(isEnd){
isRun=false;
drawThread=null;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
try {
/**用于控制绘制帧率*/
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}}
}
public void stop(){
if(drawThread!=null){
for (int i = 0; i < zanBeen.size(); i++) {
zanBeen.get(i).pause();
}
drawThread.isRun=false;
drawThread=null;
}
}
public void start(){
if(drawThread==null){
for (int i = 0; i < zanBeen.size(); i++) {
zanBeen.get(i).resume();
}
drawThread=new DrawThread();
drawThread.start();}
}
}
以上是ZanView的所有代码 重要的地方都做了注释 还是比较好懂的吧
为了验证贝赛尔曲线
只要将这句话注释就能看到每一帧的轨迹
/**清除画面*/
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
以上就是关于点赞 心动的效果 有什么问题欢迎指出
作者:u011453163 发表于2016/9/3 17:17:27 原文链接
阅读:82 评论:0 查看评论