[용선축제] 말하는 만두 본 적 있어?

자세한 내용은 "Early Summer Creative Contest"에 참여하고 있습니다. 자세한 내용은 Early Summer Creative Contest 를 참조하십시오 .

머리말

안녕하세요 여러분, 단오절이 다가오고 있습니다. 먼저 너겟의 건강한 단오절을 기원합니다. 중화민족의 매우 중요한 전통 축제로서 단오절은 빼놓을 수 없는 것이지만, 정말 알고 계십니까? 만두의 역사? 오늘 이 글을 따라 Flutter path인기 과학 축제에서 인기를 끌 zongzi를 그려보세요~

그리다

1. 기본개요

먼저 종지의 기본 윤곽을 그려야 합니다. 사진에서 종자의 윤곽이 둥근 삼각형임을 알 수 있습니다.
이 기사의 모든 그래픽은 순수한 Path경로로 만들어졌습니다. 여기에서 우리는 나눌 수 있습니다 zongzi의 윤곽은 세 부분으로 나뉩니다. 두 번째 수준의 Bezier 곡선은 머리 윤곽, 왼쪽 및 오른쪽 잎을 그리는 데 사용됩니다. 핵심 코드:


canvas.translate(size.width / 2, size.height / 2);
canvas.translate(-50, -50);
Paint paint = Paint()
  ..style = PaintingStyle.stroke
  ..strokeWidth = 2
  ..color = Colors.black
  ..isAntiAlias = true;

Path path = Path();
path.relativeQuadraticBezierTo(50, -80, 100, 0);
path.relativeQuadraticBezierTo(90, 130, -50, 130);
path.relativeQuadraticBezierTo(-140, 0, -50, -130);
path.close();
canvas.drawPath(path, paint);
复制代码

효과 그림:
이미지.png

2. 만두잎

기본 윤곽선으로 다음으로 zongzi 잎을 그려야 합니다. zongzi 잎의 모양이 불규칙한 모양을 볼 수 있습니다. 여기에서 Path 경로를 사용하여 두 경로를 결합하여 새 경로를 생성할 수 있습니다. 왼쪽 zongzi를 얻을 수 있습니다.잎 영역, 핵심 코드:

Path path2 = Path();
path2.relativeQuadraticBezierTo(60, 100, 190, 130);
path2.relativeLineTo(0, 40);
path2.relativeLineTo(-260, 0);
path2.relativeLineTo(0, -200);
path2.close();

canvas.drawPath(
   path2,
   paint
     ..color = Color(0xFF2A9200));
复制代码

효과 그림:
이미지.png

그런 다음 경로 조인트를 사용하여 이 두 영역의 교차점을 가져와 종계의 왼쪽 영역을 얻습니다.
핵심 코드:

Path pathStart = Path.combine(PathOperation.intersect, path, path2);
pathStart.close();

canvas.drawPath(
   pathStart,
   paint
     ..color = Color(0xFF2A9200)
     ..style = PaintingStyle.fill);
复制代码

효과 그림:
이미지.png

우리가 영역을 얻은 후에는 더 많은 텍스처가 필요하며 더 많은 zongye 잎처럼 보입니다. 여기에서 계속 경로 결합을 사용합니다. 위에서 합성한 새 경로를 왼쪽 아래로 여러 번 오프셋하면 됩니다. 핵심 코드:

_canvasStartLines(Canvas canvas, Path pathStart, Paint paint) {
  for (int i = 1; i < 10; i++) {
    Path path = Path();
    path.moveTo(-8 * i.toDouble(), 8 * i.toDouble());
    path.relativeQuadraticBezierTo(60, 100, 190, 130);
    path.relativeLineTo(0, 60);
    path.relativeLineTo(-300, 0);
    path.relativeLineTo(0, -200);
    path.close();
    canvas.drawPath(
        Path.combine(PathOperation.intersect, pathStart, path),
        paint
          ..color = Colors.black
          ..style = PaintingStyle.stroke);
  }
}
复制代码

효과 그림:
이미지.png

다음으로 오른쪽 잎사귀도 마찬가지인데 핵심코드
는 여기에 올리지 않겠습니다 보고 싶은 분들은 데모를 참고하시고 주소는 하단에 github에 올려두었습니다 .

효과 그림:
이미지.png

만두 잎이 거의 완성되었습니다.

입의 경우 3차 베지어 곡선을 사용하여 행복한 표정을 그립니다.
핵심 코드:

Path path4 = Path();
path4.moveTo(40, 20);
path4.relativeCubicTo(2, 18, 18, 18, 20, 0);
path4.close();
canvas.drawPath(path4, paint..color = Colors.black87);
复制代码

효과 그림:
이미지.png

우리는 또한 행복해 보이는 두 개의 3차 베지어 곡선을 눈에 사용합니다.

/// 眼睛
Path path5 = Path();
path5.moveTo(20, 5);
path5.relativeCubicTo(5, -10, 15, -10, 20, 0);
canvas.drawPath(
    path5,
    paint
      ..color = Colors.black87
      ..style = PaintingStyle.stroke);
canvas.save();
canvas.translate(40, 0);
canvas.drawPath(
    path5,
    paint
      ..color = Colors.black87
      ..style = PaintingStyle.stroke);
canvas.restore();
复制代码

효과 그림:
이미지.png

붉히다

接下来给面部设置下肤色,然后添加一点点细节。这里我们给path路径添加一个椭圆点缀那么一下。 核心代码:

/// 晒红
Path path9 = Path();
path9.addArc(Rect.fromCenter(center: Offset(30,30), width: 4, height: 6),0,pi*2);
canvas.drawPath(path9, paint..color = Color(0xFFFFA2AE)..style = PaintingStyle.fill);
canvas.save();
canvas.translate(6, 0);
canvas.drawPath(path9, paint..color = Color(0xFFFFA2AE)..style = PaintingStyle.fill);
canvas.restore();
canvas.save();
canvas.translate(34, 0);
canvas.drawPath(path9, paint..color = Color(0xFFFFA2AE)..style = PaintingStyle.fill);
canvas.restore();
canvas.save();
canvas.translate(40, 0);
canvas.drawPath(path9, paint..color = Color(0xFFFFA2AE)..style = PaintingStyle.fill);
canvas.restore();
复制代码

效果图:
이미지.png

手&脚

看着光溜溜的没有手脚怎么行,接下来我们继续使用path路径给粽子添加手脚,这里有一个知识点就是我们需要找到手脚的位置坐标在哪,就需要使用到 path的路径测量 ,根据路径上的点找到我们合适的手脚位置。
通过path.computeMetrics()我们可以得到一个路径的迭代对象PathMetric() ,这个迭代对象里面包含这个路径所有图形的很多信息,我们都可以从这个对象得到,这里我们从粽叶路径中得到我们的手脚的坐标点。绘制手脚, 这里只贴了左手的代码,其他同理。
核心代码:

///粽叶路径
///左边
var pms = pathStart.computeMetrics();
var first = pms.first;
var offsetStart = first.getTangentForOffset(first.length * 0.55)!;
/// 
Path path7 = Path();
path7.moveTo(offsetStart.position.dx, offsetStart.position.dy);
path7.relativeLineTo(-30, 20);
path7.relativeLineTo(-5, -30);
/// 左手
canvas.drawPath(
    path7,
    paint
      ..color = Colors.black
      ..style = PaintingStyle.stroke
      ..strokeWidth = 3);
复制代码

效果图: 이미지.png

头巾

粽子有了,接下来给粽子来个标记,我是甜粽子还是咸粽子,毕竟有人爱吃咸粽子,有人爱吃甜粽子, 这里我们用路径绘制一个头绳,然后在头绳中间对标记粽子的咸甜。
这里知识点需要掌握绘制文字,核心代码:

Path path6 = Path();
path6.moveTo(0, -50);
path6.quadraticBezierTo(50, 10, 100, -50);
canvas.drawPath(
    Path.combine(PathOperation.intersect, path, path6),
    paint
      ..color = Colors.pink
      ..style = PaintingStyle.stroke);

var textPainter = TextPainter(
    text: TextSpan(
        text: "甜", style: TextStyle(fontSize: 16, color: Colors.white)),
    textDirection: TextDirection.ltr);
textPainter.layout();
var size2 = textPainter.size;

canvas.drawCircle(
    Offset(50, -20),
    size2.width,
    paint
      ..color = Colors.pink
      ..style = PaintingStyle.fill);
textPainter.paint(
    canvas, Offset(-size2.width / 2, -size2.height / 2).translate(50, -20));
复制代码

效果图: 이미지.png

咸甜是一家

有甜粽子那也得有咸粽子不是,我们只需将上面粽子的代码复制,改下文字就可以制作一个咸粽子啦。 效果图: 이미지.png 背景有点空,再加个背景图:
이미지.png OK,粽子的绘制工作到这里已经结束了,接下来我们让粽子说话可以给小朋友科普端午节的来历吧。

发声

粽子制作完成,接下来我们需要让粽子会说话,这里我使用的讯飞的语音合成WebAPI流式传输数据,将文本转化为音频文件实时播放,只需要调用接口即可将文本转化为音频文件播放,这里需要web_socket_channel插件来和讯飞进行webSocket连接,这样做的好处是不需要集成任何sdk,只需要通过APi接口就可以实时转换, 이미지.png

web_socket_channel的简单使用:
创建连接:
이미지.png
发送消息方法:_channel?.sink.add(data);
在listen监听接收信息。
具体实现步骤已经整理文章分享: Flutter实现讯飞在线语音合成(WebSocket流式版)。

动画控制嘴巴开合

这里我们就让甜粽子为我们讲解,动画绘制之间的配合使用之前的文章介绍过多次,这里就不再过多介绍,直接看效果吧。

  • 嘴巴张合运动曲线:

d8de77b13cc41ee8e0e97d6bf50e35d5.gif
身体也加一个默认的运动曲线。
效果图:

1653970078130.gif 科普文字
이미지.png 加上科普文章,加上声音,此处已经有声音科普了哦,想体验的小伙伴可以下载源码自己体验一下,传送门 demo地址

1653970114575.gif 这里我们使用童声豆豆的声音, 이미지.png 设置到这里即可。 示例代码demo地址 이미지.png

用到的技术点

绘制: path路径贝塞尔曲线路径联合路径测量路径添加图形绘制文字绘制图片域
动画: 多动画与绘制联合使用。
通信: web_socket_channel 的使用。
文件操作: 插件 path_provider 写入文件。
播放音频操作: 插件 audioplayers 播放音频。

总个结

이 영감은 중국 전통 대중 과학 축제의 영상에서 따온 것이고, 그 다음에 이 글이 있습니다. 이전에 스마트 가구를 만들 때 클라이언트 측에서 iFLYTEK 음성을 통합했기 때문에 iFLYTEK의 공식 문서에 가서 확인했습니다. Flutter 플러그인이 있긴 WebSocket한데 당연히 없는게 스트림 형태의 전송이 있는거 보고 음, 너인줄 알았는데 데모를 찾다보니 dart언어버전이 없었다. 문서를 단계별로 따라가 마침내 요구 사항을 실현했습니다. WebSocket방법이 매우 적합합니다.크로스 플랫폼 응용 프로그램의 사용은 SDK를 통합할 필요가 없으며 스트림 전송을 통해서만 변환을 완료할 수 있습니다. 한 쪽 끝에서 SDK 통합을 사용하는 것은 정말 편리하지만 크로스 플랫폼 응용 프로그램에서는 번거롭습니다. 그 후, Xunfei의 음성 인식을 연구하는 시간이 있는데, 비슷해야 합니다. 자, 여기까지입니다. 끝으로 너겟의 건강한 단오절, 정원에서의 향기로운 삶을 기원합니다.

рекомендация

отjuejin.im/post/7103758484910866446
рекомендация