123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- import 'dart:async';
- import 'dart:io';
- import 'dart:math';
- import 'dart:typed_data';
- import 'dart:ui' as ui;
- import 'package:amap_flutter_base/amap_flutter_base.dart';
- import 'package:amap_flutter_map/amap_flutter_map.dart';
- import 'package:flutter/foundation.dart';
- import 'package:flutter/gestures.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:path_provider/path_provider.dart';
- import 'package:sport/pages/run/location.dart';
- import 'package:sport/pages/run/run_start.dart';
- import 'package:sport/pages/run/setting_page.dart';
- import 'package:sport/utils/path_smooth_tool.dart';
- import 'package:sport/utils/sport_utils.dart';
- import 'package:sport/widgets/dialog/request_dialog.dart';
- Future<File> getFile({String suffix = "png"}) async {
- String sTempDir = (await getTemporaryDirectory()).path;
- bool isDirExist = await Directory(sTempDir).exists();
- if (!isDirExist) {
- Directory(sTempDir).create();
- }
- var now = DateTime.now();
- File file = File(sTempDir + "/poster-temp-${now.millisecondsSinceEpoch}-${Random().nextInt(100)}.$suffix");
- return file;
- }
- Future<File> takeAMapSnapshot(AMapController? mapController) async {
- File file = await getFile();
- if (mapController != null) {
- var mapData = await mapController.takeSnapshot();
- if(mapData != null)
- file.writeAsBytesSync(mapData);
- }
- return file;
- }
- Future<Uint8List?> takeAMapSnapshotData(AMapController? mapController) async {
- if (mapController != null) {
- return await mapController.takeSnapshot();
- }
- return null;
- }
- class MapWidget extends StatefulWidget {
- final double distance;
- final int runMapType;
- final bool runMapKm;
- final List<Location> points;
- final Map<int, Location> pointsKm;
- final bool record;
- final bool anim;
- final int showType;
- const MapWidget({Key? key, this.showType = 0, this.record = false, this.anim = false, this.distance = 0, this.runMapType = 0, this.runMapKm = true, required this.points, required this.pointsKm}) : super(key: key);
- @override
- State<StatefulWidget> createState() => MapState();
- }
- class MapState extends State<MapWidget> with RunSetting, TickerProviderStateMixin {
- AMapController? _mapController;
- final Map<int, Marker> _markerMap = <int, Marker>{};
- final Map<int, Marker> _markerMapReady = <int, Marker>{};
- AnimationController? _animationController;
- Animation? _animation;
- Marker? _marker;
- late double _avgSpeed;
- int _perKm = 0;
- List<Location> polylines = [];
- bool _mapReady = false;
- double _zoom = 15;
- @override
- void initState() {
- super.initState();
- List<Location> points = widget.points;
- _avgSpeed = points.map((e) => e.speed ?? 0).reduce((value, element) => value + element) / points.length;
- runMapType = widget.runMapType;
- loadCustomData();
- }
- @override
- void dispose() {
- _animationController?.dispose();
- _mapController?.disponse();
- _mapController = null;
- super.dispose();
- }
- @override
- void didUpdateWidget(covariant MapWidget oldWidget) {
- super.didUpdateWidget(oldWidget);
- if (oldWidget.runMapKm != widget.runMapKm) {
- _fillMarker(widget.points).then((value) => setState(() {
- _markerMap.addAll(_markerMapReady);
- _markerMap.remove(0);
- }));
- }
- if (oldWidget.runMapType != widget.runMapType) {
- runMapType = widget.runMapType;
- loadCustomData();
- }
- }
- void _onMapCreated(AMapController controller) async {
- _mapController = controller;
- if(widget.anim != true){
- initAnimation();
- }
- }
- initAnimation() async{
- List<Location> points = widget.points;
- await _fillMarker(points);
- if (widget.anim == true) {
- if (widget.showType == 0) {
- _mapController?.moveCamera(CameraUpdate.newCameraPosition(CameraPosition(tilt: 45.0, zoom: 17, bearing: -15, target: LatLng(points.first.latitude, points.first.longitude))), animated: true, duration: 1000);
- } else {
- LatLngBounds? bounds = LatLngBoundsBuilder.fromList(points.map((e) => LatLng(e.latitude, e.longitude)).toList());
- if (bounds == null) return;
- LatLng center = LatLng((bounds.northeast.latitude + bounds.southwest.latitude) / 2, (bounds.northeast.longitude + bounds.southwest.longitude) / 2);
- _mapController?.moveCamera(
- CameraUpdate.newLatLngBounds(bounds, 40.0)
- );
- // _mapController?.moveCamera(
- // CameraUpdate.newCameraPosition(CameraPosition(zoom: _zoom, bearing: -15, target: LatLng(center.latitude, center.longitude))),
- // );
- double zoom = calculateLatLngDistance(bounds.southwest, bounds.northeast) / 2000.0;
- print("22222222222222222222222222222 $_zoom ${15 - zoom}");
- _mapController?.moveCamera(CameraUpdate.newCameraPosition(CameraPosition(tilt: 45.0, zoom: min(_zoom, 15 - zoom) + 1.5, bearing: -15, target: LatLng(center.latitude, center.longitude))), animated: true, duration: 1000);
- }
- // await Future.delayed(Duration(seconds: 1));
- // _mapController?.moveCamera(
- // CameraUpdate.newCameraPosition(CameraPosition(zoom: _zoom, bearing: -270, target: LatLng(center.latitude, center.longitude))),
- // );
- setState(() {
- _markerMap[-1] = _markerMapReady[-1]!;
- });
- // _mapController?.moveCamera(CameraUpdate.newCameraPosition(CameraPosition(tilt: 45.0, zoom: _zoom, bearing: -15, target: LatLng(center.latitude, center.longitude))), animated: true, duration: 1000);
- // Future.delayed(Duration(seconds: 2)).then((value) => controller.moveCamera(CameraUpdate.newLatLngBounds(LatLngBoundsBuilder.fromList(points.map((e) => LatLng(e.latitude, e.longitude)).toList()), 50.0), animated: true, duration: 1000));
- await Future.delayed(Duration(seconds: 2));
- }
- _startAnimation();
- }
- _startAnimation() {
- List<Location> points = widget.points;
- int duration = widget.showType == 1 ? 16000 : widget.distance ~/ 1000 * 4000;
- _finish() {
- LatLngBounds? latLngBounds = LatLngBoundsBuilder.fromList(points.map((e) => LatLng(e.latitude, e.longitude)).toList());
- if (latLngBounds != null) {
- _mapController?.moveCamera(CameraUpdate.newLatLngBounds(latLngBounds, 70.0), animated: true, duration: 1000);
- }
- setState(() {
- polylines.clear();
- polylines.addAll(points);
- if (widget.record == false) _mapReady = true;
- _markerMap.remove(0);
- _markerMap[1000] = _markerMapReady[1000]!;
- });
- // Future.delayed(Duration(seconds: 1)).then((value) =>
- // _mapController?.moveCamera(CameraUpdate.scrollBy(0, 100), animated: true),);
- MapNotification(this.distance, this.duration, this.step, finish: true).dispatch(context);
- }
- if (duration > 0 && widget.anim == true) {
- _animationController = AnimationController(vsync: this, duration: Duration(milliseconds: duration))
- ..addStatusListener((status) {
- if (status == AnimationStatus.completed) {
- _finish();
- }
- });
- _animation = Tween(begin: 0.0, end: points.length).animate(
- _animationController!,
- )..addListener(() {
- var index = _animation?.value.toInt();
- if (mounted) {
- var list = points.sublist(0, index);
- if (list.isNotEmpty == true) {
- // _valueNotifier.value = list;
- // _mapController?.moveCamera(CameraUpdate.newCameraPosition(CameraPosition(target: LatLng(list.last.latitude, list.last.longitude), tilt: 45.0, zoom: 18, bearing: -15)));
- Location location = list.last;
- LatLng latLng = SportUtils.toLatLng(location);
- if (widget.showType == 0) {
- _mapController?.moveCamera(CameraUpdate.newLatLng(latLng));
- }
- if (_marker != null) {
- _marker = _marker!.copyWith(positionParam: latLng, rotationParam: (location.bearing ?? 0) - 90);
- _markerMap[0] = _marker!;
- }
- polylines.add(location);
- MapNotification notification = _calPoints(list);
- int km = notification.distance ~/ 1000;
- if (km != _perKm) {
- _perKm = km;
- _markerMap[km] = _markerMapReady[km]!;
- }
- notification.dispatch(context);
- setState(() {
- });
- }
- }
- });
- _animationController?.forward(from: 0.0);
- } else {
- _markerMap.addAll(_markerMapReady);
- this.distance = widget.distance;
- _finish();
- }
- }
- double distance = 0;
- int duration = 0;
- int step = 0;
- MapNotification _calPoints(List<Location> points) {
- distance = 0;
- duration = 0;
- step = 0;
- for (var i = 1; i < points.length; i++) {
- Location point = points[i];
- if (point.state == 1) continue;
- distance += calculateLineDistance(points[i - 1].latitude, points[i - 1].longitude, point.latitude, point.longitude);
- duration = point.time ?? 0;
- step = point.step ?? 0;
- }
- return MapNotification(distance, duration, step);
- }
- Future _fillMarker(List<Location> points) async {
- _markerMap.clear();
- _markerMapReady.clear();
- _markerMapReady[-1] = Marker(position: SportUtils.toLatLng(points.first), icon: BitmapDescriptor.fromIconPath("lib/assets/img/map_icon_start_replay.png"), anchor: Offset(0.5, 0.8), clickable: false, zIndex: 101, infoWindowEnable: false, anim: true);
- _markerMapReady[1000] = Marker(position: SportUtils.toLatLng(points.last), icon: BitmapDescriptor.fromIconPath("lib/assets/img/map_icon_over_replay.png"), anchor: Offset(0.5, 0.8), clickable: false, zIndex: 102, infoWindowEnable: false, anim: true);
- _marker = Marker(
- position: SportUtils.toLatLng(points.first),
- icon: BitmapDescriptor.fromIconPath("lib/assets/img/map_icon_direction_replay.png"),
- anchor: Offset(0.5, 0.5),
- clickable: false,
- zIndex: 103,
- infoWindowEnable: false,
- );
- _markerMapReady[0] = _marker!;
- if (widget.runMapKm == true && widget.pointsKm.isNotEmpty == true)
- for (var e in widget.pointsKm.entries) {
- await _updateLocationKm(e.key, LatLng(e.value.latitude, e.value.longitude));
- }
- }
- _updateLocationKm(int key, LatLng latLng) async {
- ui.PictureRecorder pictureRecorder = new ui.PictureRecorder(); // 图片记录仪
- Canvas canvas = new Canvas(pictureRecorder); //canvas接受一个图片记录仪
- ByteData data = await rootBundle.load("lib/assets/img/map_bg_kilometre_replay.png");
- ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
- ui.FrameInfo fi = await codec.getNextFrame();
- ui.Image image = fi.image;
- Paint _paint = new Paint()
- ..color = Colors.red
- ..isAntiAlias = true;
- canvas.drawImage(image, Offset(0, 0), _paint);
- final ui.ParagraphStyle _valueStyle = ui.ParagraphStyle(
- textAlign: TextAlign.center,
- fontSize: 32,
- );
- ui.ParagraphBuilder pb = ui.ParagraphBuilder(_valueStyle)
- ..pushStyle(ui.TextStyle(color: Colors.white))
- ..addText("$key");
- ui.ParagraphConstraints constraints = ui.ParagraphConstraints(width: image.width.toDouble());
- ui.Paragraph paragraph = pb.build()..layout(constraints);
- paragraph.computeLineMetrics().forEach((element) {
- canvas.drawParagraph(paragraph, Offset(0, (image.height - element.height) / 2 - element.descent));
- });
- ui.Image picture = await pictureRecorder.endRecording().toImage(image.width, image.height); //设置生成图片的宽和高
- ByteData? pngImageBytes = await picture.toByteData(format: ui.ImageByteFormat.png);
- if (pngImageBytes == null) return;
- Uint8List pngBytes = pngImageBytes.buffer.asUint8List();
- _markerMapReady[key] = Marker(
- position: latLng,
- // anchor: Offset(0.5, 0.5),
- icon: BitmapDescriptor.fromBytes(pngBytes),
- clickable: false,
- infoWindowEnable: false,
- zIndex: 100,
- anim: true);
- }
- Future _waitMapReady() async {
- Completer completer = Completer();
- Timer timer = Timer.periodic(Duration(seconds: 1), (timer) {
- if (_mapReady == true) {
- if (!completer.isCompleted) completer.complete(true);
- }
- });
- var result = await completer.future;
- timer.cancel();
- return result;
- }
- Future<File?> takeSnapshot() async {
- var path = await request(context, () async {
- await _waitMapReady();
- return takeAMapSnapshot(_mapController);
- });
- return path;
- }
- runAnimation() async {
- bool result = false;
- if (_mapController == null) return result;
- setState(() {
- _mapReady = false;
- polylines.clear();
- _markerMap.clear();
- });
- _mapController!.moveCamera(CameraUpdate.newCameraPosition(CameraPosition(target: LatLng(widget.points.first.latitude, widget.points.first.longitude), zoom: 15, bearing: -270)));
- _onMapCreated(_mapController!);
- }
- @override
- Widget build(BuildContext context) {
- return AMapWidget(
- privacyStatement: amapPrivacyStatement,
- apiKey: AMapApiKey(androidKey: KEY_ANDROID, iosKey: KEY_IOS),
- mapType: widget.anim == true ? selectMapType(_mapReady ? widget.runMapType : 2) : selectMapType(widget.runMapType),
- onMapCreated: _onMapCreated,
- customStyleOptions: _mapReady ? customStyleOptions : null,
- initialCameraPosition: CameraPosition(target: LatLng(widget.points.first.latitude, widget.points.first.longitude), zoom: 15),
- buildingsEnabled: true,
- tiltGesturesEnabled: false,
- rotateGesturesEnabled: false,
- touchPoiEnabled: false,
- labelsEnabled: selectMapType(widget.runMapType) == MapType.navi ? false : _mapReady,
- scrollGesturesEnabled: _mapReady,
- scaleEnabled: _mapReady,
- gestureRecognizers: Set()..add(Factory<VerticalDragGestureRecognizer>(() => VerticalDragGestureRecognizer()))..add(Factory<HorizontalDragGestureRecognizer>(() => HorizontalDragGestureRecognizer())),
- markers: Set<Marker>.of(_markerMap.values),
- polylines: Set<Polyline>.of(SportUtils.splitLine(polylines, speed: _avgSpeed)),
- // onCameraMove: (position) {
- // _zoom = position.zoom;
- // },
- );
- }
- }
- class MapNotification extends Notification {
- final double distance;
- final int duration;
- final int step;
- final bool finish;
- MapNotification(this.distance, this.duration, this.step, {this.finish = false});
- }
|