white_board.dart 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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.patientColor,
  14. required this.paintType,
  15. required this.sendData,
  16. required this.onReceiveData,
  17. required this.onClearCanavs,
  18. });
  19. /// 初始画板副本数据
  20. final List<String> initData;
  21. /// 用户id
  22. final String userId;
  23. /// 用户颜色
  24. final Color patientColor;
  25. /// 当前绘制模式
  26. final PaintType paintType;
  27. /// 发送数据(接 JsonRPC)
  28. final void Function(String data) sendData;
  29. /// 接收数据(接 WebSocket)
  30. final FEventHandler<String> onReceiveData;
  31. /// 画布清除事件
  32. final FEventHandler<String> onClearCanavs;
  33. @override
  34. State<FWhiteBoard> createState() => _FWhiteBoardState();
  35. }
  36. class _FWhiteBoardState extends State<FWhiteBoard> {
  37. /// 画布尺寸
  38. late double canvasWidth = 0.0;
  39. late double canvasHeight = 0.0;
  40. /// 当前用户的画笔
  41. WhiteBoardPen myPen = WhiteBoardPen();
  42. /// 其他人的画笔
  43. WhiteBoardPen otherPen = WhiteBoardPen();
  44. /// 当前用户的画笔颜色
  45. Color get myPenColor => widget.patientColor;
  46. /// 固定的画笔粗细
  47. static const double strokeWidth = 3.0;
  48. /// 异步绘制队列
  49. List<String> _asyncDrawQueue = [];
  50. /// 异步绘制锁
  51. bool _asyncDrawLock = false;
  52. @override
  53. void initState() {
  54. super.initState();
  55. widget.onReceiveData.addListener((sender, data) {
  56. _onReceiveDrawData(data);
  57. });
  58. widget.onClearCanavs.addListener((sender, userId) {
  59. _clearUserLines(userId);
  60. });
  61. /// 加载完后一帧绘制初始数据
  62. WidgetsBinding.instance.addPostFrameCallback((_) {
  63. _paintInitData();
  64. });
  65. }
  66. @override
  67. Widget build(BuildContext context) {
  68. canvasWidth = MediaQuery.of(context).size.width;
  69. canvasHeight = MediaQuery.of(context).size.height;
  70. return MouseRegion(
  71. cursor: SystemMouseCursors.click,
  72. child: GestureDetector(
  73. onPanDown: _onPanDown,
  74. onPanUpdate: _onPanUpdate,
  75. onPanEnd: _onPanEnd,
  76. onPanCancel: _onPanCancel,
  77. onDoubleTap: myPen.clear,
  78. child: Stack(
  79. children: [
  80. CustomPaint(
  81. size: MediaQuery.of(context).size,
  82. painter: WhiteBoardPainter(
  83. pen: myPen,
  84. width: canvasWidth,
  85. height: canvasHeight,
  86. ),
  87. ),
  88. CustomPaint(
  89. size: MediaQuery.of(context).size,
  90. painter: WhiteBoardPainter(
  91. pen: otherPen,
  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. }
  123. /// 绘制初始数据
  124. void _paintInitData() {
  125. for (int i = 0; i < widget.initData.length; i++) {
  126. _onReceiveDrawDataImmediately(widget.initData[i]);
  127. }
  128. }
  129. /// 发送绘制指令
  130. void _sentDrawAction() {
  131. List<List<double>> pointsList = [];
  132. for (int i = 0; i < myPen.activeLine.points.length; i++) {
  133. pointsList.add(myPen.activeLine.points[i].toList());
  134. }
  135. print(widget.patientColor.value.toString());
  136. widget.sendData(jsonEncode({
  137. "u_Id": widget.userId, // 用户id
  138. "l_Id": "", // 线id
  139. "color": widget.patientColor.value.toString(), // 线的颜色
  140. "points": PointsUtil.compressPointsList(pointsList),
  141. }));
  142. }
  143. /// 接收绘制数据【如果存在数据正在绘制,其他的加入绘制队列,等待当前绘制完成继续绘制】
  144. void _onReceiveDrawData(String jsonData) async {
  145. if (_asyncDrawLock) {
  146. _asyncDrawQueue.add(jsonData);
  147. return;
  148. }
  149. var data = jsonDecode(jsonData);
  150. /// 清除时走的逻辑
  151. if (data["isClear"] == "true") {
  152. _clearUserLines(data["u_Id"]);
  153. return;
  154. }
  155. _asyncDrawLock = true;
  156. List<dynamic> pointsList = PointsUtil.decompressPointsList(data["points"]);
  157. Line line = Line(
  158. color: Color(int.parse(data["color"])),
  159. strokeWidth: strokeWidth,
  160. userId: data["u_Id"],
  161. );
  162. otherPen.pushLine(line);
  163. for (int i = 0; i < pointsList.length; i++) {
  164. otherPen.pushPoint(Point.fromList(
  165. pointsList[i],
  166. ));
  167. await Future.delayed(const Duration(milliseconds: 2));
  168. }
  169. otherPen.doneLine();
  170. _asyncDrawLock = false;
  171. /// 判断如果队列存在数据,继续绘制
  172. if (_asyncDrawQueue.isNotEmpty) {
  173. _onReceiveDrawData(_asyncDrawQueue.removeAt(0));
  174. }
  175. }
  176. /// 接收绘制数据立即绘制,无 Future.delayed
  177. void _onReceiveDrawDataImmediately(String jsonData) {
  178. var data = jsonDecode(jsonData);
  179. List<dynamic> pointsList = PointsUtil.decompressPointsList(data["points"]);
  180. Line line = Line(
  181. color: Color(int.parse(data["color"])),
  182. strokeWidth: strokeWidth,
  183. userId: data["u_Id"],
  184. );
  185. otherPen.pushLine(line);
  186. for (int i = 0; i < pointsList.length; i++) {
  187. otherPen.pushPoint(Point.fromList(
  188. pointsList[i],
  189. ));
  190. }
  191. otherPen.doneLine();
  192. }
  193. void _clearUserLines(String userId) {
  194. myPen.clearCurrectUserLines(userId);
  195. otherPen.clearCurrectUserLines(userId);
  196. }
  197. void _onPanCancel() {
  198. myPen.removeEmpty();
  199. }
  200. }