自带美感的贝塞尔曲线原理与实战——Android高级UI,硬核

当得出 D和E 点,就可以进行求 点F,逻辑还是一样。具体如下

F点的x轴坐标:fx = dx + (ex-dx) * u = (1-u) * dx + u * ex (u ∈ [0,1])
F点的y轴坐标:fy = dy + (ey-dy) * u = (1-u) * dy + u * ey (u ∈ [0,1])

至此最终的点 F 的可绘制坐标便得出。

推导公式

从以上的 计算公式 和 之前的 “三个结论”,借助下图我们可以得出一个公式

P0k = (1-u) * P0k-1 + u * P1k-1

Tips: x轴 和 y轴 的坐标计算公式是一样,所以我们这里就使用 x轴 作为代表,方便讲解

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通用公式,想必童鞋们已经想到算法中的一个词叫 “递归”,的确没错,但细想一下还缺少一个 递归的终止条件 。我们再细想一下,其实终止条件就是 降阶最开始依赖的控制点是固定不变的,或是说是我们程序猿给定的,所以不用计算直接返回该控制点的x轴或y轴即可。

最终的递归公式如下

公式说明:

1、k 表示阶数,当 k=n 时,即相当于前面demo所讲的一阶控制点;当 k=0 时,表示最高阶的控制点,即我们程序猿最初给定的那几个控制点;

2、 i 表示点的下标,这个只是为了便于区分,可参照上面的图进行带入理解;

3、u 表示比例值

将通用公式编写成如下代码,调用 buildBezierPoint 方法,即可获得对应的最终的贝塞尔曲线,二阶和三阶也同样适用。

/**

  • 构建贝塞尔曲线,具体点数由 参数frame 决定
  • @param controlPointList 控制点的坐标
  • @param frame 帧数
  • @return
    */
    public static List buildBezierPoint(List controlPointList,
    int frame) {
    List pointList = new ArrayList<>();

// 此处注意,要用1f,否则得出结果为0
float delta = 1f / frame;

// 阶数,阶数=绘制点数-1
int order = controlPointList.size() - 1;

// 循环递增
for (float u = 0; u <= 1; u += delta) {
pointList.add(new PointF(BezierUtils.calculatePointCoordinate(BezierUtils.X_TYPE, u, order, 0, controlPointList),
BezierUtils.calculatePointCoordinate(BezierUtils.Y_TYPE, u, order, 0, controlPointList)));
}

return pointList;

}

/**

  • 计算坐标 [贝塞尔曲线的核心关键]
  • @param type {@link #X_TYPE} 表示x轴的坐标, {@link #Y_TYPE} 表示y轴的坐标
  • @param u 当前的比例
  • @param k 阶数
  • @param p 当前坐标(具体为 x轴 或 y轴)
  • @param controlPointList 控制点的坐标
  • @return
    */
    public static float calculatePointCoordinate(@IntRange(from = X_TYPE, to = Y_TYPE) int type,
    float u,
    int k,
    int p,
    List controlPointList) {

/**

  • 公式解说:(p表示坐标点,后面的数字只是区分)
  • 场景:有一条线p1到p2,p0在中间,求p0的坐标
  •  p1◉--------○----------------◉p2
    
  •        u    p0
    
  • 公式:p0 = p1+u*(p2-p1) 整理得出 p0 = (1-u)p1+up2
    */

// 一阶贝塞尔,直接返回
if (k == 1) {

float p1;
float p2;

// 根据是 x轴 还是 y轴 进行赋值
if (type == X_TYPE) {
p1 = controlPointList.get§.x;
p2 = controlPointList.get(p + 1).x;
} else {
p1 = controlPointList.get§.y;
p2 = controlPointList.get(p + 1).y;
}

return (1 - u) * p1 + u * p2;

} else {

/**

  • 这里应用了递归的思想:
  • 1阶贝塞尔曲线的端点 依赖于 2阶贝塞尔曲线
  • 2阶贝塞尔曲线的端点 依赖于 3阶贝塞尔曲线
  • n-1阶贝塞尔曲线的端点 依赖于 n阶贝塞尔曲线
  • 1阶贝塞尔曲线 则为 真正的贝塞尔曲线存在的点
    */
    return (1 - u) * calculatePointCoordinate(type, u, k - 1, p, controlPointList)
  • u * calculatePointCoordinate(type, u, k - 1, p + 1, controlPointList);

}

}

四、实战

经过漫长的理论,童鞋们早就摩拳搽掌,想用贝塞尔曲线前去挑战设计师,少侠勿急,看完实战我们再去碾压😄。

温馨提示:

理论是进阶中必不可少的部分,否则只知其然而不知其所以然。永远只能是作为使用别人代码的使用者,而不是创造者,更无法体会到创造的快乐。

贝塞尔曲线Demo的 Github 入口:传送门

1、圆变心

文章最开始出现的就是以下这张效果图,现在是时候进行撸起袖子开始打代码了。

效果图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

动画分析:

动态图中,我们可以清楚的看出,从一个圆形慢慢变成心形,然后带有一点弹性效果。这样一分析,我们便需要三样东西:圆、心、弹性效果公式,接下来就是逐个突破。

准备零件

(1)圆 此圆非彼圆,我们不能借助canvas直接使用drawCircle进行绘制,因为这样的圆我们无法控制。那要如何处理呢?当然是用贝塞尔曲线画圆,因为这样一来这个 “圆”的控制点 就全都在我们的可控范围内,因为我持有了这些控制点就能进行坐标的变动,进而改变曲线的形状。

正当你在坐等这个 贝塞尔曲线画圆的公式 时,我又要泼一盆冷水了,因为根本就不存在这样一个公式。但我们可以通过前面的理论找到一个 近似圆的贝塞尔曲线公式

至于 贝塞尔曲线 为什么无法画出一个圆,有兴趣的童鞋们自行百度和google吧,毕竟这个一两行字无法解释清楚。

我们可以通过 三阶贝塞尔曲线 画出一段圆弧,通过四段圆弧就能拼凑出一个完整的圆了。但是又来了一个问题,三阶贝塞尔曲线有四个控制点,两端的控制点容易取,中间的控制点如何取? 带着这个疑问,我们来看下面这个动画,当 控制点比例 从0到1增加过程中,蓝色区域从方形慢慢的变得接近圆,然后溢出变成圆角方形红色的圆圈是用canvas的drawCircle绘制,从一些贝塞尔曲线绘制圆的论证资料和这里的动画效果可以得出,当 控制点比例等于0.55时(保留两位小数),最接近一个圆。 前面提到的 四段圆弧的贝塞尔曲线 ,在这里使用了四种颜色,需要自己体验效果的童鞋,请进传送门

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可能还有些童鞋对动态图中的 控制点比例 不太理解,我们借助下图来解释,图中只留了一段圆弧,其他的三段是一样的道理。具体比例公式如下

控制点比例=ABED=CDAE控制点比例 =\frac{AB}{ED} = \frac{CD}{AE}控制点比例=EDAB​=AECD​

E为圆心,AE和ED为半径,即AE=ED;所以AB=CD;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

至此圆的问题解决了。我们继续过关斩将。

(2)心 心要如何绘制呢?小盆友这里给出一个小工具,我们可以通过自行拽动来获取需要的图形,然后打印坐标(单位dp),拿到坐标了就可以为所欲为了。工具效果图如下,我们以拽动一个圆为例:

拽动的动态效果图 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 打印出来的坐标点(单位为dp): 坐标肯定会有些许偏差,毕竟是手指拽动出来的,而且左右的心是不对称的,所以需要进行微调一半心,然后另一半心进行对称取坐标。如此一来,心形也搞定了。 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个小工具可用于从圆变成另一种形状,而不局限于心形,或许说局限于我们的想象力。下图是小盆友随便拽出来的一个图,个人觉得有点像兔子🐰,哈哈,挺抽象的吧。感兴趣的可以进传送门

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(3)弹性效果公式 终于到最后一个零件啦,弹性效果公式可以从一个网站尝试得出

inloop.github.io/interpolato…

最终得到一个让自己觉得还不错的公式

float x = (float) animation.getAnimatedValue();
float factor = 0.15f;
double value = Math.pow(2, -10 * x) * Math.sin((x - factor / 4) * (2 * Math.PI) / factor) + 1;

组装零件

零件都已经备好了,组装起来就是我们看到的效果,因为代码比较简单,就不再贴出来了,需要的请进传送门

2、乘风破浪的小船

面试复习路线,梳理知识,提升储备

自己的知识准备得怎么样,这直接决定了你能否顺利通过一面和二面,所以在面试前来一个知识梳理,看需不需要提升自己的知识储备是很有必要的。

关于知识梳理,这里再分享一下我面试这段时间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)

资料获取方式:前往我的GitHub

  • 架构师筑基必备技能
  • Android高级UI与FrameWork源码
  • 360°全方面性能调优
  • 解读开源框架设计思想
  • NDK模块开发
  • 微信小程序
  • Hybrid 开发与Flutter

知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:

Android开发七大模块核心知识笔记

《960全网最全Android开发笔记》

《379页Android开发面试宝典》

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

如何使用它?

1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

R-1711026824413)]
[外链图片转存中…(img-N2vvqThR-1711026824413)]
[外链图片转存中…(img-xT3RWoC4-1711026824414)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-HJlSlcWO-1711026824414)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值