mind_painter.dart 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. import 'package:flutter/material.dart';
  2. import 'dart:ui' as ui;
  3. import '../../common/models/mindmap/mind_node.dart';
  4. import '../../common/utils/o2_api_manager.dart';
  5. import 'theme/mind_map_theme.dart';
  6. class SelectNode {
  7. final NodePaintElement node;
  8. final RRectPaintElement selectRect;
  9. SelectNode(this.node, this.selectRect);
  10. }
  11. ///
  12. /// 网络图片异步加载
  13. ///
  14. typedef RefreshImage = Function(String, String);
  15. class MindMapPainter extends CustomPainter {
  16. final LinePaintElement? linePaintElement;
  17. final NodePaintElement root ;
  18. final RRectPaintElement? selectRect;
  19. final Map<String, ui.Image> mindMapImages;
  20. final Map<int, ui.Image> priorityImages;
  21. final Map<int, ui.Image> progressImages;
  22. final ui.Image? linkIconImage;
  23. ui.Paint myPaint = ui.Paint();
  24. MindMapPainter({
  25. required this.root,
  26. required this.linePaintElement,
  27. required this.selectRect,
  28. required this.priorityImages,
  29. required this.progressImages,
  30. required this.linkIconImage,
  31. required this.mindMapImages
  32. });
  33. @override
  34. void paint(Canvas canvas, Size size) {
  35. paintNodeInner(canvas, root);
  36. paintLines(canvas);
  37. if (selectRect != null) {
  38. myPaint.style = selectRect!.style.style;
  39. myPaint.color = selectRect!.style.color;
  40. myPaint.strokeWidth = selectRect!.style.strokeWidth;
  41. canvas.drawRRect(selectRect!.rrect, myPaint);
  42. }
  43. }
  44. ///
  45. /// 画节点内部元素
  46. ///
  47. void paintNodeInner(Canvas canvas, NodePaintElement node) {
  48. final elements = node.paintElements;
  49. // 背景
  50. if(elements.containsKey(NodeElement.background)) {
  51. RRectPaintElement? back = elements[NodeElement.background] as RRectPaintElement?;
  52. if (back != null) {
  53. myPaint.style = back.style.style;
  54. myPaint.color = back.style.color;
  55. canvas.drawRRect(back.rrect, myPaint);
  56. }
  57. }
  58. if(elements.containsKey(NodeElement.border)) {
  59. RRectPaintElement? border = elements[NodeElement.border] as RRectPaintElement?;
  60. if (border != null) {
  61. myPaint.style = border.style.style;
  62. myPaint.color = border.style.color;
  63. myPaint.strokeWidth = border.style.strokeWidth;
  64. canvas.drawRRect(border.rrect, myPaint);
  65. }
  66. }
  67. // 画图片
  68. if(elements.containsKey(NodeElement.image)) {
  69. ImagePaintElement? imagePaint = elements[NodeElement.image] as ImagePaintElement?;
  70. if (imagePaint != null) {
  71. var url = node.data.image;
  72. if (node.data.imageId != null && node.data.imageId != 'null' && node.data.imageId?.isNotEmpty == true) {
  73. url = O2ApiManager.instance.getFileURL(node.data.imageId!);
  74. }
  75. if(url != null && url.isNotEmpty && mindMapImages.containsKey(url)) {
  76. final image = mindMapImages[url];
  77. myPaint.style = imagePaint .style.style;
  78. myPaint.color = imagePaint.style.color;
  79. // print('paint image ,url:$url');
  80. canvas.drawImageRect(
  81. image!,
  82. Rect.fromLTWH(0.0, 0.0, image.width.toDouble(), image.height.toDouble()),
  83. imagePaint.rect,
  84. myPaint);
  85. }
  86. }
  87. }
  88. // 优先级
  89. if(elements.containsKey(NodeElement.priority)) {
  90. ImagePaintElement? image = elements[NodeElement.priority] as ImagePaintElement?;
  91. if (image != null) {
  92. myPaint.style = image.style.style;
  93. myPaint.color = image.style.color;
  94. var iconImage = priorityImages[node.data.priority];
  95. canvas.drawImageRect(
  96. iconImage!,
  97. Rect.fromLTWH(0.0, 0.0, iconImage.width.toDouble(), iconImage.height.toDouble()),
  98. image.rect,
  99. myPaint);
  100. }
  101. }
  102. // 进度
  103. if(elements.containsKey(NodeElement.progress)) {
  104. ImagePaintElement? image = elements[NodeElement.progress] as ImagePaintElement?;
  105. if (image != null) {
  106. myPaint.style = image.style.style;
  107. myPaint.color = image.style.color;
  108. var iconImage = progressImages[node.data.progress];
  109. canvas.drawImageRect(
  110. iconImage!,
  111. Rect.fromLTWH(0.0, 0.0, iconImage.width.toDouble(), iconImage.height.toDouble()),
  112. image.rect,
  113. myPaint);
  114. }
  115. }
  116. // 文字
  117. TextPaintElement? text = elements[NodeElement.text] as TextPaintElement?;
  118. if (text != null) {
  119. text.painter.paint(canvas, text.offset!);
  120. }
  121. // 超链接
  122. if(elements.containsKey(NodeElement.hyperlink)) {
  123. ImagePaintElement? image = elements[NodeElement.hyperlink] as ImagePaintElement?;
  124. if (image != null && linkIconImage != null) {
  125. myPaint.style = image.style.style;
  126. myPaint.color = image.style.color;
  127. canvas.drawImageRect(
  128. linkIconImage!,
  129. Rect.fromLTWH(0.0, 0.0, linkIconImage!.width.toDouble(), linkIconImage!.height.toDouble()),
  130. image.rect,
  131. myPaint);
  132. }
  133. }
  134. if(node.children!=null && node.children?.isNotEmpty == true) {
  135. for(var i=0;i<node.children!.length;i++) {
  136. paintNodeInner(canvas, node.children![i]);
  137. }
  138. }
  139. }
  140. ///
  141. /// 画连接线
  142. ///
  143. void paintLines(Canvas canvas) {
  144. final lines = linePaintElement?.lines;
  145. if(lines !=null && lines.isNotEmpty) {
  146. myPaint.style = linePaintElement!.style.style;
  147. myPaint.strokeWidth = linePaintElement!.style.strokeWidth;
  148. myPaint.color = linePaintElement!.style.color;
  149. for (var line in lines) {
  150. // final bezierControlX = line.start.dx < line.end.dx ? line.start.dx + (line.end.dx - line.start.dx) / 3: line.start.dx - (line.start.dx - line.end.dx) / 3;
  151. // final bezierControlY = line.end.dy;
  152. final bifurcationX = line.start.dx < line.end.dx ? (line.end.dx - line.start.dx) / 2 : (line.start.dx - line.end.dx) / 2;
  153. final bifurcationPoint = line.start.dx < line.end.dx ? Offset(line.start.dx + bifurcationX, line.start.dy) : Offset(line.start.dx - bifurcationX, line.start.dy);
  154. final turnPoint = Offset(bifurcationPoint.dx, line.end.dy);
  155. // final rect = Rect.fromLTWH(turnPoint.dx, turnPoint.dy, 8, 8);
  156. // final startAngle = pi;
  157. // final sweepAngle = bifurcationPoint.dy > turnPoint.dy ? 0.5 * pi : -0.5 * pi;
  158. // final Path path = Path()
  159. // ..moveTo(line.start.dx, line.start.dy)
  160. // ..lineTo(bifurcationPoint.dx, bifurcationPoint.dy)
  161. // ..lineTo(turnPoint.dx, turnPoint.dy)
  162. // ..addArc(rect, startAngle, sweepAngle)
  163. // ..lineTo(line.end.dx, line.end.dy);
  164. //// ..quadraticBezierTo(bezierControlX, bezierControlY, line.end.dx, line.end.dy);
  165. // canvas.drawPath(path, myPaint);
  166. canvas.drawLine(line.start, bifurcationPoint, myPaint);
  167. canvas.drawLine(bifurcationPoint, turnPoint, myPaint);
  168. canvas.drawLine(turnPoint, line.end, myPaint);
  169. }
  170. }
  171. }
  172. @override
  173. bool shouldRepaint(CustomPainter oldDelegate) {
  174. // if(oldDelegate is MindMapPainter) {
  175. // // todo 这里有问题 每次oldDelegate里面的数据都没有变化
  176. // final thisMap = json.encode(this.root.toJson());
  177. // final oldMap = json.encode(oldDelegate.root.toJson());
  178. // final check = checkIsImageCacheChanged(oldDelegate.mindMapImages);
  179. // print('check:$check , size:${this.mindMapImages.length} , old:${oldDelegate.mindMapImages.length}');
  180. // if(thisMap == oldMap
  181. // && this.selectRect == oldDelegate.selectRect
  182. // && !check
  183. // && !checkLinesChanged(oldDelegate.linePaintElement)) {
  184. // return false;
  185. // }
  186. // }
  187. return this != oldDelegate;
  188. }
  189. ///
  190. /// 图片资源是否有变化
  191. ///
  192. bool checkIsImageCacheChanged(Map<String, ui.Image> oldMap) {
  193. if (mindMapImages.length != oldMap.length) {
  194. return true;
  195. }
  196. for(var url in mindMapImages.keys) {
  197. bool isInOldMap = false;
  198. for(var oldUrl in oldMap.keys) {
  199. if (url == oldUrl) {
  200. isInOldMap = true;
  201. }
  202. }
  203. if (!isInOldMap) {
  204. return true;
  205. }
  206. }
  207. return false;
  208. }
  209. bool checkLinesChanged(LinePaintElement oldlinePaint) {
  210. if(linePaintElement?.lines.length != oldlinePaint.lines.length) {
  211. return true;
  212. }
  213. if(linePaintElement?.style.style != oldlinePaint.style.style
  214. || linePaintElement?.style.color != oldlinePaint.style.color
  215. || linePaintElement?.style.strokeWidth != oldlinePaint.style.strokeWidth) {
  216. return true;
  217. }
  218. var list = linePaintElement?.lines ?? [];
  219. var oldlist = oldlinePaint.lines;
  220. for(var line in list) {
  221. bool isInOld = false;
  222. for(var oldline in oldlist) {
  223. if(line.start == oldline.start && line.end == oldline.end) {
  224. isInOld = true;
  225. }
  226. }
  227. if(!isInOld) {
  228. return true;
  229. }
  230. }
  231. return false;
  232. }
  233. }