从左上角到右下角的水滴效果实现(基于贝塞尔曲线)

我想做的效果图是


后来知道类似的效果用贝塞尔函数能实现,然后找到了这篇文章:

http://www.jianshu.com/p/791d3a791ec2   (打赏了作者两元,因为作者把原理解析的深入浅出。 简书有打赏功能,好想把文章搬过去,后来想想自己的文章深度还不够,就算了,2333)

我在上面文章demo的基础上改成了如下的效果:

用 Chrome应用启动器的 ARC Welder 和 liceCap(http://www.cockos.com/licecap/)做的gif效果图(ARC Welder 太卡,建议在手机上录制屏幕生成视频文件,然后在PC上播放视频,用liceCap制作gif)



感觉形状的变化可以改进,而且只是个初步的demo。从看到第一个gif图到实现第二个效果图,也是用了两天,所以想拿出来分享下。

package com.shadev.pierrebeziercircle;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;

/**
 * 代码写的比较仓猝,以后再优化写法和补充那些数值的具体含义,求勿喷QAQ
 */
public class MagicCircle extends View {

    private Path mPath;
    private Paint mFillCirclePaint;

    /** View的宽度 **/
    private int width;
    /** View的高度,这里View应该是正方形,所以宽高是一样的 **/
    private int height;
    /** View的中心坐标x **/
    private int centerX;
    /** View的中心坐标y **/
    private int centerY;

    private float maxLength;
    private float maxWidth;
    private float mInterpolatedTime;
    private float stretchDistance;
    private float moveDistance;
    private float cDistance;
    private float radius;
    private float c;
    private float blackMagic = 0.551915024494f;
    private VPoint p2,p4;
    private HPoint p1,p3;
    private float time1 = 0.2f;
    private float time2 = 0.5f;
    private float time3 = 0.8f;
    private float time4 = 0.9f;
    private float retangleTime = time1;
    /*private float time1 = 0.4f;
    private float time2 = 0.6f;
    private float time3 = 0.8f;
    private float time4 = 0.9f;*/



    public MagicCircle(Context context) {
        this(context, null, 0);
    }

    public MagicCircle(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MagicCircle(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mFillCirclePaint = new Paint();
        mFillCirclePaint.setColor(0xFFfe626d);
        mFillCirclePaint.setStyle(Paint.Style.FILL);
        mFillCirclePaint.setStrokeWidth(1);
        mFillCirclePaint.setAntiAlias(true);
        mPath = new Path();
        p2 = new VPoint();
        p4 = new VPoint();

        p1 = new HPoint();
        p3 = new HPoint();
    }

    @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        width = getWidth();
        height = getHeight();
        centerX = width / 2;
        centerY = height / 2;
        radius = 100;
        c = radius*blackMagic;
        stretchDistance = radius;
        moveDistance = radius*(3/5f);
        cDistance = c*0.45f;
        maxLength = height - radius -radius;
        maxWidth = width - radius -radius;
    }

    @Override protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPath.reset();
        canvas.translate(radius, radius);
        if(mInterpolatedTime == 0){
                model1(mInterpolatedTime);
                float offset = maxLength* (mInterpolatedTime-1000*0.1f);
                offset = offset>0?offset:0;
                p1.adjustAllX(offset);
               p2.adjustAllX(offset);
                p3.adjustAllX(offset);
                p4.adjustAllX(offset);
                canvas.drawRect(p4.x, p3.y, getWidth(), p1.y, mFillCirclePaint);// 长方形
                return;
        }
        if(mInterpolatedTime>=0&&mInterpolatedTime<=time1){
            model1(mInterpolatedTime);
            float offset = maxLength* (mInterpolatedTime-1000*0.2f);
            offset = offset>0?offset:0;
            p1.adjustAllX(offset);
            p2.adjustAllX(offset);
            p3.adjustAllX(offset);
            p4.adjustAllX(offset);
            mPath.moveTo(p1.x, p2.y);
            moveToOffset();
            Path path3=new Path();
            int trapezoidOffSet = 20;
            path3.moveTo(p4.x - trapezoidOffSet, p3.y);
            path3.lineTo(p2.x+trapezoidOffSet,p3.y);
            path3.lineTo(p2.x+trapezoidOffSet,p1.y);
            path3.lineTo(p4.x - trapezoidOffSet, p1.y);
            path3.close();
            canvas.drawPath(path3, mFillCirclePaint);
            return;
        }else if(mInterpolatedTime>time1&&mInterpolatedTime<=time2){
            model2(mInterpolatedTime);
        }else if(mInterpolatedTime>time2&&mInterpolatedTime<=time3){
            model3(mInterpolatedTime);
        }else if(mInterpolatedTime>time3&&mInterpolatedTime<=time4){
            model4(mInterpolatedTime);
        }else if(mInterpolatedTime>time4&&mInterpolatedTime<=1){
            model5(mInterpolatedTime);
        }


        moveToOffset();
        mPath.moveTo(p4.x, p4.y);
        mPath.cubicTo(p4.bottom.x, p4.bottom.y, p1.left.x, p1.left.y, p1.x,p1.y);
        mPath.cubicTo(p1.right.x, p1.right.y, p2.bottom.x, p2.bottom.y, p2.x, p2.y);
        mPath.cubicTo(p2.top.x, p2.top.y, p3.right.x, p3.right.y, p3.x, p3.y);
        mPath.cubicTo(p3.left.x, p3.left.y, p4.top.x, p4.top.y, p4.x, p4.y);

        canvas.drawPath(mPath, mFillCirclePaint);

    }

    private void moveToOffset() {

        float yOffset = maxLength*(mInterpolatedTime-time1);
        yOffset = yOffset>0?yOffset:0;
        p1.adjustAllY(yOffset);
        p2.adjustAllY(yOffset);
        p3.adjustAllY(yOffset);
        p4.adjustAllY(yOffset);
        /*float time = mInterpolatedTime * (10f / (retangleTime*10));
        if(time<0){
            time = 0;
        }else if(time >1){
            time = 1;
        }
        float xOffset = (float) (maxWidth*0.6*time+maxWidth*0.4*(mInterpolatedTime-time1));*/
        float xOffset = (float) (maxWidth*0.6+maxWidth*0.4*(mInterpolatedTime-time1));
        p1.adjustAllX(xOffset);
        p2.adjustAllX(xOffset);
        p3.adjustAllX(xOffset);
        p4.adjustAllX(xOffset);
    }

    private void model0(){
        p1.setY(radius);
        p3.setY(-radius);
        p3.x = p1.x = 0;
        p3.left.x =  p1.left.x = -c;
        p3.right.x = p1.right.x = c;

        p2.setX(radius);
        p4.setX(-radius);
        p2.y = p4.y = 0;
        p2.top.y =  p4.top.y = -c;
        p2.bottom.y = p4.bottom.y = c;
    }

    private void model1(float time){//0~0.2 下伸
        model0();
        //下伸
        time = time * (1f / time1);
        p1.setY(radius + stretchDistance * time);
    }

    private void model2(float time){//0.2~0.5
        model1(time1);
        time = (time - time1) * (1f / (time2-time1));
        //往中间靠
        p2.adjustAllY(stretchDistance / 2 * time);
        p4.adjustAllY(stretchDistance / 2 * time);
        //变化M
        p1.adjustX(cDistance * time);
        p3.adjustX(cDistance * time);
    }

    private void model3(float time){//0.5~0.8
        model2(time2);
        time = (time - time2) * (1f / (time3-time2));
        //居中
        p2.adjustAllY(stretchDistance / 2 * time);
        p4.adjustAllY(stretchDistance / 2 * time);
        //恢复M值
        p1.adjustX(-cDistance * time);
        p3.adjustX(-cDistance * time);
        //迁移上面的点
        p3.adjustAllY(stretchDistance / 2 * time);

    }

    private void model4(float time){//0.8~0.9
        model3(time3);
        time = (time - time3) * (1f / (time4-time3));
        p3.adjustAllY(stretchDistance / 2 * time);
    }

    private void model5(float time){
        model4(time4);
        time = time - time4;
        p3.adjustAllY((float) (Math.sin(Math.PI * time * 10f) * (2 / 10f * radius)));
    }

    class VPoint{
        public float x;
        public float y;
        public PointF top = new PointF();
        public PointF bottom = new PointF();

        public void setX(float x){
            this.x = x;
            top.x = x;
            bottom.x = x;
        }

        public void adjustY(float offset){
            top.y -= offset;
            bottom.y += offset;
        }

        public void adjustAllX(float offset){
            this.x+= offset;
            top.x+= offset;
            bottom.x+=offset;
        }

        public void adjustAllY(float offset){
            this.y+= offset;
            top.y+= offset;
            bottom.y+=offset;
        }
    }

    class HPoint{
        public float x;
        public float y;
        public PointF left = new PointF();
        public PointF right = new PointF();

        public void setY(float y){
            this.y = y;
            left.y = y;
            right.y = y;
        }

        public void adjustAllX(float offset){
            this.x +=offset;
            left.x +=offset;
            right.x +=offset;
        }

        public void adjustAllY(float offset){
            this.y +=offset;
            left.y +=offset;
            right.y +=offset;
        }

        public void adjustX(float offset){
            left.x -= offset;
            right.x += offset;
        }
    }

    private class MoveAnimation extends Animation {

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            mInterpolatedTime = interpolatedTime;
            Log.d("mInterpolatedTime",""+mInterpolatedTime);
            invalidate();
        }
    }

    public void startAnimation() {

        mPath.reset();
        mInterpolatedTime = 0;
        MoveAnimation move = new MoveAnimation();
        move.setDuration(1500);
        move.setInterpolator(new AccelerateDecelerateInterpolator());
        //move.setRepeatCount(Animation.INFINITE);
        //move.setRepeatMode(Animation.REVERSE);
        setVisibility(View.VISIBLE);
        startAnimation(move);
    }
}


package com.shadev.pierrebeziercircle;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private android.widget.Button btnstart;
    private MagicCircle circle3;
    private View retangle;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        circle3 = (MagicCircle)findViewById(R.id.circle3);
        this.btnstart = (Button) findViewById(R.id.btn_start);
        btnstart.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                circle3.startAnimation();
            }
        });
    }
}




  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值