Flutter【绘制】制作一个掘金Logo组件_flutter png svg
monyOS鸿蒙开发知识点,真正体系化!**
绘制折线
接下来绘制菱形下方的折线,折线我们使用非填充画笔来实现,首先掘金的logo整体关于y轴对称,角度一致,关键要计算折线之间与菱形的距离,首先我们知道菱形四个点的坐标,那么最下面的坐标就是(0, side * cos(angle));
, 根据掘金logo的设计,折线的宽度大约为菱形边长的0.7倍
,所以这里我们暂设画笔的宽度为double paintWidth = side * 0.7;
,y轴折线中心点距离菱形底部的距离为下图红线部分,这个距离大约为菱形边长的1.5倍
。
左 | 左右连接 |
---|---|
代码:
Path path2 = Path();
// 原点距离下方折线中心y轴距离
double h1 = side * cos(angle) + side * 1.5;
path2.moveTo(-h1 * tan(angle), 0);
path2.lineTo(0, h1);
path2.lineTo(h1 * tan(angle), 0);
canvas.drawPath(path2, paint);
接下来绘制最下面的折线,这里为了让两条折线之间距离一致,我们需要计算出下图c
点坐标,下图中b
是中点,那么ab=bc
,求出ab的长度也就知道c
点的坐标了,过a
点做bd
垂直线交点设为g
,那么已知ag
等于线宽的1/2,角abg= angle°;
,就能得出ab
的长度 ab = paintWidth / 2 / sin(angle);
,那么也就得到c点坐标=(0, h1+ab);
那么折线之间的距离也就可以算出来了。
代码:
Path path3 = Path();
double h2 = h1 +
(paintWidth / 2 / sin(angle) + side * 1.5);
path3.moveTo(-h2 * tan(angle), 0);
path3.lineTo(0, h2);
path3.lineTo(h2 * tan(angle), 0);
效果:
裁剪
上方大致画出来了效果,接下来需要进行对画布进行裁剪成以下阴影效果,主要就是计算b
点和d
点的坐标,涉及到两条直线的交点和三角函数。
首先a
点的值可以通过两条相交直线求交点公式可以得出,然后过b
点做红线的中垂线先计算出ab
的值,已知bd = paintWidth / 2
,角bad = 180°-100°=80°
,那么就可以得出ab = paintWidth / 2 / sin(pi - angle * 2)
,然后再分别过a
点和b
点做垂直三角形,就能得出b
点坐标为(a.x - paintWidth / 2 / sin(pi - angle * 2) * sin(angle), a.y + paintWidth / 2 / sin(pi - angle * 2) * cos(angle));
同理d
点坐标也可得出。
计算代码:
Point left = toTwoPoint(Point(-side * sin(angle), 0),
Point(0, -side * cos(angle)), Point(-h2 * tan(angle), 0), Point(0, h2));
Point right = toTwoPoint(Point(side * sin(angle), 0),
Point(0, -side * cos(angle)), Point(h2 * tan(angle), 0), Point(0, h2));
Path pathBg = Path();
pathBg.moveTo(0, -side * cos(angle));
pathBg.lineTo(
left.x.toDouble() - paintWidth / 2 / sin(pi - angle * 2) * sin(angle),
left.y.toDouble() + paintWidth / 2 / sin(pi - angle * 2) * cos(angle));
pathBg.lineTo(left.x.toDouble(), h2 + (paintWidth / 2 / sin(pi - angle * 2) / sin(angle)));
pathBg.lineTo(right.x.toDouble(), h2 + (paintWidth / 2 / sin(pi - angle * 2)/ sin(angle)));
pathBg.lineTo(right.x.toDouble() + paintWidth / 2 / sin(pi - angle * 2) * sin(angle),
right.y.toDouble() + paintWidth / 2 * cos(angle));
pathBg.close();
// 通过裁剪画布得到最终效果
canvas.clipPath(pathBg);
效果:
原始 | 移到画布中间 |
---|---|
上面我们是通过菱形边长去求的各个坐标点,现在我们为了使用方便需求是已知组件宽高,求菱形的边长,这样组件使用起来才会比较方便精准的控制组件大小,这里就是一些的繁琐的倒推计算,假设我们的高度是我们设定的height
,那么宽度其实是也就确定了,因为角度一旦确立,宽度自然也就确定了,所以这里我们向外暴露两个属性
,一个组件高度
,一个菱形角度
即可。
完整源码:
/// 掘金logo组件
class JueJinLogo extends StatelessWidget {
final double height; // 组件高度
final double angle; // 菱形上下角度1/2
const JueJinLogo({Key? key, this.height = 140, this.angle = pi / 18 * 5})
: super(key: key);
@override
Widget build(BuildContext context) {
double m = 0.7;// 折线线宽相对菱形边长倍数
double n = 1.5;// 折线之间线宽相对菱形边长倍数
var a = (2 * cos(angle) + m * 0.5 / sin(angle) + 3);
double side = height / (a + m * 0.5 / sin(pi - angle * 2) / sin(angle));
double paintWidth = m * side;
double h2 = side * cos(angle) +
side * n +
(paintWidth / 2 / sin(angle) + side * n);
Point right = PointUtil.toTwoPoint(Point(side * sin(angle), 0),
Point(0, -side * cos(angle)), Point(h2 * tan(angle), 0), Point(0, h2));
double width = (right.x.toDouble() +
paintWidth / 2 / sin(pi - angle * 2) * sin(angle)) *
2;
return CustomPaint(
size: Size(width, height),
painter: _JueJinLogoPaint(side, angle),
);
}
}
class _JueJinLogoPaint extends CustomPainter {
double side;
double angle;
_JueJinLogoPaint(this.side, this.angle);
@override
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
double paintWidth = side * 0.7;
Paint paint = Paint()
..strokeWidth = paintWidth
..style = PaintingStyle.fill
..isAntiAlias = true
..strokeJoin = StrokeJoin.miter
..color = Color(0xff1E80FF);
canvas.save();
Path path = Path();
path.moveTo(-side * sin(angle), 0);
path.lineTo(0, -side * cos(angle));
path.lineTo(side * sin(angle), 0);
path.lineTo(0, side * cos(angle));
path.close();
Path path2 = Path();
double h1 = side * cos(angle) + side * 1.5;
path2.moveTo(-h1 * tan(angle), 0);
path2.lineTo(0, h1);
path2.lineTo(h1 * tan(angle), 0);
Path path3 = Path();
double h2 = h1 + (paintWidth / 2 / sin(angle) + side * 1.5);
path3.moveTo(-h2 * tan(angle), 0);
path3.lineTo(0, h2);
path3.lineTo(h2 * tan(angle), 0);
// 平移组件到画布中心
canvas.translate(
0,
side * cos(angle) -
(h2 + (paintWidth / 2 / sin(angle)) + side * cos(angle)) / 2);
Point left = PointUtil.toTwoPoint(Point(-side * sin(angle), 0),
Point(0, -side * cos(angle)), Point(-h2 * tan(angle), 0), Point(0, h2));
Point right = PointUtil.toTwoPoint(Point(side * sin(angle), 0),
Point(0, -side * cos(angle)), Point(h2 * tan(angle), 0), Point(0, h2));
Path pathBg = Path();
pathBg.moveTo(0, -side * cos(angle));
pathBg.lineTo(
left.x.toDouble() - paintWidth / 2 / sin(pi - angle * 2) * sin(angle),
left.y.toDouble() + paintWidth / 2 / sin(pi - angle * 2) * cos(angle));
pathBg.lineTo(left.x.toDouble(),
h2 + (paintWidth / 2 / sin(pi - angle * 2) / sin(angle)));
pathBg.lineTo(right.x.toDouble(),
h2 + (paintWidth / 2 / sin(pi - angle * 2) / sin(angle)));
pathBg.lineTo(right.x.toDouble() + paintWidth / 2 * sin(angle),
right.y.toDouble() + paintWidth / 2 * cos(angle));
pathBg.close();
// 裁剪画布
canvas.clipPath(pathBg);
// 绘制菱形以及折线
canvas.drawPath(path, paint);
canvas.drawPath(path2, paint..style = PaintingStyle.stroke);
canvas.drawPath(path3, paint..style = PaintingStyle.stroke);
canvas.restore();
}
@override
bool shouldRepaint(covariant _JueJinLogoPaint oldDelegate) {
return false;
}
}
class PointUtil {
/// 两点求直线方程
static double towPointKb(Point<double> p1, Point<double> p2,
{bool isK = true}) {
/// 求得两点斜率
double k = 0;
double b = 0;
// 防止除数 = 0 出现的计算错误 a e x轴重合
if (p1.x == p2.x) {
k = (p1.y - p2.y) / (p1.x - p2.x - 1);
} else {
k = (p1.y - p2.y) / (p1.x - p2.x);
}
b = p1.y - k * p1.x;
if (isK)
return k;
else
return b;
}
static Point<double> toTwoPoint(
Point<double> a, Point<double> b, Point<double> m, Point<double> n) {
double k1 = towPointKb(a, b);
double b1 = towPointKb(a, b, isK: false);
double k2 = towPointKb(m, n);
double b2 = towPointKb(m, n, isK: false);
return Point((b2 - b1) / (k1 - k2), (b2 - b1) / (k1 - k2) * k1 + b1);
}
}
使用
使用也是非常的方便,直接设置组件高度即可。
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: JueJinLogo(
height: 200,
),
);
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
monyOS鸿蒙开发知识点,真正体系化!**
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
[外链图片转存中…(img-y9aPjj7u-1712835155787)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
更多推荐
所有评论(0)