white_board.dart 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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. import 'task_queue.dart';
  9. class FWhiteBoard extends StatefulWidget implements FWidget {
  10. const FWhiteBoard({
  11. key,
  12. required this.initData,
  13. required this.userId,
  14. required this.patientColor,
  15. required this.paintType,
  16. required this.sendData,
  17. required this.onReceiveData,
  18. required this.onClearCanavs,
  19. });
  20. /// 初始画板副本数据
  21. final List<String> initData;
  22. /// 用户id
  23. final String userId;
  24. /// 用户颜色
  25. final Color patientColor;
  26. /// 当前绘制模式
  27. final PaintType paintType;
  28. /// 发送数据(接 JsonRPC)
  29. final void Function(String data) sendData;
  30. /// 接收数据(接 WebSocket)
  31. final FEventHandler<String> onReceiveData;
  32. /// 画布清除事件
  33. final FEventHandler<String> onClearCanavs;
  34. @override
  35. State<FWhiteBoard> createState() => _FWhiteBoardState();
  36. }
  37. class _FWhiteBoardState extends State<FWhiteBoard> {
  38. /// 画布尺寸
  39. late double canvasWidth = 0.0;
  40. late double canvasHeight = 0.0;
  41. /// 当前用户的画笔
  42. WhiteBoardPen myPen = WhiteBoardPen();
  43. /// 所有历史画笔
  44. WhiteBoardPen historicalBrush = WhiteBoardPen();
  45. /// 当前用户的画笔颜色
  46. Color get myPenColor => widget.patientColor;
  47. /// 固定的画笔粗细
  48. static const double strokeWidth = 3.0;
  49. /// 任务队列
  50. late final _taskQueue = WhiteBoardTaskQueue(handler: _handleDrawTask);
  51. @override
  52. void initState() {
  53. super.initState();
  54. widget.onReceiveData.addListener((sender, data) {
  55. _onReceiveDrawData(data);
  56. });
  57. widget.onClearCanavs.addListener((sender, userId) {
  58. _clearUserLines(userId);
  59. });
  60. /// 加载完后一帧绘制初始数据
  61. WidgetsBinding.instance.addPostFrameCallback((_) {
  62. _paintInitData();
  63. });
  64. }
  65. @override
  66. Widget build(BuildContext context) {
  67. return LayoutBuilder(builder: (_, constraints) {
  68. canvasWidth = constraints.maxWidth;
  69. canvasHeight = constraints.maxHeight;
  70. final canvasSize = Size(canvasWidth, canvasHeight);
  71. return MouseRegion(
  72. cursor: SystemMouseCursors.click,
  73. child: GestureDetector(
  74. onPanDown: _onPanDown,
  75. onPanUpdate: _onPanUpdate,
  76. onPanEnd: _onPanEnd,
  77. onPanCancel: _onPanCancel,
  78. child: Stack(
  79. children: [
  80. CustomPaint(
  81. size: canvasSize,
  82. painter: WhiteBoardPainter(
  83. pen: historicalBrush,
  84. width: canvasWidth,
  85. height: canvasHeight,
  86. ),
  87. ),
  88. CustomPaint(
  89. size: canvasSize,
  90. painter: WhiteBoardPainter(
  91. pen: myPen,
  92. width: canvasWidth,
  93. height: canvasHeight,
  94. ),
  95. ),
  96. ],
  97. )));
  98. });
  99. }
  100. /// 按下时,生成新的线实体
  101. void _onPanDown(DragDownDetails details) {
  102. Line line = Line(
  103. color: myPenColor,
  104. strokeWidth: strokeWidth,
  105. paintType: widget.paintType,
  106. userId: widget.userId,
  107. );
  108. myPen.pushLine(line);
  109. }
  110. /// 拖动时,生成新的点实体,加入到当前线实体中
  111. void _onPanUpdate(DragUpdateDetails details) {
  112. myPen.pushPoint(Point.fromOffset(
  113. details.localPosition,
  114. canvasWidth,
  115. canvasHeight,
  116. ));
  117. }
  118. /// 抬起时,发送数据
  119. void _onPanEnd(DragEndDetails details) {
  120. _sentDrawAction();
  121. myPen.doneLine();
  122. myPen.clear();
  123. }
  124. /// 绘制初始数据
  125. void _paintInitData() {
  126. for (int i = 0; i < widget.initData.length; i++) {
  127. _onReceiveDrawDataImmediately(widget.initData[i]);
  128. }
  129. }
  130. /// 发送绘制指令
  131. void _sentDrawAction() {
  132. List<List<double>> pointsList = [];
  133. for (int i = 0; i < myPen.activeLine.points.length; i++) {
  134. pointsList.add(myPen.activeLine.points[i].toList());
  135. }
  136. String myPenLine = jsonEncode({
  137. "u_Id": widget.userId, // 用户id
  138. "l_Id": "", // 线id
  139. "color": widget.patientColor.value.toString(), // 线的颜色
  140. "points": PointsUtil.compressPointsList(pointsList),
  141. });
  142. widget.sendData(myPenLine);
  143. /// 将自己画的线也加入队列
  144. _onReceiveDrawData(myPenLine);
  145. }
  146. Future<void> _handleDrawTask(Map<String, dynamic> data) async {
  147. /// 清除时走的逻辑
  148. if (data["isClear"] == "true") {
  149. _clearUserLines(data["u_Id"]);
  150. return;
  151. }
  152. List<dynamic> pointsList = PointsUtil.decompressPointsList(data["points"]);
  153. Line line = Line(
  154. color: Color(int.parse(data["color"])),
  155. strokeWidth: strokeWidth,
  156. userId: data["u_Id"],
  157. );
  158. historicalBrush.pushLine(line);
  159. for (int i = 0; i < pointsList.length; i++) {
  160. /// 非自己画的线加动画
  161. if (data["u_Id"] != widget.userId) {
  162. await Future.delayed(const Duration(milliseconds: 2));
  163. }
  164. historicalBrush.pushPoint(Point.fromList(
  165. pointsList[i],
  166. ));
  167. }
  168. historicalBrush.doneLine();
  169. }
  170. /// 接收绘制数据【如果存在数据正在绘制,其他的加入绘制队列,等待当前绘制完成继续绘制】
  171. void _onReceiveDrawData(String jsonData) async {
  172. _taskQueue.add(jsonData);
  173. }
  174. /// 接收绘制数据立即绘制,无 Future.delayed
  175. void _onReceiveDrawDataImmediately(String jsonData) {
  176. var data = jsonDecode(jsonData);
  177. List<dynamic> pointsList = PointsUtil.decompressPointsList(data["points"]);
  178. Line line = Line(
  179. color: Color(int.parse(data["color"])),
  180. strokeWidth: strokeWidth,
  181. userId: data["u_Id"],
  182. );
  183. historicalBrush.pushLine(line);
  184. for (int i = 0; i < pointsList.length; i++) {
  185. historicalBrush.pushPoint(Point.fromList(
  186. pointsList[i],
  187. ));
  188. }
  189. historicalBrush.doneLine();
  190. }
  191. void _clearUserLines(String userId) {
  192. myPen.clearCurrectUserLines(userId);
  193. historicalBrush.clearCurrectUserLines(userId);
  194. }
  195. void _onPanCancel() {
  196. myPen.removeEmpty();
  197. }
  198. }