700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > canvas贝塞尔曲线爱心_贝塞尔曲线之爱心点赞代码全解析!| CSDN 博文精选

canvas贝塞尔曲线爱心_贝塞尔曲线之爱心点赞代码全解析!| CSDN 博文精选

时间:2024-01-30 07:13:02

相关推荐

canvas贝塞尔曲线爱心_贝塞尔曲线之爱心点赞代码全解析!| CSDN 博文精选

原标题:贝塞尔曲线之爱心点赞代码全解析!| CSDN 博文精选

作者 | 威威喵

责编 | 屠敏

出品 | CSDN 博客

直接步入正题,我们要实现的是一个 Android 客户端应用里面的一种点赞效果,比如你点一下那个爱心型的图片,就会产生一个小爱心,而且会以曲线的方式进行上升,直到它消失为止。

文字描述只能是这样的了,我们直接来看动态图吧,效果更直观。

本案例是由我自己写的,因为之前对这个贝塞尔曲线有一点点了解,还有无意间看到了这个效果,觉得挺赞的,就顺便写了一下demo,并且学习了一些关于贝塞尔曲线的相关知识。

首先,要看懂本案例的代码,你需要具备 Android 自定义 View 的基本知识,并且你还有了解一些关于贝塞尔曲线的公式和算法。不过没关系,我们并不需要对贝塞尔深刻了解,只要会基本的根据公式,套用代码就好了。

来看一下贝塞尔曲线的一些相关知识,我也是从大佬的博客中学习得来的。我们来看看什么是贝塞尔曲线?

贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。

更形象的就直接来看动态图吧。

一阶贝塞尔曲线公式:由 P0 至 P1 的连续点, 描述的一条线段

二阶贝塞尔曲线公式:曲线的切线 P0-P1、P1-P2 组成的运动轨迹

三阶贝塞尔曲线公式:

从上面的动态图,可以很直观的看到曲线的计算公式和它的路径形成的规律。而我们要实现的效果,运用的就是三阶贝塞尔曲线的公式。首先,需要确定曲线的路径的话,就必须先确定它的点位置。我以是这样的方式来确定点位置的,如下图:

我使用的就是这三个点,两边都可以,随机的选择一边。这样的话,我们的曲线就在屏幕内,它的形成大致和我们上面的动态图有点类似。那么看代码:

privatePoint[] setPoint1(){

Point[] points = newPoint[]{

newPoint(mLoveX, mLoveY),

newPoint(0, mCanvasHeight / 2),

newPoint(mCanvasWidth + 20, -mLoveWidth - 10),

};

returnpoints;

}

privatePoint[] setPoint2(){

Point[] points = newPoint[]{

newPoint(mLoveX, mLoveY),

newPoint(mCanvasWidth, mCanvasHeight / 2),

newPoint(-mLoveWidth - 20, -mLoveWidth - 10),

};

returnpoints;

}

上面代码是初始化两种点的坐标,mLoveX,mLoveY 表示我们的爱心起始的位置。第一个集合点,对应图中的蓝线,第二个集合点,就对应橙色了。

接下来是重点部分,也就是把贝塞尔曲线公式转化为代码的形式,根据动态图中有一个 t 值,它的区间是 [0,1] 的,这个也很形象,t 从 0 变到 1 时,意味着曲线已经绘制完了。看代码:

/**

* 根据点得到曲线的路径上的点,k 是变化趋势

*/

privatePoint deCasteljau(Point[] points, floatk){

final intn = points.length;

for(inti = 1; i <= n; i++)

for(intj = 0; j < n - i; j++) {

points[j].x = (int) ((1- k) * points[j].x + k * points[j + 1].x);

points[j].y = (int) ((1- k) * points[j].y + k * points[j + 1].y);

}

returnpoints[0];

}

刚刚我们定义的两种点的集合,就可以将它传入了,这样根据 k 值的变化,就可以得到对应位置曲线上的点坐标。接下来,我们的任务就是开启一个子线程去跟新 k 值,将 k 值有 0 加到 1,然后返回的每个 point 对象,就是整条曲线的坐标散点。执行子线程获取点的代码:

mLoveThread = newThread(newRunnable {

@Override

publicvoidrun{

while(k < 1) {

k += 0.01;

Point point = deCasteljau(mPoints, k);

mLoveX = point.x;

mLoveY = point.y;

if(mLoveY <= -mLoveWidth || mLoveY >= mCanvasHeight) {

k = 1;

}

if(mLoveX <= -mLoveWidth || mLoveX >= mCanvasWidth) {

k = 1;

}

postInvalidate;//异步刷新

try{

Thread.sleep(80);

} catch(InterruptedException e) {

e.printStackTrace;

}

}

}

});

通过上面代码,我们就可以获取爱心图片的 x,y 坐标值了,然后再通过 onDraw 里面将它进行绘制就搞定啦。

@Override

protectedvoidonDraw(Canvas canvas){

super.onDraw(canvas);

mCanvasWidth = canvas.getWidth;

mCanvasHeight = canvas.getHeight;

mLoveBitmapX = mCanvasWidth / 2- mLoveBitmapWidth / 2;

mLoveBitmapY = mCanvasHeight - 2* mLoveBitmapHeight;

drawLoveBitmap(canvas);

canvas.drawBitmap(mDefLove, mLoveX, mLoveY, mPaint);

//随便画的

canvas.drawText("点赞", mCanvasWidth / 2- mPaint.getTextSize, mLoveBitmapY + mLoveBitmapHeight + 100, mPaint);

canvas.drawLine(0, mLoveBitmapY + mLoveBitmapHeight + 20, mCanvasWidth, mLoveBitmapY + mLoveBitmapHeight + 20, mPaint);

}

这里的爱心,我使用的是六张不同的图片,我之前想尝试使用爱心函数公式来绘制的,不过也放弃了,计算太慢了,每个爱心算出来都要停顿一下,只好换图片的形式。

最后提一下就是点击这个图片才绘制的功能,我是在 onTouchEvent 中拿到点击的坐标位置,然后去判断它的点击位置是不是在那个爱心图片里面,代码如下:

privatebooleanisTouchLoveArea(inttouchX, inttouchY){

returntouchX >= mLoveBitmapX && touchX <= mLoveBitmapX + mLoveBitmapWidth

&& touchY > mLoveBitmapY && touchY <= mLoveBitmapY + mLoveBitmapHeight;

}

好了,最后也没什么好介绍的了,剩下的基本都是自定义 View 的知识,我们主要是关注这个贝塞尔曲线是如何绘制的就好,那么完整代码如下:

packagecom.example.xww.myapplication;

importandroid.content.Context;

importandroid.graphics.Bitmap;

importandroid.graphics.BitmapFactory;

importandroid.graphics.Canvas;

importandroid.graphics.Paint;

importandroid.graphics.Point;

importandroid.os.Build;

importandroid.support.annotation.Nullable;

importandroid.support.annotation.RequiresApi;

importandroid.util.AttributeSet;

importandroid.view.MotionEvent;

importandroid.view.View;

importjava.util.Random;

importjava.util.concurrent.ExecutorService;

importjava.util.concurrent.Executors;

/**

* @authorxww

* @desciption: 点赞时爱心飘了,爱心路径绘制的是贝塞尔曲线

* @博客:/smile_running

* @date/7/30

* @time20:59

*/

@RequiresApi(api = Build.VERSION_CODES.N)

publicclassLoveViewextendsView{

privatePaint mPaint;

//爱心图片

privateBitmap mLoveBitmap;

privateBitmap mLove1;

privateBitmap mLove2;

privateBitmap mLove3;

privateBitmap mLove4;

privateBitmap mLove5;

privateBitmap mLove6;

privateBitmap mDefLove;

privateintmLoveWidth;

privateintmLoveX;

privateintmLoveY;

//图片绘制的 x,y 坐标

privateintmLoveBitmapX;

privateintmLoveBitmapY;

//图片的宽、高

privateintmLoveBitmapWidth;

privateintmLoveBitmapHeight;

// 画布宽、高

privateintmCanvasWidth;

privateintmCanvasHeight;

//触摸点

privateintmTouchX;

privateintmTouchY;

privateExecutorService mExecutorService;

privateThread mLoveThread;

//随机数

privateRandom mRandom;

privatefloatk;//曲线斜率 k:[0,1]

privatePoint[] mPoints;//构成曲线随机点集合

@Override

protectedvoidonMeasure(intwidthMeasureSpec, intheightMeasureSpec){

setMeasuredDimension(measureSpecWidth(widthMeasureSpec), measureSpecHeigth(heightMeasureSpec));

}

/**

* EXACTLY :精确值,即 64dp 这样的具体值

* AT_MOST :最大值,即 wrap_content 类型,可以达到父 View 一样的大小

* UNSPECIFIED :未指定,即这个 View 可以无限大

*

* @paramwidthMeasureSpec 传入的 width 值

* @return宽度值

*/

privateintmeasureSpecWidth(intwidthMeasureSpec){

intmode = MeasureSpec.getMode(widthMeasureSpec);

intsize = MeasureSpec.getSize(widthMeasureSpec);

returnmode == MeasureSpec.EXACTLY ? size : Math.min(200, size);

}

privateintmeasureSpecHeigth(intheightMeasureSpec){

intmode = MeasureSpec.getMode(heightMeasureSpec);

intsize = MeasureSpec.getSize(heightMeasureSpec);

returnmode == MeasureSpec.EXACTLY ? size : Math.min(200, size);

}

privatevoidinit{

initPaint;

initBitmap;

mRandom = newRandom;

mExecutorService = Executors.newWorkStealingPool(6);

}

privatevoidinitBitmap{

mLoveBitmap = BitmapFactory.decodeResource(getResources, R.drawable.loveclick);

mLoveBitmap = Bitmap.createScaledBitmap(mLoveBitmap, 180, 180, false);

mLoveBitmapWidth = mLoveBitmap.getWidth;

mLoveBitmapHeight = mLoveBitmap.getHeight;

mLove1 = BitmapFactory.decodeResource(getResources, R.drawable.love1);

mLove2 = BitmapFactory.decodeResource(getResources, R.drawable.love2);

mLove3 = BitmapFactory.decodeResource(getResources, R.drawable.love3);

mLove4 = BitmapFactory.decodeResource(getResources, R.drawable.love4);

mLove5 = BitmapFactory.decodeResource(getResources, R.drawable.love5);

mLove6 = BitmapFactory.decodeResource(getResources, R.drawable.love6);

mLove1 = reSizeLove(mLove1);

mLove2 = reSizeLove(mLove2);

mLove3 = reSizeLove(mLove3);

mLove4 = reSizeLove(mLove4);

mLove5 = reSizeLove(mLove5);

mLove6 = reSizeLove(mLove6);

mDefLove = mLove1;

mLoveWidth = mLove1.getWidth;

setDefPosition;

}

privateBitmap reSizeLove(Bitmap src){

returnBitmap.createScaledBitmap(src, 160, 160, false);

}

privatevoidinitPaint{

mPaint = newPaint;

mPaint.setColor(getResources.getColor(android.R.color.holo_purple));

mPaint.setStrokeWidth(8f);

mPaint.setStyle(Paint.Style.FILL);

mPaint.setDither(true);

mPaint.setAntiAlias(true);

mPaint.setTextSize(45f);

}

publicLoveView(Context context){

this(context, null);

}

publicLoveView(Context context, @Nullable AttributeSet attrs){

this(context, attrs, 0);

}

publicLoveView(Context context, @Nullable AttributeSet attrs, intdefStyleAttr){

super(context, attrs, defStyleAttr);

init;

}

@Override

protectedvoidonDraw(Canvas canvas){

super.onDraw(canvas);

mCanvasWidth = canvas.getWidth;

mCanvasHeight = canvas.getHeight;

mLoveBitmapX = mCanvasWidth / 2- mLoveBitmapWidth / 2;

mLoveBitmapY = mCanvasHeight - 2* mLoveBitmapHeight;

drawLoveBitmap(canvas);

canvas.drawBitmap(mDefLove, mLoveX, mLoveY, mPaint);

//随便画的

canvas.drawText("点赞", mCanvasWidth / 2- mPaint.getTextSize, mLoveBitmapY + mLoveBitmapHeight + 100, mPaint);

canvas.drawLine(0, mLoveBitmapY + mLoveBitmapHeight + 20, mCanvasWidth, mLoveBitmapY + mLoveBitmapHeight + 20, mPaint);

}

privatePoint[] setPoint1 {

Point[] points = newPoint[]{

newPoint(mLoveX, mLoveY),

newPoint(0, mCanvasHeight / 2),

newPoint(mCanvasWidth + 20, -mLoveWidth - 10),

};

returnpoints;

}

privatePoint[] setPoint2 {

Point[] points = newPoint[]{

newPoint(mLoveX, mLoveY),

newPoint(mCanvasWidth, mCanvasHeight / 2),

newPoint(-mLoveWidth - 20, -mLoveWidth - 10),

};

returnpoints;

}

privatevoidsetDefPosition{

mLoveX = mCanvasWidth / 2- mLoveWidth / 2;

mLoveY = mLoveBitmapY - 80;

}

privatevoiddrawDynamicLove{

setDefPosition;

//设置爱心的样式和位置

intcolor = mRandom.nextInt(6) + 1;

mDefLove = getBitmap(color);

k = 0;//开始

//添加贝塞尔路径的点

if(mRandom.nextInt(2) == 0) {

mPoints = setPoint1;

} else{

mPoints = setPoint2;

}

mLoveThread = newThread(newRunnable {

@Override

publicvoidrun{

while(k < 1) {

k += 0.01;

Point point = deCasteljau(mPoints, k);

mLoveX = point.x;

mLoveY = point.y;

if(mLoveY <= -mLoveWidth || mLoveY >= mCanvasHeight) {

k = 1;

}

if(mLoveX <= -mLoveWidth || mLoveX >= mCanvasWidth) {

k = 1;

}

postInvalidate;//异步刷新

try{

Thread.sleep(80);

} catch(InterruptedException e) {

e.printStackTrace;

}

}

}

});

mExecutorService.execute(mLoveThread);

}

privateBitmap getBitmap(intcolor){

switch(color) {

case1:

returnmLove1;

case2:

returnmLove2;

case3:

returnmLove3;

case4:

returnmLove4;

case5:

returnmLove5;

case6:

returnmLove6;

}

returnnull;

}

privatevoiddrawLoveBitmap(Canvas canvas){

canvas.drawBitmap(mLoveBitmap, mLoveBitmapX, mLoveBitmapY, mPaint);

}

/**

* 根据点得到曲线的路径上的点,k 是变化趋势

*/

privatePoint deCasteljau(Point[] points, floatk){

finalintn = points.length;

for(inti = 1; i <= n; i++)

for(intj = 0; j < n - i; j++) {

points[j].x = (int) ((1- k) * points[j].x + k * points[j + 1].x);

points[j].y = (int) ((1- k) * points[j].y + k * points[j + 1].y);

}

returnpoints[0];

}

@Override

publicbooleanonTouchEvent(MotionEvent event){

mTouchX = (int) event.getX;

mTouchY = (int) event.getY;

switch(event.getAction) {

caseMotionEvent.ACTION_DOWN:

if(isTouchLoveArea(mTouchX, mTouchY)) {

drawDynamicLove;

}

break;

caseMotionEvent.ACTION_UP:

break;

}

returnsuper.onTouchEvent(event);

}

privatebooleanisTouchLoveArea(inttouchX, inttouchY){

returntouchX >= mLoveBitmapX && touchX <= mLoveBitmapX + mLoveBitmapWidth

&& touchY > mLoveBitmapY && touchY <= mLoveBitmapY + mLoveBitmapHeight;

}

}

这就是整个效果的代码图了,将它放到 activity_main 里面,运行一下就可以看到效果了。

声明:本文为 CSDN 博客精选文章,版权归作者所有。作者:威威喵

原文:/smile_Running/article/details/98170645

热 文 推 荐

责任编辑:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。