mind_painter.dart 8.6 KB

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