layer_map.dart 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. import 'dart:math';
  2. import 'package:bubble/bubble.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:get/get.dart';
  5. import 'package:trackoffical_app/screen.dart';
  6. import 'package:trackoffical_app/service/game/game_model.dart';
  7. import 'package:trackoffical_app/view/ingame/widget_ruler.dart';
  8. import 'package:trackoffical_app/widget/compass2.dart';
  9. import 'dart:ui' as ui;
  10. import '../../model/game_map.dart';
  11. import '../../model/m_control_point.dart';
  12. import '../../service/game/game.dart';
  13. import 'in_game_controller.dart';
  14. const cpRadius = 15.0;
  15. class LayerMap extends StatelessWidget {
  16. const LayerMap({super.key});
  17. @override
  18. Widget build(BuildContext context) {
  19. return Stack(
  20. children: [
  21. _LayerMapRotateStatic(),
  22. _Trajectory(),
  23. _LayerMapRuler(),
  24. _LayerMapFrontDraw(),
  25. _LayerCPNum(),
  26. _LayerCPStartBubble(),
  27. _LayerCompass(),
  28. ],
  29. );
  30. }
  31. }
  32. /// 地图图层,所有元素静态跟随整体旋转
  33. class _LayerMapRotateStatic extends GetView<GameModel> {
  34. @override
  35. Widget build(BuildContext context) {
  36. return Obx(() {
  37. final model = controller;
  38. // model.mapRotateCenter = Offset(context.width / 2, context.height / 2);
  39. var tran = model.mapTransformMatrix.value;
  40. return Center(
  41. child: Transform(
  42. transform: tran,
  43. // alignment: Alignment.center,
  44. child: SizedBox(
  45. height: context.height,
  46. width: context.width,
  47. child: Stack(
  48. children: [
  49. _StaticMap(),
  50. _RoutePoints(tran, model.startAt == null),
  51. ],
  52. )),
  53. ),
  54. );
  55. });
  56. }
  57. }
  58. class _RoutePoints extends StatefulWidget {
  59. const _RoutePoints(this.tran, this.isOnlyStart);
  60. final Matrix4 tran;
  61. final bool isOnlyStart;
  62. @override
  63. State<StatefulWidget> createState() {
  64. return _RoutePointsState();
  65. }
  66. }
  67. class _RoutePointsState extends State<_RoutePoints>
  68. with SingleTickerProviderStateMixin {
  69. final model = Get.find<GameModel>();
  70. final controller = Get.find<GameService>();
  71. late AnimationController _animationController;
  72. late Animation<int?> _animation;
  73. @override
  74. void initState() {
  75. super.initState();
  76. _animationController = AnimationController(
  77. vsync: this,
  78. duration: const Duration(milliseconds: 500),
  79. reverseDuration: const Duration(milliseconds: 500),
  80. )..repeat(reverse: true);
  81. _animation = IntTween(begin: 255, end: 0).animate(_animationController)
  82. ..addListener(() {
  83. setState(() {
  84. // The state that has changed here is the animation object’s value.
  85. });
  86. });
  87. }
  88. @override
  89. Widget build(BuildContext context) {
  90. final radiusPx = controller.mapStatus.mapScaleSrc * cpRadius;
  91. final info = controller.mapStatus.gameMapData;
  92. final matrix = widget.tran;
  93. return Obx(() {
  94. var points = controller.controlPointWantSequence;
  95. return SizedBox(
  96. height: context.height,
  97. width: context.width,
  98. child: CustomPaint(
  99. painter: _RoutePointsPainter(
  100. points,
  101. info,
  102. matrix,
  103. radiusPx,
  104. _animation.value ?? 255,
  105. widget.isOnlyStart,
  106. model.nextPlanPointIndex.value,
  107. ),
  108. ),
  109. );
  110. });
  111. }
  112. @override
  113. void dispose() {
  114. _animationController.dispose();
  115. super.dispose();
  116. }
  117. }
  118. class _RoutePointsPainter extends CustomPainter {
  119. final List<MControlPoint> _points;
  120. final GameMap info;
  121. final Matrix4 matrix;
  122. final service = GameService.to;
  123. final double radiusPx;
  124. final int nextAlpha;
  125. static const nextColor = Colors.red;
  126. final bool isOnlyStart;
  127. final int? nextPlanIndex;
  128. _RoutePointsPainter(this._points, this.info, this.matrix, this.radiusPx,
  129. this.nextAlpha, this.isOnlyStart, this.nextPlanIndex);
  130. @override
  131. void paint(Canvas canvas, Size size) {
  132. final paint = Paint();
  133. final strokeWidthColor = radiusPx * 0.15;
  134. final strokeWidthWhite = strokeWidthColor * 2;
  135. paint.strokeWidth = strokeWidthColor;
  136. final picFirstScale = service.mapStatus.picFirstScale;
  137. // final textSize = radiusPx * 0.6;
  138. final textSize = -22.0 / matrix[0];
  139. // const textSize = 5.0;
  140. const isShowText = false;
  141. var lastXY = Offset.zero;
  142. for (var i = _points.length - 1; i >= 0; i--) {
  143. if (isOnlyStart && i != 0) {
  144. continue;
  145. }
  146. final one = _points[i];
  147. if (one.onMap != Offset.zero) {
  148. // var xy = service.mapStatus.picOffsetToScreen(one.onMap);
  149. var xy =
  150. Offset(one.onMap.dx * picFirstScale, one.onMap.dy * picFirstScale);
  151. var x = xy.dx;
  152. var y = xy.dy;
  153. var isNext = false;
  154. var isJumped = false;
  155. if (nextPlanIndex == null) {
  156. isNext = one.isNext;
  157. } else {
  158. isNext = i == nextPlanIndex;
  159. isJumped = !one.isSuccess && i < nextPlanIndex!;
  160. }
  161. var color = const Color(0xffff00f7);
  162. var strokeColor = Colors.white;
  163. if (one.isSuccess) {
  164. color = Colors.green;
  165. }
  166. if (isNext) {
  167. color = nextColor.withAlpha(nextAlpha);
  168. strokeColor = Colors.white.withAlpha(nextAlpha);
  169. // paint.color = const Color(0xffff4d00);
  170. }
  171. if (isJumped) {
  172. color = Colors.grey;
  173. }
  174. paint.style = PaintingStyle.fill;
  175. final thisXY = Offset(x, y);
  176. // canvas.drawCircle(thisXY, 2, paint);
  177. // if (lastXY != Offset.zero) {
  178. if (i - 1 >= 0) {
  179. final next = _points[i - 1];
  180. lastXY = Offset(
  181. next.onMap.dx * picFirstScale, next.onMap.dy * picFirstScale);
  182. final dx = thisXY.dx - lastXY.dx;
  183. final dy = thisXY.dy - lastXY.dy;
  184. final len = sqrt(dx * dx + dy * dy);
  185. final dx1 = dx / len * radiusPx;
  186. final dy1 = dy / len * radiusPx;
  187. var p1 = lastXY + Offset(dx1, dy1);
  188. var p2 = thisXY - Offset(dx1, dy1);
  189. if (p1.dx.isNaN || p1.dy.isNaN || p2.dx.isNaN || p2.dy.isNaN) {
  190. continue;
  191. }
  192. paint.color = strokeColor;
  193. paint.strokeWidth = strokeWidthWhite;
  194. canvas.drawLine(p1, p2, paint);
  195. paint.color = color;
  196. paint.strokeWidth = strokeWidthColor;
  197. canvas.drawLine(p1, p2, paint);
  198. }
  199. lastXY = thisXY;
  200. if (one.isStart) {
  201. final path = Path();
  202. final widthLeft = radiusPx * 0.9;
  203. final widthRight = radiusPx * 1.1;
  204. final heightHalf = radiusPx;
  205. path.moveTo(x - widthLeft, y - heightHalf);
  206. path.lineTo(x + widthRight, y);
  207. path.lineTo(x - widthLeft, y + heightHalf);
  208. path.close();
  209. paint.style = PaintingStyle.stroke;
  210. paint.color = strokeColor;
  211. paint.strokeWidth = strokeWidthWhite;
  212. canvas.drawPath(path, paint);
  213. paint.color = color;
  214. paint.strokeWidth = strokeWidthColor;
  215. canvas.drawPath(path, paint);
  216. } else if (one.isFinish) {
  217. paint.style = PaintingStyle.stroke;
  218. paint.color = strokeColor;
  219. paint.strokeWidth = strokeWidthWhite;
  220. canvas.drawCircle(thisXY, radiusPx, paint);
  221. canvas.drawCircle(thisXY, radiusPx * 0.7, paint);
  222. paint.color = color;
  223. paint.strokeWidth = strokeWidthColor;
  224. canvas.drawCircle(thisXY, radiusPx, paint);
  225. canvas.drawCircle(thisXY, radiusPx * 0.7, paint);
  226. } else {
  227. paint.style = PaintingStyle.stroke;
  228. paint.color = strokeColor;
  229. paint.strokeWidth = strokeWidthWhite;
  230. canvas.drawCircle(thisXY, radiusPx, paint);
  231. paint.color = color;
  232. paint.strokeWidth = strokeWidthColor;
  233. canvas.drawCircle(thisXY, radiusPx, paint);
  234. }
  235. if (!one.isStart && !one.isFinish && isShowText) {
  236. var textPainter = TextPainter(
  237. text: TextSpan(
  238. text: one.sn,
  239. style: TextStyle(
  240. color: paint.color,
  241. fontSize: textSize,
  242. fontWeight: FontWeight.w700)),
  243. textDirection: TextDirection.rtl,
  244. textWidthBasis: TextWidthBasis.longestLine,
  245. maxLines: 1,
  246. )..layout();
  247. textPainter.paint(
  248. canvas,
  249. Offset(x - textPainter.width / 2,
  250. y - textPainter.height - radiusPx));
  251. }
  252. }
  253. }
  254. }
  255. @override
  256. bool shouldRepaint(_RoutePointsPainter oldDelegate) => false;
  257. }
  258. class _LayerMapFrontDraw extends GetView<GameService> {
  259. final model = Get.find<GameModel>();
  260. @override
  261. Widget build(BuildContext context) {
  262. return Obx(() {
  263. var points = controller.controlPointWantSequence;
  264. final info = controller.mapStatus.gameMapData;
  265. final matrix = controller.mapStatus.matrix.value;
  266. var showLocationAlpha = 255;
  267. if (controller.isOutBoundary || model.isEnableUserLocation) {
  268. showLocationAlpha = 255;
  269. }
  270. if (!model.isEnableUserLocation) {
  271. showLocationAlpha = 0;
  272. }
  273. return SizedBox(
  274. height: context.height,
  275. width: context.width,
  276. child: CustomPaint(
  277. painter: _LayerMapFrontDrawPainter(
  278. points,
  279. info,
  280. matrix,
  281. controller.positionOnMap ?? Offset.zero,
  282. controller.mapStatus.compassAngle,
  283. controller.mapStatus.rotateRadian,
  284. showLocationAlpha),
  285. ),
  286. );
  287. });
  288. }
  289. }
  290. class _LayerMapFrontDrawPainter extends CustomPainter {
  291. final List<MControlPoint> _points;
  292. final GameMap info;
  293. final Matrix4 matrix;
  294. final Offset myPositionOnMap;
  295. final service = GameService.to;
  296. final double northRadians;
  297. final double mapRadians;
  298. final int showLocationAlpha;
  299. _LayerMapFrontDrawPainter(
  300. this._points,
  301. this.info,
  302. this.matrix,
  303. this.myPositionOnMap,
  304. this.northRadians,
  305. this.mapRadians,
  306. this.showLocationAlpha);
  307. @override
  308. void paint(Canvas canvas, Size size) {
  309. final paint = Paint();
  310. drawMyLocation(canvas, paint);
  311. // drawRouteNumber(canvas, paint);
  312. }
  313. @override
  314. bool shouldRepaint(_LayerMapFrontDrawPainter oldDelegate) => false;
  315. drawMyLocation(Canvas canvas, Paint paint) {
  316. var myXY = service.mapStatus.picOffsetToScreen(myPositionOnMap);
  317. // final myXY = myPositionOnMap;
  318. final color = (const Color(0xFFFF0000)).withAlpha(showLocationAlpha);
  319. final color2 = (const Color(0xFFFFA1A1)).withAlpha(showLocationAlpha);
  320. paint.color = color;
  321. var rect = Rect.fromLTRB(myXY.dx - 12, myXY.dy - 40, myXY.dx + 12, myXY.dy);
  322. paint.shader = ui.Gradient.linear(rect.topCenter, rect.bottomCenter, [
  323. const Color(0x00FFFFFF),
  324. color2
  325. ], [
  326. 0,
  327. 1,
  328. ]);
  329. final path = Path();
  330. path.moveTo(rect.topLeft.dx, rect.topLeft.dy);
  331. path.lineTo(rect.topRight.dx, rect.topRight.dy);
  332. path.lineTo(rect.bottomCenter.dx, rect.bottomCenter.dy);
  333. canvas.drawPath(path, paint);
  334. paint.shader = null;
  335. paint.color = Colors.white.withAlpha(showLocationAlpha);
  336. canvas.drawCircle(myXY, 8, paint);
  337. paint.color = color;
  338. canvas.drawCircle(myXY, 5, paint);
  339. }
  340. drawRouteNumber(Canvas canvas, Paint paint) {
  341. var h0 = service.mapStatus.picOffsetToScreen(const Offset(0, 0));
  342. var h1 = service.mapStatus.picOffsetToScreen(const Offset(0, 60));
  343. final dx = h1.dx - h0.dx;
  344. final dy = h1.dy - h0.dy;
  345. var h = sqrt(dx * dx + dy * dy);
  346. for (var one in _points) {
  347. if (one.onMap != Offset.zero) {
  348. var xy = service.mapStatus.picOffsetToScreen(one.onMap);
  349. var x = xy.dx;
  350. var y = xy.dy;
  351. if (!one.isStart && !one.isFinish) {
  352. var textPainter = TextPainter(
  353. text: TextSpan(
  354. text: one.sn,
  355. style: const TextStyle(
  356. color: Colors.white,
  357. fontSize: 27,
  358. fontWeight: FontWeight.w900,
  359. letterSpacing: -3)),
  360. textDirection: TextDirection.rtl,
  361. textWidthBasis: TextWidthBasis.longestLine,
  362. maxLines: 1,
  363. )..layout();
  364. // textPainter.paint(canvas, Offset(x - textPainter.width / 2 -1 , y - textPainter.height -h + 3));
  365. textPainter = TextPainter(
  366. text: TextSpan(
  367. text: one.sn,
  368. style: const TextStyle(
  369. color: Color(0xffff00f7),
  370. fontSize: 22,
  371. fontWeight: FontWeight.w700)),
  372. textDirection: TextDirection.rtl,
  373. textWidthBasis: TextWidthBasis.longestLine,
  374. maxLines: 1,
  375. )..layout();
  376. textPainter.paint(canvas,
  377. Offset(x - textPainter.width / 2, y - textPainter.height - h));
  378. }
  379. }
  380. }
  381. }
  382. drawRoutes(Canvas canvas, Paint paint) {
  383. const radiusPx = 19.0;
  384. paint.strokeWidth = 2.18;
  385. var lastXY = Offset.zero;
  386. for (var one in _points) {
  387. if (one.onMap != Offset.zero) {
  388. var xy = service.mapStatus.picOffsetToScreen(one.onMap);
  389. var x = xy.dx;
  390. var y = xy.dy;
  391. paint.color = const Color(0xffff00f7);
  392. if (one.isSuccess) {
  393. paint.color = Colors.green;
  394. }
  395. if (one.isNext) {
  396. paint.color = const Color(0xffffcb00);
  397. }
  398. paint.style = PaintingStyle.fill;
  399. final thisXY = Offset(x, y);
  400. // canvas.drawCircle(thisXY, 2, paint);
  401. if (lastXY != Offset.zero) {
  402. final dx = thisXY.dx - lastXY.dx;
  403. final dy = thisXY.dy - lastXY.dy;
  404. final len = sqrt(dx * dx + dy * dy);
  405. final dx1 = dx / len * radiusPx;
  406. final dy1 = dy / len * radiusPx;
  407. var p1 = lastXY + Offset(dx1, dy1);
  408. var p2 = thisXY - Offset(dx1, dy1);
  409. if (p1.dx.isNaN || p1.dy.isNaN || p2.dx.isNaN || p2.dy.isNaN) {
  410. continue;
  411. }
  412. canvas.drawLine(p1, p2, paint);
  413. }
  414. lastXY = thisXY;
  415. if (one.isStart) {
  416. paint.style = PaintingStyle.stroke;
  417. final path = Path();
  418. const widthLeft = 12;
  419. const widthRight = 18;
  420. const heightHalf = 17;
  421. path.moveTo(x - widthLeft, y - heightHalf);
  422. path.lineTo(x + widthRight, y);
  423. path.lineTo(x - widthLeft, y + heightHalf);
  424. path.close();
  425. canvas.drawPath(path, paint);
  426. } else if (one.isFinish) {
  427. paint.style = PaintingStyle.stroke;
  428. canvas.drawCircle(thisXY, radiusPx, paint);
  429. canvas.drawCircle(thisXY, 13.0, paint);
  430. } else {
  431. paint.style = PaintingStyle.stroke;
  432. canvas.drawCircle(thisXY, radiusPx, paint);
  433. }
  434. if (!one.isStart && !one.isFinish) {
  435. var textPainter = TextPainter(
  436. text: TextSpan(
  437. text: one.sn,
  438. style: TextStyle(color: paint.color, fontSize: 14)),
  439. textDirection: TextDirection.rtl,
  440. textWidthBasis: TextWidthBasis.longestLine,
  441. maxLines: 1,
  442. )..layout();
  443. textPainter.paint(canvas, Offset(x - textPainter.width / 2, y + 20));
  444. }
  445. }
  446. }
  447. }
  448. }
  449. class _LayerCPStartBubble extends GetView<GameService> {
  450. final service = GameService.to;
  451. @override
  452. Widget build(BuildContext context) {
  453. return Obx(() {
  454. var points = controller.controlPointWantSequence;
  455. if (service.startAt != null || points.isEmpty) {
  456. return const SizedBox();
  457. }
  458. final start = points[0];
  459. var xy = service.mapStatus
  460. .picOffsetToScreen(Offset(start.onMap.dx, start.onMap.dy));
  461. var x = xy.dx;
  462. var y = xy.dy;
  463. var h0 = service.mapStatus.picOffsetToScreen(const Offset(0, 0));
  464. var h1 = service.mapStatus.picOffsetToScreen(const Offset(0, 60));
  465. final dx = h1.dx - h0.dx;
  466. final dy = h1.dy - h0.dy;
  467. var h = sqrt(dx * dx + dy * dy);
  468. return Positioned(
  469. top: y,
  470. left: x + h + 1.0.wp,
  471. child: Bubble(
  472. nip: BubbleNip.leftTop,
  473. alignment: Alignment.topRight,
  474. child: const Text('打开始点后计时', style: TextStyle(color: Colors.red)),
  475. ),
  476. );
  477. });
  478. }
  479. }
  480. class _LayerCPNum extends GetView<GameService> {
  481. final model = Get.find<GameModel>();
  482. final service = GameService.to;
  483. static const style = TextStyle(
  484. color: Color(0xffff00f7), fontSize: 22, fontWeight: FontWeight.w700);
  485. final ingame = Get.find<InGameController>();
  486. @override
  487. Widget build(BuildContext context) {
  488. final radiusPx = controller.mapStatus.mapScaleSrc * cpRadius;
  489. return Obx(() {
  490. if (service.startAt == null) {
  491. return const SizedBox();
  492. }
  493. final scale = service.mapStatus.matrix.value[0].abs();
  494. var h0 = service.mapStatus.picOffsetToScreen(const Offset(0, 0));
  495. var h1 = service.mapStatus.picOffsetToScreen(Offset(0, radiusPx));
  496. final dx = h1.dx - h0.dx;
  497. final dy = h1.dy - h0.dy;
  498. var h = sqrt(dx * dx + dy * dy);
  499. final children = <Widget>[];
  500. h = radiusPx*scale*2;
  501. h = controller.mapStatus.mapScale.value * cpRadius + 4;
  502. var points = controller.controlPointWantSequence;
  503. for (var one in points) {
  504. if (one.onMap != Offset.zero) {
  505. var xy = service.mapStatus.picOffsetToScreen(one.onMap);
  506. var x = xy.dx;
  507. var y = xy.dy;
  508. if (!one.isStart && !one.isFinish) {
  509. final height = h + 24;
  510. children.add(Positioned(
  511. top: y - height,
  512. left: x - 40,
  513. width: 80,
  514. height: height,
  515. child: Transform.rotate(
  516. angle: model.compassPlantRadian,
  517. alignment: Alignment.bottomCenter,
  518. child: Container(
  519. alignment: Alignment.topCenter,
  520. // color: Colors.white,
  521. child: Text(one.sn, style: style))),
  522. ));
  523. }
  524. }
  525. }
  526. return Stack(
  527. children: children,
  528. );
  529. });
  530. }
  531. }
  532. class _StaticMap extends GetView<GameService> {
  533. @override
  534. Widget build(BuildContext context) {
  535. return Obx(() {
  536. final data = controller.mapStatus.gameMapData;
  537. final pic = controller.mapStatus.mapImageData.value;
  538. final geoInfo = data.mapPackage;
  539. if (pic.isNotEmpty && geoInfo != null) {
  540. // var fit = BoxFit.fitWidth;
  541. // if(data.width > data.height){
  542. // fit = BoxFit.fitHeight;
  543. // }
  544. return Image.memory(controller.mapStatus.gameMapData.pic!,
  545. fit: BoxFit.contain);
  546. } else {
  547. return Container(
  548. width: context.width,
  549. height: context.height,
  550. decoration: const BoxDecoration(color: Colors.white));
  551. }
  552. });
  553. }
  554. }
  555. class _LayerCompass extends GetView<InGameController> {
  556. @override
  557. Widget build(BuildContext context) {
  558. GameModel model = Get.find();
  559. return Obx(() {
  560. if (controller.isShowCompass) {
  561. final diameter = controller.compassDiameter;
  562. final left = (context.width - diameter) / 2;
  563. return Positioned(
  564. left: left,
  565. top: controller.compassCenter.dy - diameter / 2,
  566. child: Compass2(
  567. compassRadians: model.compassRadiansFused.value,
  568. mapNorthRadians: model.compassPlantRadian,
  569. nextPointRadians: controller.isShowNextCPRadians.value
  570. ? model.compassPlantRadian
  571. : null,
  572. level: controller.compassLevel.value,
  573. showDegrees: model.compassShowDegrees,
  574. diameter: diameter,
  575. ));
  576. } else {
  577. return Container();
  578. }
  579. });
  580. }
  581. }
  582. class _LayerMapRuler extends GetView<InGameController> {
  583. final service = GameService.to;
  584. @override
  585. Widget build(BuildContext context) {
  586. return Obx(() {
  587. if (controller.isShowRuler.value) {
  588. final mapScale = service.mapStatus.mapScale.value;
  589. if (mapScale == 0) {
  590. return const SizedBox();
  591. }
  592. var height = context.height / 2;
  593. var hideHeight = 0.0;
  594. if (controller.isMapRotateAtCompassCenter.value) {
  595. height = controller.compassCenter.dy;
  596. hideHeight = controller.compassDiameter / 2 + 1;
  597. }
  598. return Container(
  599. alignment: Alignment.topCenter,
  600. padding: EdgeInsets.only(top: context.mediaQueryPadding.top),
  601. height: height,
  602. child: Ruler(hideHeight: hideHeight, mapScale: mapScale));
  603. } else {
  604. return const SizedBox();
  605. }
  606. });
  607. }
  608. }
  609. class _Trajectory extends GetView<GameModel> {
  610. @override
  611. Widget build(BuildContext context) {
  612. return Obx(() {
  613. return SizedBox(
  614. height: context.height,
  615. width: context.width,
  616. child: CustomPaint(
  617. painter: _TrajectoryPainter(
  618. controller.trajectoryPoints, controller.isDrawTrajectory),
  619. ),
  620. );
  621. });
  622. }
  623. }
  624. class _TrajectoryPainter extends CustomPainter {
  625. final bool isShow;
  626. final List<Offset> _points;
  627. final painter = Paint()
  628. ..strokeWidth = 3
  629. ..color = const Color(0xFF309EF9)
  630. ..style = PaintingStyle.stroke
  631. ..isAntiAlias = true;
  632. final service = GameService.to;
  633. _TrajectoryPainter(this._points, this.isShow);
  634. @override
  635. void paint(Canvas canvas, Size size) {
  636. if (_points.length > 1 && isShow) {
  637. var alpha = 255;
  638. final lineCount = _points.length - 1;
  639. final alphaStep = (alpha / lineCount).round();
  640. for (var i = 1; i < _points.length; i++) {
  641. final one = _points[i];
  642. final last = _points[i - 1];
  643. final p1 = service.mapStatus.picOffsetToScreen(last);
  644. final p2 = service.mapStatus.picOffsetToScreen(one);
  645. painter.color = Color.fromARGB(alpha, 255, 0, 0);
  646. canvas.drawLine(p1, p2, painter);
  647. alpha -= alphaStep;
  648. if (alpha < 0) {
  649. alpha = 0;
  650. }
  651. }
  652. }
  653. }
  654. @override
  655. bool shouldRepaint(_TrajectoryPainter oldDelegate) => false;
  656. }