热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

我想做的效果图是后来知道类似的效果用贝塞尔函数能实现,然后找到了这篇文章:http:www.jianshu.comp791d3a791ec2(打赏了作者两元,因为作者把原理

我想做的效果图是


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

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();
}
});
}
}





推荐阅读
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 本文介绍了如何使用elementui分页组件进行分页功能的改写,只需一行代码即可调用。通过封装分页组件,避免在每个页面都写跳转请求的重复代码。详细的代码示例和使用方法在正文中给出。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
author-avatar
yuzhenhua09
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有