贝塞尔曲线:原理、自定义贝塞尔曲线View、使用!!!
一、原理
转自:http://www.2cto.com/kf/201401/275838.html
Android动画学习Demo(3) 沿着贝塞尔曲线移动的Property Animation
Property Animation中最重要,最基础的一个类就是ValueAnimator了。Property Animation利用ValueAnimator来跟踪记录对象属性已经变化了多长时间及当前这个时间点的值。
而在ValueAnimator中,又封装了两个类:
1)TimeInterpolator,也称插值器,是来计算当前动画运动的一个跟时间有关系的比例因子。
2)TypeEvaluator,这个就是利用TimeInterpolator计算出来的因子来算出当前动画运行到的位置。
这样讲太抽象了,我们还是先用自然语言来描述一下整个动画的过程吧。
动画的原理,其实就是一帧帧的画面顺着时间顺序,在我们眼中形成视觉残留的效果。所以在动画中,时间的概念是很重要的,只有时间的变化,才能形成动画效果。
0)动画准备开始,我们在这里设置了一个动画的时长(duration),如果不设置的话,动画的时长就是300毫秒,每个画面显示的时间是10ms。同时也设置了某个属性值在这个时间段中变化的起始值start和结束值end,意思就是说,在duration时间中,属性值要从start 变化到 end。
1)动画开始了,过了 t 时间,ValueAnimator会根据 t / duration 算出一个时间消逝的比例因子(elapsed fraction),意思就是说,现在时间到 t了,我们假设总的时间的duration就是3t吧,那就是现在已经过了1/3时间了,那这个属性值也应该要变化到1/3了。
2)动画继续,现在到了2t了,那么现在动画时间已经过了2/3了,那么这个属性值是不是已经变化到2/3了呢。
3)现在到了3t了,动画结束了,属性值就已经从start变成end值了。
那么现在问题来了,如果都是这样算的话,那动画不就一直是很匀速的了吗?是的,如果用的是LinearInterpolator的话。
TimeInterpolator
TimeInterpolator就是用来改变我们这个动画速度的这样一个类了。为什么叫插值器呢?我理解就是,本来动画踩着时间点,一步一步走的挺好的,它硬生生在中间的插了些值进去,或者抽了一些值出去,让整条路变得不好走了,前面变然变上坡了,走起来就慢了,本来过去 t 时间之后,动画的画面也应该在1/3的位置了,但是路不好走,它就走不到1/3,而可能只走了1/4了,而后面是下坡,一激动,步伐就快了许多,又赶上去了,但是不管中间的路怎么变化,时间点一到,一定是刚刚好落在最终的位置上的。 Android中提供的Interpolator主要有九个: 1)AccelerateDecelerateInterpolator:先加速再减速。
2)AccelerateInterpolator:一直加速。
3)AnticipateInterpolator:先往后一下,再嗖的一声一往无前。
4)AnticipateOvershootInterpolator:先往后一下,再一直往前超过终点,再往回收一下。
5)BounceInterpolator:最后像个小球弹几下。
6)CycleInterpolator:重复几次,感觉就是环形进度条那种,具体我还没试过。
7)DecelerateInterpolator:一直减速。
8)LinearInterpolator:线性,这个就是我们上面讲到的很均匀的了。
9)OvershootInterpolator:到了终点之后,超过一点,再往回走。有个参数可以定义,超过的力度。
这些Interpolator都是实现了TimeInterpolator接口的类,它们只需要实现一个方法:getInterpolation (float input),将这个input根据自己的需求重新计算这个比例
第一步:当到了某时间t之后,ValueAnimator会算出某个比例 fraction = t / duration,而Interpolator会接收这个比例fraction,再调用其getInterpolation方法将这个比例因子重新计算一下,返回一个新的比例因子,比如LinearInterpolator实现的方法就是什么都不变,如下:
|
1
2
3
|
public float getInterpolation(float input) { return input;} |
而 AccelerateDecelerateInterpolator 则会利用余弦函数的对称性变化计算这个比例因子,如下:
|
1
2
3
|
public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;} |
如上所述,通过第一步 Interpolator 的插值,我们会得到一个比例因子,接下来就是要用到我们的TypeEvaluator了。
TypeEvaluator
第二步:TypeEvaluator会接受第一步中算出来的比例因子,然后算出当前的属性的值,将其返回给ValuaAnimator,由ValueAnimator去设置对应属性的值。 比如,我自己写了一个BezierTypeEvaluator,根据时间的变化来让一个按钮沿着贝塞尔曲线移动,如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
class BezierEvaluator implements TypeEvaluator<pointf>{ @Override public PointF evaluate(float fraction, PointF startValue, PointF endValue) { final float t = fraction; float oneMinusT = 1.0f - t; PointF point = new PointF(); PointF point0 = (PointF)startValue; PointF point1 = new PointF(); point1.set(width, 0); PointF point2 = new PointF(); point2.set(0, height); PointF point3 = (PointF)endValue; point.x = oneMinusT * oneMinusT * oneMinusT * (point0.x) + 3 * oneMinusT * oneMinusT * t * (point1.x) + 3 * oneMinusT * t * t * (point2.x) + t * t * t * (point3.x); point.y = oneMinusT * oneMinusT * oneMinusT * (point0.y) + 3 * oneMinusT * oneMinusT * t * (point1.y) + 3 * oneMinusT * t * t * (point2.y) + t * t * t * (point3.y); return point; } }</pointf> |
自定义TypeEvaluator,我们必须实现其evaluate方法,目的就是计算出目前的对象对应属性的值,而它会接收三个参数,一个是上文中通过interpolator算出的比例,还有我们在创建动画时设置的起始值和结束值。
ValueAnimator.AnimatorUpdateListener
既然我们已经算出了在 t 时刻,对象的某个属性的值,那么我们要把这个值重新设置到对象中,才能够起作用啊。所以ValueAnimator也提供了一个内部的Listener接口,其只有一个方法,就是获取TypeEvaluator计算出来的值,并设置给对应的属性,比如我们Demo中的代码:
|
1
2
3
4
5
6
7
8
|
valueAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { PointF pointF = (PointF)animation.getAnimatedValue(); button.setX(pointF.x); button.setY(pointF.y); } }); |
我们在这里改变Button的X坐标和Y坐标,从而改变其具体的位置。至于validate,然后引起重新绘制的过程,对于这些基本的属性,ValueAnimator已经帮我们实现了。 下面,我们看看效果图,然后再总结一下ValueAnimator的实现机制。
下载
嗯,这一篇文章大概就是这样了,大家如果有兴趣了解Property Animation的应用的话,可以看一下Android动画学习Demo(2) 关于Property Animation的用法及总结
最后还是要提醒一下,Property Animation是3.0以后才支持的,如果大家想在3.0之前去应用这些属性的话,可以去下载jake wharton的nineoldandroids包,基本上都可以直接将方法套上,不过据我实验,还是有某些方法,比如 PropertyValuesHolder就会有些bug出现的。我把这个包也放在这里吧,
点击NineoldAndroids下载
二、自定义贝塞尔曲线View
转自:http://www.2cto.com/kf/201604/497130.html
Android 自定义View高级特效,神奇的贝塞尔曲线
效果图

效果图中我们实现了一个简单的随手指滑动的二阶贝塞尔曲线,还有一个复杂点的,穿越所有已知点的贝塞尔曲线。学会使用贝塞尔曲线后可以实现例如QQ红点滑动删除啦,360动态球啦,bulabulabula~
什么是贝塞尔曲线?
贝赛尔曲线(Bézier曲线)是电脑图形学中相当重要的参数曲线。更高维度的广泛化贝塞尔曲线就称作贝塞尔曲面,其中贝塞尔三角是一种特殊的实例。贝塞尔曲线于1962年,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由Paul de Casteljau于1959年运用de Casteljau算法开发,以稳定数值的方法求出贝塞尔曲线。
读完上述贝塞尔曲线简介我还是一头雾水,来个示例呗。
示例
线性贝塞尔曲线
给定点P0、P1,线性贝塞尔曲线只是一条两点之间的直线。这条线由下式给出:

二次方贝塞尔曲线
二次方贝塞尔曲线的路径由给定点P0、P1、P2的函数B(t)追踪:


三次方贝塞尔曲线
P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1或P2;公式如下:


N次方贝塞尔曲线
身为三维生物超出三维我很方,这里只给示例图。想具体了解的同学请左转度娘。

就当没看过上面
Android在API=1的时候就提供了贝塞尔曲线的画法,只是隐藏在Path#quadTo()和Path#cubicTo()方法中,一个是二阶贝塞尔曲线,一个是三阶贝塞尔曲线。当然,如果你想自己写个方法,依照上面贝塞尔的表达式也是可以的。不过一般没有必要,因为Android已经在native层为我们封装好了二阶和三阶的函数。
从一个二阶贝塞尔开始
自定义一个BezierView
初始化各个参数,花3s扫一下即可。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<code class="hljs java"> private Paint mPaint; private Path mPath; private Point startPoint; private Point endPoint; // 辅助点 private Point assistPoint; public BezierView(Context context) { this(context, null); } public BezierView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public BezierView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { mPaint = new Paint(); mPath = new Path(); startPoint = new Point(300, 600); endPoint = new Point(900, 600); assistPoint = new Point(600, 900); // 抗锯齿 mPaint.setAntiAlias(true); // 防抖动 mPaint.setDither(true); }</code> |
在onDraw中画二阶贝塞尔
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<code class="hljs avrasm"> // 画笔颜色 mPaint.setColor(Color.BLACK); // 笔宽 mPaint.setStrokeWidth(POINTWIDTH); // 空心 mPaint.setStyle(Paint.Style.STROKE); // 重置路径 mPath.reset(); // 起点 mPath.moveTo(startPoint.x, startPoint.y); // 重要的就是这句 mPath.quadTo(assistPoint.x, assistPoint.y, endPoint.x, endPoint.y); // 画路径 canvas.drawPath(mPath, mPaint); // 画辅助点 canvas.drawPoint(assistPoint.x, assistPoint.y, mPaint);</code> |
上面注释很清晰就不赘述了。示例中贝塞尔是可以跟着手指的滑动而变化,我一拍榴莲,肯定是复写了onTouchEvent()!
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<code class="hljs cs"> @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: assistPoint.x = (int) event.getX(); assistPoint.y = (int) event.getY(); Log.i(TAG, "assistPoint.x = " + assistPoint.x); Log.i(TAG, "assistPoint.Y = " + assistPoint.y); invalidate(); break; } return true; }</code> |
最后将我们自定义的BezierView添加到布局文件中。至此一个简单的二阶贝塞尔曲线就完成了。假设一下,在向下拉动的过程中,在曲线上增加一个“小超人”,360动态清理是不是就出来了呢?有兴趣的可以自己拓展下。
以一个三阶贝塞尔结束
天气预报曲线图示例
(图一)
(图二)
概述
要想得到上图的效果,需要二阶贝塞尔和三阶贝塞尔配合。具体表现为,第一段和最后一段曲线为二阶贝塞尔,中间N段都为三阶贝塞尔曲线。
思路
先根据相邻点(P1,P2, P3)计算出相邻点的中点(P4, P5),然后再计算相邻中点的中点(P6)。然后将(P4,P6, P5)组成的线段平移到经过P2的直线(P8,P2,P7)上。接着根据(P4,P6,P5,P2)的坐标计算出(P7,P8)的坐标。最后根据P7,P8等控制点画出三阶贝塞尔曲线。
点和线的解释
黑色点:要经过的点,例如温度 蓝色点:两个黑色点构成线段的中点 黄色点:两个蓝色点构成线段的中点 灰色点:贝塞尔曲线的控制点 红色线:黑色点的折线图 黑色线:黑色点的贝塞尔曲线,也是我们最终想要的效果
声明
为了方便讲解以及读者的理解。本篇以图一效果为例进行讲解。BezierView坐标都是根据屏幕动态生成的,想要图二的效果只需修改初始坐标,不用对代码做很大的修改即可实现。
那么,开始吧!
初始化参数
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<code class="hljs java"> private static final String TAG = "BIZIER"; private static final int LINEWIDTH = 5; private static final int POINTWIDTH = 10; private Context mContext; /** 即将要穿越的点集合 */ private List<point> mPoints = new ArrayList<>(); /** 中点集合 */ private List<point> mMidPoints = new ArrayList<>(); /** 中点的中点集合 */ private List<point> mMidMidPoints = new ArrayList<>(); /** 移动后的点集合(控制点) */ private List<point> mControlPoints = new ArrayList<>(); private int mScreenWidth; private int mScreenHeight; private void init(Context context) { mPaint = new Paint(); mPath = new Path(); // 抗锯齿 mPaint.setAntiAlias(true); // 防抖动 mPaint.setDither(true); mContext = context; getScreenParams(); initPoints(); initMidPoints(this.mPoints); initMidMidPoints(this.mMidPoints); initControlPoints(this.mPoints, this.mMidPoints , this.mMidMidPoints); }</point></point></point></point></code> |
第一个函数获取屏幕宽高就不说了。紧接着初始化了初始点、中点、中点的中点、控制点。我们一个个的跟进。首先是初始点。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<code class="hljs java"> /** 添加即将要穿越的点 */ private void initPoints() { int pointWidthSpace = mScreenWidth / 5; int pointHeightSpace = 100; for (int i = 0; i < 5; i++) { Point point; // 一高一低五个点 if (i%2 != 0) { point = new Point((int) (pointWidthSpace*(i + 0.5)), mScreenHeight/2 - pointHeightSpace); } else { point = new Point((int) (pointWidthSpace*(i + 0.5)), mScreenHeight/2); } mPoints.add(point); } }</code> |
这里循环创建了一高一低五个点,并添加到List mPoints中。上文说道图一到图二只需修改这里的初始点即可。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<code class="hljs java"> /** 初始化中点集合 */ private void initMidPoints(List<point> points) { for (int i = 0; i < points.size(); i++) { Point midPoint = null; if (i == points.size()-1){ return; }else { midPoint = new Point((points.get(i).x + points.get(i + 1).x)/2, (points.get(i).y + points.get(i + 1).y)/2); } mMidPoints.add(midPoint); } } /** 初始化中点的中点集合 */ private void initMidMidPoints(List<point> midPoints){ for (int i = 0; i < midPoints.size(); i++) { Point midMidPoint = null; if (i == midPoints.size()-1){ return; }else { midMidPoint = new Point((midPoints.get(i).x + midPoints.get(i + 1).x)/2, (midPoints.get(i).y + midPoints.get(i + 1).y)/2); } mMidMidPoints.add(midMidPoint); } }</point></point></code> |
这里算出中点集合以及中点的中点集合,小学数学题没什么好说的。唯一需要注意的是他们数量的差别。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<code class="hljs avrasm"> /** 初始化控制点集合 */ private void initControlPoints(List<point> points, List<point> midPoints, List<point> midMidPoints){ for (int i = 0; i < points.size(); i ++){ if (i ==0 || i == points.size()-1){ continue; }else{ Point before = new Point(); Point after = new Point(); before.x = points.get(i).x - midMidPoints.get(i - 1).x + midPoints.get(i - 1).x; before.y = points.get(i).y - midMidPoints.get(i - 1).y + midPoints.get(i - 1).y; after.x = points.get(i).x - midMidPoints.get(i - 1).x + midPoints.get(i).x; after.y = points.get(i).y - midMidPoints.get(i - 1).y + midPoints.get(i).y; mControlPoints.add(before); mControlPoints.add(after); } } }</point></point></point></code> |
大家需要注意下这个方法的计算过程。以图一(P2,P4, P6,P8)为例。现在P2、P4、P6的坐标是已知的。根据由于(P8, P2)线段由(P4, P6)线段平移而来,所以可得如下结论:P2 - P6 = P8 - P4 。即P8 = P2 - P6 + P4。其余同理。
画辅助点以及对比折线图
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<code class="hljs mel"> @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // *********************************************************** // ************* 贝塞尔进阶--曲滑穿越已知点 ********************** // *********************************************************** // 画原始点 drawPoints(canvas); // 画穿越原始点的折线 drawCrossPointsBrokenLine(canvas); // 画中间点 drawMidPoints(canvas); // 画中间点的中间点 drawMidMidPoints(canvas); // 画控制点 drawControlPoints(canvas); // 画贝塞尔曲线 drawBezier(canvas); }</code> |
可以看到,在画贝塞尔曲线之前我们画了一系列的辅助点,还有和贝塞尔曲线作对比的折线图。效果如图一。辅助点的坐标全都得到了,基本的画画就比较简单了。有能力的可跳过下面这段,直接进入drawBezier(canvas)方法。基本的画画这里只贴代码,如有疑问可评论或者私信。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
<code class="hljs java"> /** 画原始点 */ private void drawPoints(Canvas canvas) { mPaint.setStrokeWidth(POINTWIDTH); for (int i = 0; i < mPoints.size(); i++) { canvas.drawPoint(mPoints.get(i).x, mPoints.get(i).y, mPaint); } } /** 画穿越原始点的折线 */ private void drawCrossPointsBrokenLine(Canvas canvas) { mPaint.setStrokeWidth(LINEWIDTH); mPaint.setColor(Color.RED); // 重置路径 mPath.reset(); // 画穿越原始点的折线 mPath.moveTo(mPoints.get(0).x, mPoints.get(0).y); for (int i = 0; i < mPoints.size(); i++) { mPath.lineTo(mPoints.get(i).x, mPoints.get(i).y); } canvas.drawPath(mPath, mPaint); } /** 画中间点 */ private void drawMidPoints(Canvas canvas) { mPaint.setStrokeWidth(POINTWIDTH); mPaint.setColor(Color.BLUE); for (int i = 0; i < mMidPoints.size(); i++) { canvas.drawPoint(mMidPoints.get(i).x, mMidPoints.get(i).y, mPaint); } } /** 画中间点的中间点 */ private void drawMidMidPoints(Canvas canvas) { mPaint.setColor(Color.YELLOW); for (int i = 0; i < mMidMidPoints.size(); i++) { canvas.drawPoint(mMidMidPoints.get(i).x, mMidMidPoints.get(i).y, mPaint); } } /** 画控制点 */ private void drawControlPoints(Canvas canvas) { mPaint.setColor(Color.GRAY); // 画控制点 for (int i = 0; i < mControlPoints.size(); i++) { canvas.drawPoint(mControlPoints.get(i).x, mControlPoints.get(i).y, mPaint); } }</code> |
画贝塞尔曲线
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<code class="hljs avrasm"> /** 画贝塞尔曲线 */ private void drawBezier(Canvas canvas) { mPaint.setStrokeWidth(LINEWIDTH); mPaint.setColor(Color.BLACK); // 重置路径 mPath.reset(); for (int i = 0; i < mPoints.size(); i++){ if (i == 0){// 第一条为二阶贝塞尔 mPath.moveTo(mPoints.get(i).x, mPoints.get(i).y);// 起点 mPath.quadTo(mControlPoints.get(i).x, mControlPoints.get(i).y,// 控制点 mPoints.get(i + 1).x,mPoints.get(i + 1).y); }else if(i < mPoints.size() - 2){// 三阶贝塞尔 mPath.cubicTo(mControlPoints.get(2*i-1).x,mControlPoints.get(2*i-1).y,// 控制点 mControlPoints.get(2*i).x,mControlPoints.get(2*i).y,// 控制点 mPoints.get(i+1).x,mPoints.get(i+1).y);// 终点 }else if(i == mPoints.size() - 2){// 最后一条为二阶贝塞尔 mPath.moveTo(mPoints.get(i).x, mPoints.get(i).y);// 起点 mPath.quadTo(mControlPoints.get(mControlPoints.size()-1).x,mControlPoints.get(mControlPoints.size()-1).y, mPoints.get(i+1).x,mPoints.get(i+1).y);// 终点 } } canvas.drawPath(mPath,mPaint); }</code> |
注释太详细,都没什么好写的了。不过这里需要注意判断里面的条件,对起点和终点的判断一定要理解。要不然很可能会送你一个ArrayIndexOutOfBoundsException。
结束
贝塞尔曲线可以实现很多绚丽的效果,难的不是贝塞尔,而是good idea。
三、使用
转自:
研究一下贝塞尔曲线.
- /**
- * 三阶贝塞尔方程
- */
- private class BeizerEvaluator implements TypeEvaluator<PointF> {
- private PointF point1;
- private PointF point2;
- private PointF pointF;
- public BeizerEvaluator(PointF point1, PointF point2) {
- this.point1 = point1;
- this.point2 = point2;
- }
- @Override
- public PointF evaluate(float time, PointF start, PointF end) {
- float timeLeft = 1.0f - time;
- pointF = new PointF();//结果
- PointF point0 = start;//起点
- PointF point3 = end;//终点
- pointF.x = timeLeft * timeLeft * timeLeft * (point0.x)
- + 3 * timeLeft * timeLeft * time * (point1.x)
- + 3 * timeLeft * time * time * (point2.x)
- + time * time * time * (point3.x);
- pointF.y = timeLeft * timeLeft * timeLeft * (point0.y)
- + 3 * timeLeft * timeLeft * time * (point1.y)
- + 3 * timeLeft * time * time * (point2.y)
- + time * time * time * (point3.y);
- return pointF;
- }
- }
- //初始化一个BezierEvaluator
- BeizerEvaluator evaluator = new BeizerEvaluator(getPointF(1), getPointF(2));
- ValueAnimator animator = ValueAnimator.ofObject(evaluator, new PointF(rand.nextInt(getWidth()), 0), new PointF(rand.nextInt(getWidth()), mHeight - dHeight));//随机
- animator.addUpdateListener(new BezierListenr(tag));
- animator.setInterpolator(interpolators[rand.nextInt(3)]);
- animator.setTarget(tag);
- animator.setDuration(3000);
然后在需要更新的时候去Update设置imageVIew的路径:
- private class BezierListenr implements ValueAnimator.AnimatorUpdateListener {
- private View target;
- public BezierListenr(View target) {
- this.target = target;
- }
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- PointF pointF = (PointF) animation.getAnimatedValue();
- ViewHelper.setX(target, pointF.x);
- ViewHelper.setY(target, pointF.y);
- ViewHelper.setAlpha(target, 1 - animation.getAnimatedFraction());
- }
- }
GitHub:https://github.com/q422013/BezierFlower
贝塞尔曲线:原理、自定义贝塞尔曲线View、使用!!!的更多相关文章
- Unity3d游戏中自定义贝塞尔曲线编辑器[转]
关于贝塞尔曲线曲线我们再前面的文章提到过<Unity 教程之-在Unity3d中使用贝塞尔曲线>,那么本篇文章我们来深入学习下,并自定义实现贝塞尔曲线编辑器,贝塞尔曲线是最基本的曲线,一般 ...
- iOS-贝塞尔曲线之自定义饼图
代码地址如下:http://www.demodashi.com/demo/11981.html 项目中需要统计数据展现, 采用了饼图形式展现. 第一步: 了解下贝塞尔曲线相关概念 贝塞尔曲线相关概念: ...
- Bezier曲线原理—动态解释
公式线性公式给定点P0.P1,线性贝兹曲线只是一条两点之间的直线.且其等同于线性插值.这条线由下式给出: 一阶贝赛尔曲线上的由两个点确定 P0 和P1,当t在0--->1区间上递增时,根据此会得 ...
- canvas绘制自定义的曲线,以椭圆为例,通俗易懂,童叟无欺
本篇文章,将讲述如何通过自定义的曲线函数,使用canvas的方式进行曲线的绘制. 为了通俗易懂,将以大家熟悉的椭圆曲线为例,进行椭圆的绘制.至于其他比较复杂的曲线,用户只需通过数学方式建立起曲线函数, ...
- 自定义控制器的View(loadView)及其注意点
*:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...
- android显示通知栏Notification以及自定义Notification的View
遇到的最大的问题是监听不到用户清除通知栏的广播.所以是不能监听到的. 自定义通知栏的View,然后service运行时更改notification的信息. /** * Show a notificat ...
- P-R曲线及与ROC曲线区别
一.P-R曲线 P-R曲线刻画查准率和查全率之间的关系,查准率指的是在所有预测为正例的数据中,真正例所占的比例,查全率是指预测为真正例的数据占所有正例数据的比例. 即:查准率P=TP/(TP + FP ...
- Atitit.struts排除url 的设计and 原理 自定义filter 排除特定url
Atitit.struts排除url 的设计and 原理 自定义filter 排除特定url 1.1. 原理流程1 2. Invoke1 3. StrutsX2 1.1. 原理流程 读取struts配 ...
- 自定义android RadioButton View,添加较为灵活的布局处理方式
android的RadioButton的使用历来都让人比较头疼,如在布局方面,图案.文字无法分别设置padding等,另外,低版本的android RadioGroup不支持换行排列的RadioBut ...
随机推荐
- [置顶] 【cocos2d-x入门实战】微信飞机大战之六:子弹层的处理
这一篇将会处理完子弹层的其他要点. 1.子弹的初始位置 子弹的初始位置在飞机的机头位置,因为飞机在游戏的过程中会随着玩家的触摸而改变其位置,所以,子弹的初始位置只能以当前飞机位置为基准进行添加. CC ...
- 2013第49周一jsp标签
2013第49周一jsp标签 今天在调试一些前台页面上的问题,在处理structs标签.jstl标签时遇到了些麻烦,为了调用后台数据字典中的类方法,开始在<c:forEach>中尝试怎么样 ...
- Asp.net MVC 3 防止 Cross-Site Request Forgery (CSRF)原理及扩展 安全 注入
原理:http://blog.csdn.net/cpytiger/article/details/8781457 原文地址:http://www.cnblogs.com/wintersun/archi ...
- Sqrt(x) 解答
Question Implement int sqrt(int x). Compute and return the square root of x. Solution 1 -- O(log n) ...
- UVA 10594-Date Flow(无向图的最小费用网络流+题目给的数据有误)
题意:给一个有N个点的无向图,要求从1向N传送一定的数据,每条边的容量是一定的,如果能做到,输出最小的费用,否则输出Impossible. 解析:由于是无向图,所以每个有连接的两个点要建4条边,分别是 ...
- POJ 1742 Coins(多重背包) DP
参考:http://www.hankcs.com/program/cpp/poj-1742-coins.html 题意:给你n种面值的硬币,面值为a1...an,数量分别为c1...cn,求问,在这些 ...
- bootstrap 兼容IE8设置
<!--[if lt IE 9]> <script src="//cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js" ...
- 归纳下js面向对象的几种常见写法
//定义Circle类,拥有成员变量r,常量PI和计算面积的成员函数area() 1.工厂方式 var Circle = function() { var obj = new Object(); ob ...
- JS nodeType返回类型
JS nodeType返回类型 前几天朋友正好问道 这个 js的nodeType是个什么概念(做浏览器底层的)正好遇到这篇文章可以向大家解释下 将HTML DOM中几个容易常用的属性做下记录: nod ...
- Oracle 学习笔记 19 -- 触发器和包浅析(PL/SQL)
触发器是存放在数据库中的一种特殊类型的子程序.不能被用户直接调用,而是当特定事件或操作发生时由系统自己主动 调用执行.触发器不能接受參数.所以执行触发器就叫做触发或点火.Oracle事件指的是数据库的 ...