不管你是想用 canvas 自己随心画,还是因为看不懂别人 canvas 操作的小伙伴,今天你都来对地方了,这里有最基础的教学,带你重温恐怖的初中生活,另外后面有时间我会总结些三角函数在 canvas 中的实际应用范例
自定义 view 中最挠头的就是明明我看了 canvas 所有 API,但我还是依然看不懂别人是怎么绘制的图形,别人 Demo 中传的参数、+- 总是和我想的不一样,如果你有这种疑惑,那么对了,因为你差的就是三角函数基础了,这些初中的知识再一次折磨了我们 (~o ̄3 ̄)~ 搞定三角函数,自定义 view 不再是梦了
学习资料
坐标系
1. 直角坐标系
这个我们应该最清楚不过,区别是 Android 中坐标原点在:左上角,但是这依然有意义。我们把不想要的画在 -值坐标上那么就不会显示,做动画时这很有必要。或者我们在 canvas 中 moveto 到一个新的点,那么就和下图很像了
2. 象限
直角坐标系的两个坐标轴将平面分成了四个部分,称为:象限,象限的编号是按照 逆时针方向 排列的
3. 空间直角坐标系
三条数轴互相垂直的笛卡尔坐标系被称为空间笛卡尔直角坐标系,否则被称为空间笛卡尔斜角坐标系
在x轴和y轴的基础上,增加了指向空间属性的z轴,z轴要符合右手规则:即以右手握住z轴,当右手的四指从正向x轴以π/2角度转向正向y轴时,大拇指的指向就是z轴的正向,这样的三条坐标轴就组成了一个空间直角坐标系点O叫做坐标原点,这样就构成了一个笛卡尔坐标
正角、负角
正角-- 逆时针旋转负角-- 顺时针旋转
canvas 里角度的 +- 我懵逼了很久 ヽ(*。>Д<)o゜
角度、弧度
角度、弧度都是为了描述圆的
- 角度 -->
- 单词:
degree - 符号:°
- 这个大家都知道,把圆分成 360 分,每一分对应的圆心角就是 1°
- 单词:
- 弧度 -->
- 单词:
radian - 符号:rad
- 当弧长=半径时,把对应的圆心角称为 1rad
- 单词:
所以大家看啊,弧度其实还是角度
- 关于角度和弧度,有个公式:
1rad * π = 180°
- 角度和弧度相互转换
1(弧度) = 180 / π (角度)
1(角度) = π / 180 (弧度)
弧度60,求角度 --> 60 * 180 / π
角度45,求弧度 --> 45 * π / 180
为什么要讲弧度和角度呢,有个非常现实的需要,Math 三角函数中,sin、cos、tan 函数参数使用的都是弧度,所以弧度转角度,角度转弧度,算弧长就必须我们了解清楚这里
我们来看看 Math 中角度、弧度换转的函数:
public static double toDegrees(double angrad) {
return angrad * 180.0 / PI;
}
public static double toRadians(double angdeg) {
return angdeg / 180.0 * PI;
}
三角函数
1. 直角三角形
有一点必须明确:所有三角形的计算,都源于直角三角形,直角三角形中的定理,就叫做:勾股定理
借用掘金上笔友的一张图 -->
这张图很形象,很实际,canvas 中我们绘制图形,最离不开的就是圆了,我们需要借助圆来实现各种弧线、path
1.1 正函数
先看图:
勾股定理就是下图中的,有没有的我都加上了
所谓的 正函数 就是借助角度来算各种边,大家下面的公式熟悉了,在知道角度的情况下就可以计算各个边了,甚至我们在不知道角度时,借助多个相互关联的三角形也是可以把边都计算出来,三角形中的边就是坐标系中点的 x/y
2个直角边的平方和 = 斜边的平方和sin-- 正玄,对角边 / 斜边cos-- 余玄,临角边 / 斜边tan-- 正切,直角边 / 临角边
1.2 反函数
反函数的定义就是正函数的反面,大家能猜的到吗?其实很简单,我们来看一个 sin 函数:
sinA = 2/5
那么我问大家,现在你知道了 sinA 的数值,那么你能算出 A 这个角度的大小吗,不能吧,所以反函数的意思就在这里
反函数:一旦知道 sin、cos、tan 的大小,通过反函数就能计算出对应角的大小,计算出来的角的大小是弧度值表示的,取值范围:[-π/2,π/2],换算成角度就是 [-90°,90°],因为三角函数都是在直接三角形中计算的,所以参与计算的角的大小都不会超过90°,注意反函数实在正函数前加 a 来表示的:asin、acos、atan
那么上面的例子,反函数应该是:
A = asin(2/5)
2. 普通三角形
勾股定理只能用在直角三角形中,那么一般的三角形怎么计算呢,思路就是延长三角形的2个斜边,形成新的直角三角形,看图
- 目标三角形
- 延长斜边新城新的三角形
- 所以最后:
这算一个思路吧,别到时候遇到了大家不会算
Math 函数
Math 这个类,以前我也是很陌生的,除了 random 随机数之外就没用过,但是一来到 canvas 自定义绘制环节,Math 的主战场就到了。很久以前我看大牛们自定义 view 中各种操作就是看不懂,不光 Math 看不懂,就是百度之后,知道这个方法干啥的之后我也是看不懂。根结就在于我们把三角函数全忘光了,再学一边三角函数,再把 Math 搞定,那我们就不会囧了
1. E、PI
Math 类里有2个常量:E、PI,PI=3.1415926,大家知道就行
2. 最大值、最小值、绝对值
System.out.println("10 和 20 的较大值:" + Math.max(10, 20));
System.out.println("15.6 和 15 的较小值:" + Math.min(15.6, 15));
System.out.println("-12 的绝对值:" + Math.abs(-12));
10和20的较大值:20
15.6和15的较小值:15.0
-12的绝对值:12
3. 求整数
| static double ceil(double a) | 返回大于或等于 a 的最小整数,理解为向上取整 |
|---|---|
| static double floor(double a) | 返回小于或等于 a 的最大整数,理解为向下取整 |
| static double rint(double a) | 返回最接近 a 的整数值,如果有两个同样接近的整数,则结果取偶数 |
| static int round(float a) | 四舍五入 |
| static long round(double a) | 四舍五入 |
rint-- 四舍五入,但是在0.5时,取偶数,比如 rint(24.5)=24round-- 真*四舍五入,round(24.5)=25
Scanner input = new Scanner(System.in);
System.outprintln("请输入一个数字:");
double num = input.nextDouble();
System.out.println("大于或等于 "+ num +" 的最小整数:" + Math.ceil(num));
System.out.println("小于或等于 "+ num +" 的最大整数:" + Math.floor(num));
请输入一个数字:
99.01
大于或等于 99.01 的最小整数:100.0
小于或等于 99.01 的最大整数:99.0
4. 三角函数
sin、cos,tan 大家要熟悉,+a 的是求反值的,toDegree() 弧度转角度,toRndian() 角度转弧度
System.out.println{"90 度的正弦值:" + Math.sin(Math.PI/2));
System.out.println("0 度的余弦值:" + Math.cos(0));
System.out.println("1 的反正切值:" + Math.atan(l));
System.out.println("120 度的弧度值:" + Math.toRadians(120.0));
90 度的正弦值:1.0
0 的余弦值:1.0
1 的反正切值:0.7853981633974483
120 度的弧度值:2.0943951023931953
5. 指数运算
System.out.println("4 的立方值:" + Math.pow(4, 3));
System.out.println("16 的平方根:" + Math.sqrt(16));
System.out.println("10 为底 2 的对数:" + Math.log1O(2));
4 的立方值:64.0
16 的平方根:4.0
10 为底 2 的对数:0.3010299956639812
6. atan/atan2
这一组函数是 canvas 中的应用难点,好多人搞不清楚,看别人的 Demo 也搞不懂这个函数求的啥,为啥用 atan2,所以大家请注意,下面的代码自己跑一下,画画图体会下,我也是自己瞎琢磨
atan/atan2 是一组反函数,反的是 tan 函数,那么反过来求的是:该切角的角度,但是为啥有2个函数呢
注意 atan/atan2 返回的是 斜线 与 正 x 轴 之间的圆心角角度
atan:- tant(1),使用 tan 值
- 函数取值【-90,90】之间,若你的角是大于 90 的话,这个值就不准了,比如你的角是 135度
atan2:- tant2(y,x) --> tant2(7,7)
- 可以直接使用点的横纵坐标
- 函数取值【-180,180】之间,第一、第二象限返回的是:0-180,第一、第二象限返回的是:0-(-180)
详细请看下图:
没画好,有什么好的在线画图网站吗?
假设点 A(7,7)
- 使用
Math.toDegrees(Math.atan(7/7))计算出来的是45度角 - 使用
Math.toDegrees(Math.atan2(7,7))计算出来的是45度角
相比较起来,还是 atan2 更方便使用一些,直接使用点的坐标就可以了,不用对像 tan 一样还得想一下谁除谁
我们选择一些点,大家看一下 atan2 取值范围,包含的点:(7,7),(7,-7),(-7,7),(-7,-7),(7,0)
Log.d("AAA", "atan2(7:7) --> ${Math.toDegrees(Math.atan2(7.0, 7.0))}")
Log.d("AAA", "atan2(-7:7) --> ${Math.toDegrees(Math.atan2(7.0, -7.0))}")
Log.d("AAA", "atan2(7:-7) --> ${Math.toDegrees(Math.atan2(-7.0, 7.0))}")
Log.d("AAA", "atan2(-7:-7) --> ${Math.toDegrees(Math.atan2(-7.0, -7.0))}")
Log.d("AAA", "atan2(7:0) --> ${Math.toDegrees(Math.atan2(0.0, 7.0))}")
D/AAA: atan2(7:7) --> 45.0
D/AAA: atan2(-7:7) --> 135.0
D/AAA: atan2(7:-7) --> -45.0
D/AAA: atan2(-7:-7) --> -135.0
D/AAA: atan2(7:0) --> 0.0
上面 tant2 我们算的是 点到坐标原点之间的斜线的角度,那要是不是坐标原点呢?
只需要将两点x,y坐标分别相减得到一个新的点(x2-x1,y2-y1).然后利用他求出角度就可以了
一定要注意:要大值减小值,要不用 atan2 会出现方向相反的问题,不确定的你就用 abs 绝对值,看例子:
Log.d("AAA", "atan2(7:7):(3,3) --> ${Math.toDegrees(Math.atan2(-4.0, -4.0))}")
Log.d("AAA", "atan2(3:3):(7,7) --> ${Math.toDegrees(Math.atan2(4.0, 4.0))}")
D/AAA: atan2(7:7):(3,3) --> -135.0
D/AAA: atan2(3:3):(7,7) --> 45.0
关于 atan/atan2 取的是哪个角的问题,还有不明白的同学请看这个测试:
Log.d("AAA", "atan2(7,5) --> ${Math.toDegrees(Math.atan2(5.0, 7.0))}")
Log.d("AAA", "atan2(-7,5) --> ${Math.toDegrees(Math.atan2(5.0, -7.0))}")
D/AAA: atan2(7,5) --> 35.53767779197438
D/AAA: atan2(-7,5) --> 144.46232220802563
- (7,5) 这个点在第一象限形成的切角应该 < 45度
- (-7,5) 这个点在第一象限形成的切角应该 > 135度
大家看上面的测试,第一象限是 35度角,第二象限是 145度角,大家自己画图对比下