“玫瑰贝塞尔曲线”效果如下:

一、效果要求

1)在布局中某个位置处玫瑰开始由小而大,淡入出现;

2)出现的玫瑰,颜色随机而定,玫瑰可在布局内做动画亦可在整个界面中做动画,如上图;

3)玫瑰自下而上做曲线变化,水平方向位移亦随机而定;

4)每个玫瑰上浮曲线不同之外,上浮速度也不尽相同;

二、实现难点及实现方法

1)展示玫瑰可采用直接画(drawBitmap())的方式实现,也可以选择动态创建ImageView的方式实现,考虑到玫瑰上浮的最后消失不见,本例中选取第二种方法,利用addViewremoveView方法动态进行ImageView的添加和删除;

2)一个玫瑰即是一个ImageView,所以选择自定义一个ViewGroup,用于管理所有ImageView;

3)调用setX()和setY()方法选定ImageView出现在布局中的某个位置,本例中布局选取中心点;

4)玫瑰做曲线运动,可选择二阶或者三阶贝塞尔公式实现,本例中使用三阶公式,使ImageView做两次变化;

5)通过指定不同的父布局(ViewGroup自身还是Activity根布局),实现动画是全局添加和布局内添加;

三、上代码,具体实现

按照上述需求,一步步实现:

1)自定义ViewGroup —— 继承自RelativeLayout,重写onMeasure()方法获取自身宽高;

public class BezierImageView extends RelativeLayout {private static final String TAG = "BezierImageView";private ViewGroup contentContainer;private Interpolator[] interpolators;private Drawable drawable[];// 图片的宽高private int dWidth = 0;private int dHeight = 0;private RelativeLayout.LayoutParams lp;private Random mRandom;// 自定义View自身宽高private int mWidth = 0;private int mHeight = 0;// 屏幕高度private int screenHeight;// 自定义View的位置(高度)private int positionY;public BezierImageView(Context context) {this(context, null);init();}public BezierImageView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);init();}public BezierImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}/*** 初始化数据*/private void init() {contentContainer = (ViewGroup) ((Activity) getContext()).findViewById(android.R.id.content);WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);screenHeight = windowManager.getDefaultDisplay().getHeight();drawable = new Drawable[6];drawable[0] = ContextCompat.getDrawable(getContext(), R.drawable.pink);drawable[1] = ContextCompat.getDrawable(getContext(), R.drawable.hotpink);drawable[2] = ContextCompat.getDrawable(getContext(), R.drawable.crimson);drawable[3] = ContextCompat.getDrawable(getContext(), R.drawable.fuchsia);drawable[4] = ContextCompat.getDrawable(getContext(), R.drawable.darkvoilet);drawable[5] = ContextCompat.getDrawable(getContext(), R.drawable.violet);interpolators = new Interpolator[4];interpolators[0] = new AccelerateInterpolator();interpolators[1] = new DecelerateInterpolator();interpolators[2] = new AccelerateDecelerateInterpolator();interpolators[3] = new LinearInterpolator();// Drawable这个类是对所有可以画的东西的抽象,可以是一张图片,也可以是实体的颜色,线等等dWidth = drawable[0].getIntrinsicWidth();dHeight = drawable[0].getIntrinsicHeight();//该方法用以获取Drawable的固有宽高 单位为dplp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);lp.addRule(ALIGN_PARENT_BOTTOM);lp.addRule(CENTER_HORIZONTAL);mRandom = new Random();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//在此处才能准确获取到控件的宽高mWidth = getMeasuredWidth();mHeight = getMeasuredHeight();}

①定义Drawable[ ]数组,里面放入6张图片,用于作为图片源动态添加进ImageView内;

②定义Interpolator[ ]数组,里面放入四种插值器,用于对不同的动画类型指定不同的变化速率;

③实例化一个Random实例,ImageView的创建、插值器的选择和贝塞尔曲线路径都是随机而定的;

④findViewById(android.R.id.content),获取Activity根布局,ImageView添加进该布局中,做全局贝塞尔曲线动画;若不想做全局的动画,只想把动画限制在布局内,则这一步省略;

⑤获取RelativeLayout.LayoutParams实例对象,用于指定ImageView的添加方式。

2)玫瑰初始出现——缩放+淡入动画效果;

    /*** 获取自定义ViewGroup——本身在布局中的位置*/public int getPositionY(int viewPositionY) {this.positionY = viewPositionY;return positionY;}/*** 玫瑰初始出现——缩放淡入动画效果*/public AnimatorSet getInitAnimationSet(final ImageView image) {image.setX((mWidth) / 2);image.setY(positionY);ObjectAnimator scaleX = ObjectAnimator.ofFloat(image, "scaleX", 0.3f, 1.6f);ObjectAnimator scaleY = ObjectAnimator.ofFloat(image, "scaleY", 0.3f, 1.6f);ObjectAnimator alpha = ObjectAnimator.ofFloat(image, "alpha", 0.3f, 1f);AnimatorSet animate = new AnimatorSet();animate.playTogether(scaleX, scaleY, alpha);animate.setDuration(500);return animate;}

①定义动画组合,里面放入缩放动画和淡入动画集合;

②调用setX()setY()方法指定ImageView添加进布局的位置,这里需要注意结合Activity层面代码获取布局纵向的位置;

3)玫瑰上浮动画——按照三阶贝塞尔公式计算曲线上点的坐标,然后一一设置给ImageView实例;

    /*** 上浮动画效果*/private AnimatorSet getRunAnimatorSet(final ImageView image) {AnimatorSet runSet = new AnimatorSet();PointF point0 = new PointF((mWidth) / 2, positionY); //起始点 定在自定义View的中心点Log.d(TAG, "positionY: " + positionY);PointF point3 = new PointF(mRandom.nextInt(getWidth()), 0); //终止点/*** 开始执行三阶贝塞尔动画*/TypeEvaluator evaluator = new BezierEvaluator(getPointF(2), getPointF(1));ValueAnimator bezier = ValueAnimator.ofObject(evaluator, point0, point3);bezier.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {//这里获取到贝塞尔曲线计算出来的的x y值 赋值给viewPointF pointF = (PointF) animation.getAnimatedValue();image.setX(pointF.x);image.setY(pointF.y);image.setAlpha(1 - animation.getAnimatedFraction());}});ObjectAnimator scaleX = ObjectAnimator.ofFloat(image, "scaleX", 1.6f, 0.7f);ObjectAnimator scaleY = ObjectAnimator.ofFloat(image, "scaleY", 1.6f, 0.7f);runSet.playTogether(scaleX, scaleY, bezier);runSet.setDuration(2000);return runSet;}/*** 获取控制点 控制点任意生成 位于界面之上*/private PointF getPointF(int scale) {PointF pointF = new PointF();pointF.x = mRandom.nextInt((mWidth - 100));//减去100 是为了控制 x轴活动范围,看效果//再Y轴上 为了确保第二个点 在第一个点之上,我把Y分成了上下两半 这样动画效果好一些  也可以用其他方法pointF.y = mRandom.nextInt((mHeight - 100)) / scale;return pointF;}/*** 自定义贝塞尔估值器* 用以将运动过程中的动画值 转换为对应的坐标* 三阶贝塞尔曲线公式*/public class BezierEvaluator implements TypeEvaluator<PointF> {/*** 构造方法参数接收两个控制点实例*/private PointF point1;private PointF point2;public BezierEvaluator(PointF point1, PointF point2) {this.point1 = point1;this.point2 = point2;}/*** 利用三阶贝塞尔曲线公式进行移动轨迹点的运算* 利用该轨迹点然后分别提取到横纵坐标** @param t      表示时间* @param point0 初始点* @param point3 终点*/@Overridepublic PointF evaluate(float t, PointF point0, PointF point3) {PointF point = new PointF();point.x = point0.x * (1 - t) * (1 - t) * (1 - t)+ 3 * point1.x * t * (1 - t) * (1 - t)+ 3 * point2.x * t * t * (1 - t) * (1 - t)+ point3.x * t * t * t;point.y = point0.y * (1 - t) * (1 - t) * (1 - t)+ 3 * point1.y * t * (1 - t) * (1 - t)+ 3 * point2.y * t * t * (1 - t) * (1 - t)+ point3.y * t * t * t;return point;}}

注意,这段代码是实现贝塞尔曲线动画的核心所在:

①利用三阶贝塞尔曲线公式计算三阶贝塞尔曲线上的每一个点的坐标;自定义贝塞尔曲线估值器用于将当前动画运动具体指转换为具体坐标信息;

②创建内部类BezierEvaluator,实现TypeEvaluator接口,接口泛型传入PointF点类,构造方法中接收两个点的坐标,两个点即是三阶贝塞尔曲线的控制点;实现接口中的方法,用以计算将动画运动的当前值,这里依据三阶贝塞尔曲线公式计算;

③定义getPointF()方法用以计算两个控制点的坐标,因为是全局添加,所以坐标任意生成;

④使用ValueAnimator属性动画,ValueAnimator.AnimatorUpdateListener()接口中获取当前动画运动值;这里调用了ofObject()方法创建具体动画实例,参数中传入定义好贝塞尔曲线估值器动画的起始点动画终止点坐标(起始点为自定义ViewGroup中心点,终止点屏幕顶部的任意位置);

⑤animation.getAnimatedValue()获取到的即为通过贝塞尔估值器转换过的值(动画进度——>具体值),本例中即为pointF实例,然后获取该实例的横纵坐标,分别设置给ImageView,只要动画不停地执行,就能获取到不同的坐标值,最终连贯为贝塞尔动画。

4)开始动画——定时循环执行;

    /*** 定时器,可以自动执行动画*/public void startAutoAnimation() {isPlayingAnim = !isPlayingAnim;if (isPlayingAnim) {if (timer != null) {timer.cancel();}if (task != null) {task.cancel();}} else {timer = new Timer();task = new TimerTask() {@Overridepublic void run() {// 发送消息Message message = handler.obtainMessage();message.what = 1;handler.sendMessage(message);}};// 执行task,经过指定时间循环执行timer.schedule(task, 0, 80);}}Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (msg.what == 1) {ImageView image = new ImageView(getContext());image.setImageDrawable(drawable[mRandom.nextInt(6)]);image.setLayoutParams(lp);contentContainer.addView(image);start(image);}}};/*** 合并执行两个动画* 动画执行完毕时调用调用removeView方法将动态添加的ImageView一一移除掉*/public void start(final ImageView image) {AnimatorSet finalSet = new AnimatorSet();finalSet.setInterpolator(interpolators[mRandom.nextInt(4)]);//实现随机变速//playSequentially接收参数为可变长参数 任意多个Animator对象的动画会逐个播放finalSet.playSequentially(getInitAnimationSet(image),getRunAnimatorSet(image));finalSet.setTarget(image); //设置动画组合目标控件finalSet.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {contentContainer.removeView(image);}});finalSet.start();}

①定义一个定时器,每隔指定时间即开始执行定时任务;

②定时任务为发送Handler异步消息,消息的处理为:动态创建ImageView实例,然后设置好LayoutParams,最终添加进Activity根布局中,若只是想添加进自定义ViewGroup区域,则直接调用addView方法即可;

③调用start()方法,开始执行组合动画,为该组合设立监听,动画执行完毕后,调用removeView方法将ImageView实例删除;

5)自定义ViewGroup资源销毁;

    /*** onAttachedToWindow方法是在Activity resume的时候被调用的* onDetachedFromWindow方法是在Activity调用onDestory方法之后* 自定义View销毁之后调用,释放资源*/@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();if (timer != null) {timer.cancel();}if (task != null) {task.cancel();}}

ImageView实例会在动画执行完的监听中销毁,这里重写onDetachedFromWindow(),调用时机在Activity销毁时,主要是将定时器和定时任务取消,防止内存泄漏。

使用:XML布局中静态添加:

    <com.example.administrator.indicatorscroller.view.BezierImageViewandroid:id="@+id/bse_img"android:layout_width="match_parent"android:layout_height="100dp"android:layout_marginTop="160dp"android:background="@color/white">

Activity代码中的处理:

     final BezierImageView bseImg = (BezierImageView) findViewById(R.id.bse_img);bseImg.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {int oH = bseImg.getHeight();int oW = bseImg.getWidth();int positionY = (int) bseImg.getY();int bootom = bseImg.getBottom();int top = bseImg.getTop();Log.d(TAG, "onGlobalLayout: " + positionY + "," + bootom + "," + top); // 480 780 480 getY() = getTop()bseImg.getPositionY(positionY + oH / 2);bseImg.getViewTreeObserver().removeGlobalOnLayoutListener(this);}});bseImg.startAutoAnimation(); //自动播放动画效果

①获取自定义ViewGroup实例,调用start()方法,执行动画;

②利用接口ViewTreeObserver.OnGlobalLayoutListener(),监听ViewTree的变化,在接口方法中获取自定义View的位置getX()、getY(),然后调用定义好的get方法将值传入BezierImageView中,完成动画位置的设定。

注:在onCreate方法中,我们无法直接利用getHeight、getWidth、getY、getX、getTop、getBottom、getLeft、getRight方法获取到具体值,需要调用getViewTreeObserver().addOnGlobalLayoutListener添加监听去获取到具体值。

完整代码如下:

package com.example.administrator.indicatorscroller.view;import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;import com.example.administrator.indicatorscroller.R;import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;/*** 作者    cpf* 时间    2019/1/8* 文件    BezierImageView* 描述    "玫瑰贝塞尔曲线"* 爱心应该是Bitmap 便于扩展,而不是画出来的形状;drawBitmap;不断改变Bitmap的坐标可实现飞起来的效果;* 属性动画也可以实现飞起来的效果,一个爱心就是一个个子view; 两种实现方式都需要-坐标* 曲线圆滑的飞起,就只能依靠贝塞尔曲线 贝塞尔曲线如何传出坐标?** 数组* 动画组合* ofObject* 三阶贝塞尔曲线* 贝塞尔曲线估值器* 实现循环/定时操作* RelativeLayout.params* 动态设置位置(坐标) setX() setY()** 可添加的自定义属性:循环定时 动画执行的时长 玫瑰的大小 是否进行全局添加** 思想:先将图片添加进来/画出来 然后再为图片做动画*/
public class BezierImageView extends RelativeLayout {private static final String TAG = "BezierImageView";private ViewGroup contentContainer;private Interpolator[] interpolators;private Drawable drawable[];// 图片的宽高private int dWidth = 0;private int dHeight = 0;private RelativeLayout.LayoutParams lp;private Random mRandom;// 自定义View自身宽高private int mWidth = 0;private int mHeight = 0;// 屏幕高度private int screenHeight;// 自定义View的位置(高度)private int positionY;private Timer timer = null;private TimerTask task = null;private boolean isPlayingAnim = true;public BezierImageView(Context context) {this(context, null);init();}public BezierImageView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);init();}public BezierImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}/*** 初始化数据*/private void init() {contentContainer = (ViewGroup) ((Activity) getContext()).findViewById(android.R.id.content);WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);screenHeight = windowManager.getDefaultDisplay().getHeight();drawable = new Drawable[6];drawable[0] = ContextCompat.getDrawable(getContext(), R.drawable.pink);drawable[1] = ContextCompat.getDrawable(getContext(), R.drawable.hotpink);drawable[2] = ContextCompat.getDrawable(getContext(), R.drawable.crimson);drawable[3] = ContextCompat.getDrawable(getContext(), R.drawable.fuchsia);drawable[4] = ContextCompat.getDrawable(getContext(), R.drawable.darkvoilet);drawable[5] = ContextCompat.getDrawable(getContext(), R.drawable.violet);interpolators = new Interpolator[4];interpolators[0] = new AccelerateInterpolator();interpolators[1] = new DecelerateInterpolator();interpolators[2] = new AccelerateDecelerateInterpolator();interpolators[3] = new LinearInterpolator();// Drawable这个类是对所有可以画的东西的抽象,可以是一张图片,也可以是实体的颜色,线等等dWidth = drawable[0].getIntrinsicWidth();dHeight = drawable[0].getIntrinsicHeight();//该方法用以获取Drawable的固有宽高 单位为dplp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);lp.addRule(ALIGN_PARENT_BOTTOM);lp.addRule(CENTER_HORIZONTAL);mRandom = new Random();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//在此处才能准确获取到控件的宽高mWidth = getMeasuredWidth();mHeight = getMeasuredHeight();}/*** 定时器,可以自动执行动画*/public void startAutoAnimation() {isPlayingAnim = !isPlayingAnim;if (isPlayingAnim) {if (timer != null) {timer.cancel();}if (task != null) {task.cancel();}} else {timer = new Timer();task = new TimerTask() {@Overridepublic void run() {// 发送消息Message message = handler.obtainMessage();message.what = 1;handler.sendMessage(message);}};// 执行task,经过指定时间循环执行timer.schedule(task, 0, 80);}}Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (msg.what == 1) {ImageView image = new ImageView(getContext());image.setImageDrawable(drawable[mRandom.nextInt(6)]);image.setLayoutParams(lp);contentContainer.addView(image);start(image);}}};/*** 获取自定义view本身在布局中的位置*/public int getPositionY(int viewPositionY) {this.positionY = viewPositionY;return positionY;}/*** onAttachedToWindow方法是在Activity resume的时候被调用的* onDetachedFromWindow方法是在Activity调用onDestory方法之后* 自定义View销毁之后调用,释放资源*/@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();if (timer != null) {timer.cancel();}if (task != null) {task.cancel();}}/*** 缩放动画效果*/public AnimatorSet getInitAnimationSet(final ImageView image) {image.setX((mWidth) / 2);image.setY(positionY);ObjectAnimator scaleX = ObjectAnimator.ofFloat(image, "scaleX", 0.3f, 1.6f);ObjectAnimator scaleY = ObjectAnimator.ofFloat(image, "scaleY", 0.3f, 1.6f);ObjectAnimator alpha = ObjectAnimator.ofFloat(image, "alpha", 0.3f, 1f);AnimatorSet animate = new AnimatorSet();animate.playTogether(scaleX, scaleY, alpha);animate.setDuration(500);return animate;}/*** 上浮动画效果*/private AnimatorSet getRunAnimatorSet(final ImageView image) {AnimatorSet runSet = new AnimatorSet();PointF point0 = new PointF((mWidth) / 2, positionY); //起始点 定在自定义View的中心点Log.d(TAG, "positionY: " + positionY);PointF point3 = new PointF(mRandom.nextInt(getWidth()), 0); //终止点/*** 开始执行三阶贝塞尔动画*/TypeEvaluator evaluator = new BezierEvaluator(getPointF(2), getPointF(1));ValueAnimator bezier = ValueAnimator.ofObject(evaluator, point0, point3);bezier.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {//这里获取到贝塞尔曲线计算出来的的x y值 赋值给viewPointF pointF = (PointF) animation.getAnimatedValue();image.setX(pointF.x);image.setY(pointF.y);image.setAlpha(1 - animation.getAnimatedFraction());}});ObjectAnimator scaleX = ObjectAnimator.ofFloat(image, "scaleX", 1.6f, 0.7f);ObjectAnimator scaleY = ObjectAnimator.ofFloat(image, "scaleY", 1.6f, 0.7f);runSet.playTogether(scaleX, scaleY, bezier);runSet.setDuration(2000);return runSet;}/*** 合并执行两个动画* 动画执行完毕时调用调用removeView方法将动态添加的ImageView一一移除掉*/public void start(final ImageView image) {AnimatorSet finalSet = new AnimatorSet();finalSet.setInterpolator(interpolators[mRandom.nextInt(4)]);//实现随机变速//playSequentially接收参数为可变长参数 任意多个Animator对象的动画会逐个播放finalSet.playSequentially(getInitAnimationSet(image),getRunAnimatorSet(image));finalSet.setTarget(image); //设置动画组合目标控件finalSet.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {contentContainer.removeView(image);}});finalSet.start();}/*** 获取控制点 控制点任意生成 位于界面之上*/private PointF getPointF(int scale) {PointF pointF = new PointF();pointF.x = mRandom.nextInt((mWidth - 100));//减去100 是为了控制 x轴活动范围,看效果//再Y轴上 为了确保第二个点 在第一个点之上,我把Y分成了上下两半 这样动画效果好一些  也可以用其他方法pointF.y = mRandom.nextInt((mHeight - 100)) / scale;return pointF;}/*** 自定义贝塞尔估值器* 用以将运动过程中的动画值 转换为对应的坐标* 三阶贝塞尔曲线公式*/public class BezierEvaluator implements TypeEvaluator<PointF> {/*** 构造方法参数接收两个控制点实例*/private PointF point1;private PointF point2;public BezierEvaluator(PointF point1, PointF point2) {this.point1 = point1;this.point2 = point2;}/*** 利用三阶贝塞尔曲线公式进行移动轨迹点的运算* 利用该轨迹点然后分别提取到横纵坐标** @param t      表示时间* @param point0 初始点* @param point3 终点*/@Overridepublic PointF evaluate(float t, PointF point0, PointF point3) {PointF point = new PointF();point.x = point0.x * (1 - t) * (1 - t) * (1 - t)+ 3 * point1.x * t * (1 - t) * (1 - t)+ 3 * point2.x * t * t * (1 - t) * (1 - t)+ point3.x * t * t * t;point.y = point0.y * (1 - t) * (1 - t) * (1 - t)+ 3 * point1.y * t * (1 - t) * (1 - t)+ 3 * point2.y * t * t * (1 - t) * (1 - t)+ point3.y * t * t * t;return point;}}
}

源代码出处

http://www.apkbus.com/blog-947568-79091.html

感谢分享!

本例可用于一些直播刷礼物的特效,为了夸张醒目些,可通过自定义view属性去指定不同的数值,比如循环定时、动画执行的时长、玫瑰的大小、是否进行全局添加等等。

砰砰砰,礼物刷起!喷发的玫瑰刺激可以用户不停消费,就像冒血一样!

有趣的自定义View — 玫瑰·三阶贝塞尔曲线相关推荐

  1. android运动轨迹怎么画,Android 利用三阶贝塞尔曲线绘制运动轨迹的示例

    本篇文章主要介绍了Android 利用三阶贝塞尔曲线绘制运动轨迹的示例,分享给大家,具体如下: 实现点赞效果,自定义起始点以及运动轨迹 效果图: xml布局: xmlns:tools="ht ...

  2. 【Android UI】贝塞尔曲线 ⑦ ( 使用 德卡斯特里奥算法 公式计算的 方法绘制三阶贝塞尔曲线示例 )

    文章目录 一.使用 德卡斯特里奥算法 公式计算的 方法绘制三阶贝塞尔曲线 二.代码示例 贝塞尔曲线参考 : https://github.com/venshine/BezierMaker 一.使用 德 ...

  3. 微信小程序画布实现冒泡点赞,三阶贝塞尔曲线

    一.核心代码 1)wxml 核心代码 <canvas wx:for="{{bezierCount-0}}" wx:key="key" class=&quo ...

  4. flutter绘图基础之三阶贝塞尔曲线cubicTo

    题记 -- 执剑天涯,从你的点滴积累开始,所及之处,必精益求精. 重要消息 [经验分享视频教程 感兴趣的伙伴可以瞅瞅] 1 flutter 中绘制基础引言 Flutter 中实现绘制的主要是Custo ...

  5. [zz]用三阶贝塞尔曲线(贝兹曲线)拟合劣圆弧的公式(附伪代码)

    转自:用三阶贝塞尔曲线(贝兹曲线)拟合劣圆弧的公式(附伪代码) 三阶贝塞尔曲线有四个控制点A.B.C.D, 若要用三阶贝塞尔曲线拟合劣圆弧,自然的要求是: 1)A位于圆弧的起点,D位于圆弧的终点: 2 ...

  6. html5创建三次贝塞尔曲线,HTML5 Canvas中使用路径描画二阶、三阶贝塞尔曲线

    在HTML5 Canvas中,可以用以下方法描画三阶和二阶的贝塞尔曲线: 复制代码代码如下: context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) c ...

  7. 【Android UI】贝塞尔曲线 ② ( 二阶贝塞尔曲线公式 | 三阶贝塞尔曲线及公式 | 高阶贝塞尔曲线 )

    文章目录 一.二阶贝塞尔曲线公式 二.三阶贝塞尔曲线 三.高阶贝塞尔曲线 贝塞尔曲线参考 : https://github.com/venshine/BezierMaker 一.二阶贝塞尔曲线公式 二 ...

  8. 贝塞尔曲线是什么?如何用 Canvas 绘制三阶贝塞尔曲线?

    大家好,我是前端西瓜哥. 今天谈谈贝塞尔曲线是什么以及它的原理,并说说如何用 Canvas 技术绘制一条三阶贝塞尔曲线. 贝塞尔曲线是什么? 贝塞尔曲线,是通过几个简单的参数描述一条曲线的一种参数曲线 ...

  9. 三阶贝塞尔曲线一分为二的一般公式

    三阶贝塞尔曲线被广泛用于各种需要平滑曲线的设计领域,一般通过多段三阶贝塞尔曲线顺次连接,构成比较复杂的曲线. 比如下图中,A.B.C和D控制红色曲线,D.E.F和G控制绿色曲线,G.H.I和A控制蓝色 ...

最新文章

  1. Java问题排查工具清单!
  2. jquery生成一个li_如何使用jQuery从字符串数组生成UL Li列表?
  3. java的decimalFormat_Java中 DecimalFormat 用法详解
  4. vc c mysql_VC++ 利用 MySQL connector c 访问MySQL 数据库
  5. 计算机指针知识,指针_计算机基础知识142页.ppt
  6. wireshark抓包:分析阿里小蜜网络通信方式
  7. bzoj 4238: 电压 dfs树
  8. 华为HCNA实验学习
  9. spring入门学习粗解(一)
  10. npm包发布正式和测试版
  11. 读SQL进阶教程笔记14_SQL编程要点
  12. Win11账号被锁定无法登录怎么办?Win11账号被锁定无法登录
  13. Android Oss上传文件简单使用
  14. Python爬虫之爬取起点中文网
  15. 博弈论-斐波那契博弈
  16. js模板字符串嵌套html,在元素内插入一个有角度的js模板字符串
  17. html语言br怎么用,HTML br 标签如何使用
  18. WebSocket 学习之认识websocket
  19. Unity动画系统详解4:如何用代码控制动画?
  20. CCF 2019-12 第三题 化学方程式配平(100分)

热门文章

  1. 发布/订阅的一些理解
  2. 基于多种群机制的PSO算法Python实现(优化与探索二)
  3. 部门销售总结汇报PPT模板
  4. Yahoo!奇摩分享书签!
  5. 用CNN判断机械轴承的故障数据
  6. 帮我写一个画山水画的代码
  7. uniapp定义全局的filters
  8. 一款js生成的不错的页面上雪花飞舞效果
  9. 计算机网络 RIP,OSPF,BGP,MPLS协议
  10. 提示文件过大无法复制到U盘怎么解决