贝塞尔曲线原理及应用
来源:互联网 发布:linux数据库启动命令 编辑:程序博客网 时间:2024/06/17 23:38
今天在学习贝塞尔曲线的过程中觉得很新奇,特别是之前觉得很神秘的东西一下全部融会贯通了,为了实践,特地写了一个demo——波浪图,先看效果图:
纸上得来终觉浅,绝知此事要躬行!本来觉得挺简单的一件事结果各种坑!什么,你说贝塞尔曲线不简单?no,no,看看大神们是怎么总结的?
二阶贝塞尔曲线形成原理:
1.连接 A,B 形成 AB 线段,连接 B,C 形成 BC 线段。
2.在 AB 线段取一个点 D,BC 线段取一个点 E ,使其满足条件: AD/AB = BE/BC,连接 D,E 形成线段 DE。
3.在 DE 取一个点 F,使其满足条件:AD/AB = BE/BC = DF/DE。
4.而满足这些条件的所有的F点所形成的轨迹就是二阶贝塞尔曲线,动态过程如下:
上面的代码解释得很清楚了,已知D,可以求得E,已知D、E可以求得F,如果D为一个变量,则F在二维平面就是一条曲线,如图四。
那么知道了二阶贝塞尔曲线的原理以后,怎么运用呢?
方法预览:
public void quadTo (float x1, float y1, float x2, float y2)
怎么用:
因为二阶贝塞尔曲线需要三个点才能确定,所以quadTo方法中的四个参数分别是确定第二,第三的点的。第一个点就是path上次操作的点。
现在用一个实例来练习下这个方法:水波纹效果:
实现思路:
我看见这幅图的第一思路是首先得到波浪长度mWL、波浪中心点mCenterY。因为图中的波浪宽度为整个屏幕宽度,所以我们需要得到屏幕的宽度。百度一下方法就出来了:
方法一:
//获取屏幕的宽度 public static int getScreenWidth(Context context) { WindowManager manager = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); return display.getWidth(); } //获取屏幕的高度 public static int getScreenHeight(Context context) { WindowManager manager = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); return display.getHeight(); }
方法二:
DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); int screenWidth = dm.widthPixels; int screenHeight = dm.heightPixels;
由于第二种方法只能在Activity里面使用,所以这里使用第一种方法。但是在写的过程中我又发现一个提醒:
有强迫症的我马上去官网查了一下,发现官网推荐的是另一个方法,于是马上就改用了另一个方法:
于是,我们现在就可以根据波浪的宽度和中心点绘制一条静态的波浪线,代码如下:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPath.reset(); mPath.moveTo(-mWL, mCenterY); //将path操作的起点移动到(-mWL,mCenterY) mPath.quadTo((-mWL * 3 / 4) , mCenterY + 60, (-mWL / 2), mCenterY); //画出第一段波纹的第一条曲线 mPath.quadTo((-mWL / 4) , mCenterY - 60, 0, mCenterY); //画出第一段波纹的第二条曲线 mPath.quadTo((mWL /4) , mCenterY + 60, (mWL / 2), mCenterY); //画出第二段波纹的第一条曲线 mPath.quadTo((mWL * 3/ 4) , mCenterY - 60, mWL, mCenterY); //画出第二段波纹的第二条曲线 mPath.lineTo(mScreenWidth, mScreenHeight); mPath.lineTo(0, mScreenHeight); mPath.close(); canvas.drawPath(mPath,mPaint); }
查看加载效果的时候发现一个坑,就是荣耀的真机显示绘制结果的时候左边居然有一条很宽的线,这个时候为了检查不是贝塞尔曲线的问题,我还在贝塞尔曲线X方向起点到终点绘制了一条直线,但是依然如此。
然后我用自己的小米5测试了一下,居然完全没有任何问题:
虽然后来发现是因为使用了ConstraintLayout布局,然后自定义控件左边距有8dp,然后给自定义控件写死了宽度,因此小米5的宽度刚刚合适,,,,,
修改了以后正常了,于是继续将静态的波浪线调整成动态的波浪线。其实逻辑是很简单,就是给自定义View一个点击事件,然后在时间中启动一个属性动画,动态的修改波浪线水平方向右移的位置,全部代码写出来:
/** * 自定义波浪图 * Created by 魏兴 on 2017/6/12. */public class WaveView1 extends View implements View.OnClickListener{ private static final String TAG = "WaveView"; private Context mContext; /** * 波纹段长度 */ private int mWL; /** * 波浪高度中点 */ private float mCenterY; private float mScreenWidth; private float mScreenHeight; private int mOffset; private int mWaveCount; private Path mPath; private Paint mPaint; private Paint mCyclePaint; public WaveView1(Context context) { super(context); this.mContext = context;// init(); } public WaveView1(Context context, @Nullable AttributeSet attrs) { super(context, attrs); this.mContext = context;// init(); } public WaveView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.mContext = context;// init(); } private void init() { WindowManager manager = (WindowManager) mContext .getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); Point po = new Point(); display.getSize(po); mWL = po.x; mCyclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mCyclePaint.setColor(Color.RED); mCyclePaint.setStyle(Paint.Style.FILL_AND_STROKE); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mPaint = new Paint(); // 创建画笔 mPaint.setColor(Color.BLUE); // 画笔颜色 - 黑色 mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); // 填充模式 - 填充 mPath = new Path(); mWL = w*3/4; mCenterY = h/2f; mScreenHeight = h; mScreenWidth = w; //加1.5:至少保证波纹有2个,至少2个才能实现平移效果 mWaveCount = (int) Math.round(mScreenWidth / mWL + 1.5); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPath.reset(); //移到屏幕外最左边 mPath.moveTo(-mWL + mOffset, mCenterY); for (int i = 0; i < mWaveCount; i++) { //正弦曲线 mPath.quadTo((-mWL * 3 / 4) + (i * mWL) + mOffset, mCenterY + 60, (-mWL / 2) + (i * mWL) + mOffset, mCenterY); mPath.quadTo((-mWL / 4) + (i * mWL) + mOffset, mCenterY - 60, i * mWL + mOffset, mCenterY); //测试红点// canvas.drawCircle((-mWL * 3 / 4) + (i * mWL) + mOffset, mCenterY + 60, 5, mCyclePaint);// canvas.drawCircle((-mWL / 2) + (i * mWL) + mOffset, mCenterY, 5, mCyclePaint);// canvas.drawCircle((-mWL / 4) + (i * mWL) + mOffset, mCenterY - 60, 5, mCyclePaint);// canvas.drawCircle(i * mWL + mOffset, mCenterY, 5, mCyclePaint); } //填充矩形 mPath.lineTo(mScreenWidth, mScreenHeight); mPath.lineTo(0, mScreenHeight); mPath.close(); canvas.drawPath(mPath, mPaint); } @Override public void onClick(View view) { try { ValueAnimator animator = ValueAnimator.ofInt(-mWL, mWL); animator.setDuration(1000); animator.setRepeatCount(ValueAnimator.INFINITE); animator.setInterpolator(new LinearInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mOffset = (int) animation.getAnimatedValue(); postInvalidate(); } }); animator.start(); } catch (Exception e) { e.printStackTrace(); } }}
然后在实例化控件以后调用Click方法(不能直接点击View,因为Click方法此处并没有响应,需要重写boolean onTouchEvent(MotionEvent event)方法来定义Click事件——我猜的哈,反正我直接点击的时候此处方法并没有响应)。于是我简单申明控件以后直接调用了Click方法。大坑出现了!
此处波浪图并没有动起来,而且属性方法里面打印的日志显示设置的ofint()方法参数(这个是成员变量,即波浪宽度)为900,偏移量为0。上网百度“属性动画失效”找不到资料,后来查了半天发现是因为Activity里面声明控件并调用了Click方法,这个时候宽度默认为0,因此属性动画的偏移量一直为0。
于是修改调用代码:
public class WaveActivity extends AppCompatActivity { private WaveView1 mWave; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_wave); mWave = (WaveView1) findViewById(R.id.waveView); final Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { handler.post(new Runnable() { @Override public void run() { mWave.onClick(null); } }); } },500); }}
续三阶贝塞尔曲线形成原理:
1.连接 A,B 形成 AB 线段,连接 B,C 形成 BC 线段,连接 C,D 形成 CD 线段。
2.在AB线段取一个点 E,BC 线段取一个点 F,CD 线段取一个点 G,使其满足条件: AE/AB = BF/BE = CG/CD。连接 E,F 形成线段 EF,连接 F,G 形成线段 FG。
3.在EF线段取一个点 H,FG 线段取一个点 I,使其满足条件: AE/AB = BF/BE = CG/CD = EH/EF = FI/FG。连接 H,I 形成线段 HI。
![]()
4.在 HI 线段取一个点 J,使其满足条件: AE/AB = BF/BE = CG/CD = EH/EF = FI/FG = HJ/HI。
![]()
5.而满足这些条件的所有的J点所形成的轨迹就是三阶贝塞尔曲线,动态过程如下:
![]()
方法预览:
public void quadTo (float x1, float y1, float x2, float y2)
绘制圆形:
![]()
修改坐标:
![]()
最终效果:
最后再来一张效果图:
参考资料:
Path从懵逼到精通(2)——贝塞尔曲线
Android自定义View——贝塞尔曲线实现水波纹效果
- 贝塞尔曲线原理及应用
- 贝塞尔曲线初探及原理
- [Android]贝塞尔曲线应用及QQ气泡拖动原理实践
- 贝塞尔曲线原理以及在android中的应用
- 贝塞尔曲线 原理
- 贝塞尔曲线原理
- 贝塞尔曲线移动 应用
- 贝塞尔曲线的应用
- 贝塞尔曲线的应用
- Android-贝塞尔曲线应用
- 贝塞尔曲线的应用
- 贝塞尔曲线应用
- Bezier曲线的原理 及 二次Bezier曲线的实现 .
- Bezier曲线的原理 及 二次Bezier曲线的实现
- Ferguson曲线原理及绘制程序
- 贝塞尔曲线的数学原理
- 贝塞尔曲线的数学原理
- 贝塞尔曲线原理简单阐述
- python爬虫获取淘宝妹子信息和相片
- Android解决UnsupportedOperationException异常!
- python---dictionary
- 517.Super Washing Machines
- 温故而知新系列:Java反射机制详解
- 贝塞尔曲线原理及应用
- 6.12-6.14 JLL--实习日志--filetype-change功能 !+js不工作+js函数嵌套使用
- Azure deploy with git
- 单反相机自带云台螺丝大部分是1/4的
- 监听器监听日志,实时读取日志文件,把读取到的数据入库
- 【Unity】FBX和Prefab 同名造成的问题
- Matlab figure传入数据到figure(一)
- linux驱动开发—基于Device tree机制的驱动编写
- opencv 图像膨胀腐蚀(二)