white_board.dart 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import 'dart:convert';
  2. import 'package:fis_common/event/event_type.dart';
  3. import 'package:fis_lib_business_components/components/white_board/utils.dart';
  4. import 'package:fis_lib_business_components/components/white_board/structure.dart';
  5. import 'package:fis_lib_business_components/components/white_board/white_board_painter.dart';
  6. import 'package:fis_ui/index.dart';
  7. import 'package:flutter/material.dart';
  8. class FWhiteBoard extends StatefulWidget implements FWidget {
  9. const FWhiteBoard({
  10. key,
  11. required this.initData,
  12. required this.userId,
  13. required this.paintType,
  14. required this.sendData,
  15. required this.onReceiveData,
  16. required this.onClearCanavs,
  17. });
  18. /// 初始画板副本数据
  19. final List<String> initData;
  20. /// 用户id
  21. final String userId;
  22. /// 当前绘制模式
  23. final PaintType paintType;
  24. /// 发送数据(接 JsonRPC)
  25. final void Function(String data) sendData;
  26. /// 接收数据(接 WebSocket)
  27. final FEventHandler<String> onReceiveData;
  28. /// 画布清除事件
  29. final FEventHandler<String> onClearCanavs;
  30. @override
  31. State<FWhiteBoard> createState() => _FWhiteBoardState();
  32. }
  33. class _FWhiteBoardState extends State<FWhiteBoard> {
  34. /// 画布尺寸
  35. late double canvasWidth = 0.0;
  36. late double canvasHeight = 0.0;
  37. /// 当前用户的画笔
  38. WhiteBoardPen myPen = WhiteBoardPen();
  39. /// 当前用户的画笔颜色
  40. Color get myPenColor => ColorUtil.generateColor(widget.userId);
  41. /// 固定的画笔粗细
  42. static const double strokeWidth = 3.0;
  43. /// 异步绘制队列
  44. List<String> _asyncDrawQueue = [];
  45. /// 异步绘制锁
  46. bool _asyncDrawLock = false;
  47. @override
  48. void initState() {
  49. super.initState();
  50. widget.onReceiveData.addListener((sender, data) {
  51. _onReceiveDrawData(data);
  52. });
  53. widget.onClearCanavs.addListener((sender, userId) {
  54. _clearUserLines(userId);
  55. });
  56. /// 加载完后一帧绘制初始数据
  57. WidgetsBinding.instance.addPostFrameCallback((_) {
  58. _paintInitData();
  59. });
  60. }
  61. @override
  62. Widget build(BuildContext context) {
  63. canvasWidth = MediaQuery.of(context).size.width;
  64. canvasHeight = MediaQuery.of(context).size.height;
  65. return MouseRegion(
  66. cursor: SystemMouseCursors.click,
  67. child: GestureDetector(
  68. onPanDown: _onPanDown,
  69. onPanUpdate: _onPanUpdate,
  70. onPanEnd: _onPanEnd,
  71. onPanCancel: _onPanCancel,
  72. onDoubleTap: myPen.clear,
  73. child: CustomPaint(
  74. size: MediaQuery.of(context).size,
  75. painter: WhiteBoardPainter(
  76. pen: myPen,
  77. width: canvasWidth,
  78. height: canvasHeight,
  79. ),
  80. ),
  81. ),
  82. );
  83. }
  84. /// 按下时,生成新的线实体
  85. void _onPanDown(DragDownDetails details) {
  86. Line line = Line(
  87. color: myPenColor,
  88. strokeWidth: strokeWidth,
  89. paintType: widget.paintType,
  90. userId: widget.userId,
  91. );
  92. myPen.pushLine(line);
  93. }
  94. /// 拖动时,生成新的点实体,加入到当前线实体中
  95. void _onPanUpdate(DragUpdateDetails details) {
  96. myPen.pushPoint(Point.fromOffset(
  97. details.localPosition,
  98. canvasWidth,
  99. canvasHeight,
  100. ));
  101. }
  102. /// 抬起时,发送数据
  103. void _onPanEnd(DragEndDetails details) {
  104. _sentDrawAction();
  105. myPen.doneLine();
  106. }
  107. /// 绘制初始数据
  108. void _paintInitData() {
  109. for (int i = 0; i < widget.initData.length; i++) {
  110. _onReceiveDrawDataImmediately(widget.initData[i]);
  111. }
  112. }
  113. /// 发送绘制指令
  114. void _sentDrawAction() {
  115. List<List<double>> pointsList = [];
  116. for (int i = 0; i < myPen.activeLine.points.length; i++) {
  117. pointsList.add(myPen.activeLine.points[i].toList());
  118. }
  119. widget.sendData(jsonEncode({
  120. "u_Id": widget.userId, // 用户id
  121. "l_Id": "", // 线id
  122. "points": PointsUtil.compressPointsList(pointsList),
  123. }));
  124. }
  125. /// 接收绘制数据【如果存在数据正在绘制,其他的加入绘制队列,等待当前绘制完成继续绘制】
  126. void _onReceiveDrawData(String jsonData) async {
  127. if (_asyncDrawLock) {
  128. _asyncDrawQueue.add(jsonData);
  129. return;
  130. }
  131. _asyncDrawLock = true;
  132. var data = jsonDecode(jsonData);
  133. List<dynamic> pointsList = PointsUtil.decompressPointsList(data["points"]);
  134. Line line = Line(
  135. color: ColorUtil.generateColor(data["u_Id"]),
  136. strokeWidth: strokeWidth,
  137. userId: data["u_Id"],
  138. );
  139. myPen.pushLine(line);
  140. for (int i = 0; i < pointsList.length; i++) {
  141. myPen.pushPoint(Point.fromList(
  142. pointsList[i],
  143. ));
  144. await Future.delayed(const Duration(milliseconds: 2));
  145. }
  146. myPen.doneLine();
  147. _asyncDrawLock = false;
  148. /// 判断如果队列存在数据,继续绘制
  149. if (_asyncDrawQueue.isNotEmpty) {
  150. _onReceiveDrawData(_asyncDrawQueue.removeAt(0));
  151. }
  152. }
  153. /// 接收绘制数据立即绘制,无 Future.delayed
  154. void _onReceiveDrawDataImmediately(String jsonData) {
  155. var data = jsonDecode(jsonData);
  156. List<dynamic> pointsList = PointsUtil.decompressPointsList(data["points"]);
  157. Line line = Line(
  158. color: ColorUtil.generateColor(data["u_Id"]),
  159. strokeWidth: strokeWidth,
  160. userId: data["u_Id"],
  161. );
  162. myPen.pushLine(line);
  163. for (int i = 0; i < pointsList.length; i++) {
  164. myPen.pushPoint(Point.fromList(
  165. pointsList[i],
  166. ));
  167. }
  168. myPen.doneLine();
  169. }
  170. void _clearUserLines(String userId) {
  171. myPen.clearCurrectUserLines(userId);
  172. }
  173. void _onPanCancel() {
  174. myPen.removeEmpty();
  175. }
  176. }