本文正在参与「金石方案 . 分割6万现金大奖」。

logo

官方运用的logo从网页上看到是个svg文件,官方logo,点击去能够看到logo和文字都是些path标签。

Flutter【绘制】制作一个掘金Logo组件
svg的原理也是经过途径制造出来的图形,和Flutter途径制造原理类似,相同能够制造出任何平面图形,了解svg相关常识,能够看看张老师的svg解析:【Flutter 制造番外】svg 文件与制造 (上)。

为了稳固下Flutter制造的相关常识,今日咱们就用Flutter途径从头开始制造封装一个的logo组件, 的logo看起来很简单,可是其间还是触及到了很多制造以及三角函数的常识的。

制造菱形

首要咱们能够看到最上面是一个菱形,经过量角器测得logo的视点大约为100,那么菱形的上下视点也就为100。

Flutter【绘制】制作一个掘金Logo组件

为了封装的通用性,咱们设菱形的边长为side,菱形上方一半的视点为angle= 50,依据三角函数就能够得到菱形的四个坐标点,经过path途径进行链接。
代码:

double angle = pi / 18* 5;
// 菱形边长
double side = 50;
Paint paint = Paint()
  ..style = PaintingStyle.fill
  ..isAntiAlias = true
  ..strokeJoin= StrokeJoin.miter
   ..color = Color(0xff1E80FF).withOpacity(0.7);
// 顶部菱形
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();
canvas.drawPath(path, paint);

就能够得到以下作用,设置透明度为了下面核算作用能够看的更加直观。

Flutter【绘制】制作一个掘金Logo组件

制造折线

接下来制造菱形下方的折线,折线咱们运用非填充画笔来实现,首要的logo整体关于y轴对称,视点一致,要害要核算折线之间与菱形的间隔,首要咱们知道菱形四个点的坐标,那么最下面的坐标便是(0, side * cos(angle));, 依据logo的规划,折线的宽度大约为菱形边长的0.7倍,所以这儿咱们暂设画笔的宽度为double paintWidth = side * 0.7;,y轴折线中心点间隔菱形底部的间隔为下图红线部分,这个间隔大约为菱形边长的1.5倍

左右衔接
Flutter【绘制】制作一个掘金Logo组件
Flutter【绘制】制作一个掘金Logo组件

代码:

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);

Flutter【绘制】制作一个掘金Logo组件

那么折线之间的间隔也就能够算出来了。
代码:

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);

作用:

Flutter【绘制】制作一个掘金Logo组件

裁剪

上方大致画出来了作用,接下来需求进行对画布进行裁剪成以下暗影作用,主要便是核算b点和d点的坐标,触及到两条直线的交点和三角函数。

Flutter【绘制】制作一个掘金Logo组件

首要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));

Flutter【绘制】制作一个掘金Logo组件

同理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);

作用:

原始 移到画布中间
Flutter【绘制】制作一个掘金Logo组件
Flutter【绘制】制作一个掘金Logo组件

上面咱们是经过菱形边长去求的各个坐标点,现在咱们为了运用便利需求是已知组件宽高,求菱形的边长,这样组件运用起来才会比较便利精准的控制组件巨细,这儿便是一些的繁琐的倒推核算,假定咱们的高度是咱们设定的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,
    ),
  );
}

作用:

Flutter【绘制】制作一个掘金Logo组件

由于这儿露出了视点,所以也能够自定义视点,例如下便利是logo的视点是从40-100的变化。

Flutter【绘制】制作一个掘金Logo组件

当然这儿还能够露出一些色彩、渐变色等一些特点,就不逐个展示了。掌握原理即可。

总结

经过制造logo这个组件,又稳固了Flutter制造的的相关常识和一些根底的三角函数核算常识,同时对封装组件所需注意的事项也有了加深的了解,那这篇文章就到这儿,期望对你在Flutter制造以及封装组件方面有所协助,如有协助,欢迎点赞,如有疑问,欢迎指正~