【ドラゴンボートフェスティバル】おしゃべり餃子を見たことがありますか?

詳しくは「初夏クリエイティブコンテスト」に参加しておりますので、早夏クリエイティブコンテストをご覧ください

序文

みなさん、こんにちは。ドラゴンボートフェスティバルが近づいています。まず、ナゲッツが健康的なドラゴンボートフェスティバルになりますように。中国の国の非常に重要な伝統的なフェスティバルとして、ドラゴンボートフェスティバルは不可欠ですが、本当に知っていますか?餃子の歴史?今日この記事に従ってFlutter path、ポピュラーサイエンスフェスティバルで人気のちまきを描いてください〜

描く

1.基本概要

まず、ちまきの基本的な輪郭を描く必要があります。写真から、ちまきの輪郭が丸い三角形であることがわかります。
この記事のすべてのグラフィックは純粋なPathパスで作成されています。ここで、ちまきの輪郭を3つの部分に分割します。2番目のレベルのベジェ曲線を使用して、頭、左右の葉の輪郭を描画します。コアコード:


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);
复制代码

効果画像:
image.png

2.餃子の葉

基本的な輪郭で、次にちまきの葉を描く必要があります。ちまきの葉の形が不規則な形であることがわかります。ここでは、パスパスを使用して2つのパスを結合し、新しいパスを生成できます。左のちまきを取得できます。葉の領域、コアコード:

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));
复制代码

効果画像:
image.png

次に、パスジョイントを使用して、これら2つの領域の交差点を取得し、ゾンゲの左側の領域を取得します。
コアコード:

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

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

効果画像:
image.png

エリアを取得したら、ゾンゲの葉のように見えるテクスチャを追加する必要があります。ここでは引き続きパスユニオンを使用します。上で合成した新しいパスを左下に数回オフセットするだけで済みます。コアコード:

_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);
  }
}
复制代码

効果画像:
image.png

次に、右側の葉についても同じことが言えます。コアコード
はここに投稿されません。見たい人は、デモを参照してください。アドレスは下部にあり、githubにアップロードされています。 。

効果画像:
image.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);
复制代码

効果画像:
image.png

また、目には2つの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();
复制代码

効果画像:
image.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();
复制代码

效果图:
image.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);
复制代码

效果图: image.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));
复制代码

效果图: image.png

咸甜是一家

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

发声

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

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

动画控制嘴巴开合

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

  • 嘴巴张合运动曲线:

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

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

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

用到的技术点

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

总个结

このインスピレーションは、中国の伝統的な通俗科学の祭典のビデオから来ています。以前にスマート家具を作るときにクライアント側でiFLYTEKの音声を統合したので、この記事があります。そこで、iFLYTEKの公式ドキュメントにアクセスして確認しました。 Flutterプラグインはありますが、明らかにありませんが、WebSocketストリームの形で送信されているのを見て、ええと、あなただと思いましたが、デモを探していたとき、dart言語バージョンはありませんでした。ドキュメントを段階的に実行し、最終的に要件を実現しました。このWebSocket方法は非常に適しています。クロスプラットフォームアプリケーションの使用にはSDKを統合する必要はなく、変換はストリームの送信によってのみ完了できます。片方の端でSDK統合を使用するのは本当に便利ですが、クロスプラットフォームアプリケーションでは面倒です。その後、Xunfeiの音声認識を勉強する時間があります。これは似ているはずです。OK、この記事は以上です。最後に、ナゲッツが健康的なドラゴンボートフェスティバルを前もって開催し、庭で香りのよい生活を送れることを願っています。

おすすめ

転載: juejin.im/post/7103758484910866446