This commit is contained in:
toly
2023-12-21 22:06:28 +08:00
parent 8b846f86d3
commit 2f4e8d3564
62 changed files with 6651 additions and 92 deletions

758
assets/palettes.json Normal file
View File

@@ -0,0 +1,758 @@
[
[
"0xffCC5D4C",
"0xffFFFEC6",
"0xffC7D1AF",
"0xff96B49C",
"0xff5B5847"
],
[
"0xffE4E4C5",
"0xffB9D48B",
"0xff8D2036",
"0xffCE0A31",
"0xffD3E4C5"
],
[
"0xffEB9C4D",
"0xffF2D680",
"0xffF3FFCF",
"0xffBAC9A9",
"0xff697060"
],
[
"0xff360745",
"0xffD61C59",
"0xffE7D84B",
"0xffEFEAC5",
"0xff1B8798"
],
[
"0xffF2E8C4",
"0xff98D9B6",
"0xff3EC9A7",
"0xff2B879E",
"0xff616668"
],
[
"0xffFFF3DB",
"0xffE7E4D5",
"0xffD3C8B4",
"0xffC84648",
"0xff703E3B"
],
[
"0xffF5DD9D",
"0xffBCC499",
"0xff92A68A",
"0xff7B8F8A",
"0xff506266"
],
[
"0xff041122",
"0xff259073",
"0xff7FDA89",
"0xffC8E98E",
"0xffE6F99D"
],
[
"0xff1D1313",
"0xff24B694",
"0xffD22042",
"0xffA3B808",
"0xff30C4C9"
],
[
"0xffF0FFC9",
"0xffA9DA88",
"0xff62997A",
"0xff72243D",
"0xff3B0819"
],
[
"0xffC6CCA5",
"0xff8AB8A8",
"0xff6B9997",
"0xff54787D",
"0xff615145"
],
[
"0xff429398",
"0xff6B5D4D",
"0xffB0A18F",
"0xffDFCDB4",
"0xffFBEED3"
],
[
"0xff2D1B33",
"0xffF36A71",
"0xffEE887A",
"0xffE4E391",
"0xff9ABC8A"
],
[
"0xff4B1139",
"0xff3B4058",
"0xff2A6E78",
"0xff7A907C",
"0xffC9B180"
],
[
"0xff322938",
"0xff89A194",
"0xffCFC89A",
"0xffCC883A",
"0xffA14016"
],
[
"0xff540045",
"0xffC60052",
"0xffFF714B",
"0xffEAFF87",
"0xffACFFE9"
],
[
"0xff79254A",
"0xff795C64",
"0xff79927D",
"0xffAEB18E",
"0xffE3CF9E"
],
[
"0xff452E3C",
"0xffFF3D5A",
"0xffFFB969",
"0xffEAF27E",
"0xff3B8C88"
],
[
"0xff2B2726",
"0xff0A516D",
"0xff018790",
"0xff7DAD93",
"0xffBACCA4"
],
[
"0xff027B7F",
"0xffFFA588",
"0xffD62957",
"0xffBF1E62",
"0xff572E4F"
],
[
"0xffFA6A64",
"0xff7A4E48",
"0xff4A4031",
"0xffF6E2BB",
"0xff9EC6B8"
],
[
"0xff23192D",
"0xffFD0A54",
"0xffF57576",
"0xffFEBF97",
"0xffF5ECB7"
],
[
"0xffA3C68C",
"0xff879676",
"0xff6E6662",
"0xff4F364A",
"0xff340735"
],
[
"0xffA32C28",
"0xff1C090B",
"0xff384030",
"0xff7B8055",
"0xffBCA875"
],
[
"0xff6D9788",
"0xff1E2528",
"0xff7E1C13",
"0xffBF0A0D",
"0xffE6E1C2"
],
[
"0xff373737",
"0xff8DB986",
"0xffACCE91",
"0xffBADB73",
"0xffEFEAE4"
],
[
"0xff280904",
"0xff680E34",
"0xff9A151A",
"0xffC21B12",
"0xffFC4B2A"
],
[
"0xff4B3E4D",
"0xff1E8C93",
"0xffDBD8A2",
"0xffC4AC30",
"0xffD74F33"
],
[
"0xffA69E80",
"0xffE0BA9B",
"0xffE7A97E",
"0xffD28574",
"0xff3B1922"
],
[
"0xff161616",
"0xffC94D65",
"0xffE7C049",
"0xff92B35A",
"0xff1F6764"
],
[
"0xff234D20",
"0xff36802D",
"0xff77AB59",
"0xffC9DF8A",
"0xffF0F7DA"
],
[
"0xffE6EBA9",
"0xffABBB9F",
"0xff6F8B94",
"0xff706482",
"0xff703D6F"
],
[
"0xffFDCFBF",
"0xffFEB89F",
"0xffE23D75",
"0xff5F0D3B",
"0xff742365"
],
[
"0xff641F5E",
"0xff676077",
"0xff65AC92",
"0xffC2C092",
"0xffEDD48E"
],
[
"0xff26251C",
"0xffEB0A44",
"0xffF2643D",
"0xffF2A73D",
"0xffA0E8B7"
],
[
"0xff4F364C",
"0xff5E405F",
"0xff6B6B6B",
"0xff8F9E6F",
"0xffB1CF72"
],
[
"0xff230B00",
"0xffA29D7F",
"0xffD4CFA5",
"0xffF8ECD4",
"0xffAABE9B"
],
[
"0xff85847E",
"0xffAB6A6E",
"0xffF7345B",
"0xff353130",
"0xffCBCFB4"
],
[
"0xffD24858",
"0xffEA8676",
"0xffEAB05E",
"0xffFDEECD",
"0xff493831"
],
[
"0xffD3D5B0",
"0xffB5CEA4",
"0xff9DC19D",
"0xff8C7C62",
"0xff71443F"
],
[
"0xffFFFF99",
"0xffD9CC8C",
"0xffB39980",
"0xff8C6673",
"0xff663366"
],
[
"0xffED6464",
"0xffBF6370",
"0xff87586C",
"0xff574759",
"0xff1A1B1C"
],
[
"0xffCCB24C",
"0xffF7D683",
"0xffFFFDC0",
"0xffFFFFFD",
"0xff457D97"
],
[
"0xffF0F0D8",
"0xffB4DEBE",
"0xff77CCA4",
"0xff666666",
"0xffB4DF37"
],
[
"0xff1A081F",
"0xff4D1D4D",
"0xff05676E",
"0xff489C79",
"0xffEBC288"
],
[
"0xff7A5B3E",
"0xffFAFAFA",
"0xffFA4B00",
"0xffCDBDAE",
"0xff1F1F1F"
],
[
"0xffD31900",
"0xffFF6600",
"0xffFFF2AF",
"0xff7CB490",
"0xff000000"
],
[
"0xffE8C382",
"0xffB39D69",
"0xffA86B4C",
"0xff7D1A0C",
"0xff340A0B"
],
[
"0xffEBEAA9",
"0xffEBC588",
"0xff7D2948",
"0xff3B0032",
"0xff0E0B29"
],
[
"0xff063940",
"0xff195E63",
"0xff3E838C",
"0xff8EBDB6",
"0xffECE1C3"
],
[
"0xff595B5A",
"0xff14C3A2",
"0xff0DE5A8",
"0xff7CF49A",
"0xffB8FD99"
],
[
"0xff411F2D",
"0xffAC4147",
"0xffF88863",
"0xffFFC27F",
"0xffFFE29A"
],
[
"0xffE7E79D",
"0xffC0D890",
"0xff78A890",
"0xff606078",
"0xffD8A878"
],
[
"0xff9DBCBC",
"0xffF0F0AF",
"0xffFF370F",
"0xff332717",
"0xff6BACBF"
],
[
"0xff94654C",
"0xffF89FA1",
"0xffFABDBD",
"0xffFAD6D6",
"0xffFEFCD0"
],
[
"0xff1F1F20",
"0xff2B4C7E",
"0xff567EBB",
"0xff606D80",
"0xffDCE0E6"
],
[
"0xffCDDBC2",
"0xffF7E4C6",
"0xffFB9274",
"0xffF5565B",
"0xff875346"
],
[
"0xffECBE13",
"0xff738C79",
"0xff6A6B5F",
"0xff2C2B26",
"0xffA43955"
],
[
"0xffFFF5DE",
"0xffB8D9C8",
"0xff917081",
"0xff750E49",
"0xff4D002B"
],
[
"0xffF0DDBD",
"0xffBA3622",
"0xff851E25",
"0xff520C30",
"0xff1C997F"
],
[
"0xff312C20",
"0xff494D4B",
"0xff7C7052",
"0xffB3A176",
"0xffE2CB92"
],
[
"0xff3F2C26",
"0xffDD423E",
"0xffA2A384",
"0xffEAC388",
"0xffC5AD4B"
],
[
"0xff0A0310",
"0xff49007E",
"0xffFF005B",
"0xffFF7D10",
"0xffFFB238"
],
[
"0xffCDECCC",
"0xffEDD269",
"0xffE88460",
"0xffF23460",
"0xff321D2E"
],
[
"0xff574C41",
"0xffE36B6B",
"0xffE3A56B",
"0xffE3C77B",
"0xff96875A"
],
[
"0xffE7DD96",
"0xffE16639",
"0xffAD860A",
"0xffB7023F",
"0xff55024A"
],
[
"0xff213435",
"0xff46685B",
"0xff648A64",
"0xffA6B985",
"0xffE1E3AC"
],
[
"0xff181419",
"0xff4A073C",
"0xff9E0B41",
"0xffCC3E18",
"0xffF0971C"
],
[
"0xff413040",
"0xff6C6368",
"0xffB9A173",
"0xffEAA353",
"0xffFFEFA9"
],
[
"0xff4D3B36",
"0xffEB613B",
"0xffF98F6F",
"0xffC1D9CD",
"0xffF7EADC"
],
[
"0xffFFCDB8",
"0xffFDEECF",
"0xffC8C696",
"0xff97BEA9",
"0xff37260C"
],
[
"0xff130912",
"0xff3E1C33",
"0xff602749",
"0xffB14623",
"0xffF6921D"
],
[
"0xffFFFF00",
"0xffCCD91A",
"0xff99B333",
"0xff668C4D",
"0xff336666"
],
[
"0xff001449",
"0xff012677",
"0xff005BC5",
"0xff00B4FC",
"0xff17F9FF"
],
[
"0xffED5672",
"0xff160E32",
"0xff9EAE8A",
"0xffCDBB93",
"0xffFBC599"
],
[
"0xff785D56",
"0xffBE4C54",
"0xffC6B299",
"0xffE6D5C1",
"0xffFFF4E3"
],
[
"0xff2B1719",
"0xff02483E",
"0xff057C46",
"0xff9BB61B",
"0xffF8BE00"
],
[
"0xffE6A06F",
"0xff9E9C71",
"0xff5E8271",
"0xff33454E",
"0xff242739"
],
[
"0xffF7F3D5",
"0xffFFDABF",
"0xffFA9B9B",
"0xffE88087",
"0xff635063"
],
[
"0xffE7EED0",
"0xffCAD1C3",
"0xff948E99",
"0xff51425F",
"0xff2E1437"
],
[
"0xffE25858",
"0xffE9D6AF",
"0xffFFFFDD",
"0xffC0EFD2",
"0xff384252"
],
[
"0xffACDEB2",
"0xffE1EAB5",
"0xffEDAD9E",
"0xffFE4B74",
"0xff390D2D"
],
[
"0xff42282C",
"0xff6CA19E",
"0xff84ABAA",
"0xffDED1B6",
"0xff6D997A"
],
[
"0xffFDFFD9",
"0xff73185E",
"0xff36BBA6",
"0xff0C0D02",
"0xff8B911A"
],
[
"0xff9F0A28",
"0xffD55C2B",
"0xffF6E7D3",
"0xff89A46F",
"0xff55203C"
],
[
"0xffA69A90",
"0xff4A403D",
"0xffFFF1C1",
"0xffFACF7D",
"0xffEA804C"
],
[
"0xff418E8E",
"0xff5A4E3C",
"0xffC4D428",
"0xffD8E472",
"0xffE9EBBF"
],
[
"0xffC9D1D3",
"0xffF7F7F7",
"0xff9DD3DF",
"0xff3B3737",
"0xff991818"
],
[
"0xffFAF6D0",
"0xffC7D8AB",
"0xff909A92",
"0xff744F78",
"0xff30091E"
],
[
"0xffF6C7B7",
"0xffF7A398",
"0xffFA7F77",
"0xffB42529",
"0xff000000"
],
[
"0xffA7321C",
"0xffFFDC68",
"0xffCC982A",
"0xff928941",
"0xff352504"
],
[
"0xffF0371A",
"0xff000000",
"0xffF7E6A6",
"0xff3E6B48",
"0xffB5B479"
],
[
"0xffF4F4F4",
"0xff9BA657",
"0xffF0E5C9",
"0xffA68C69",
"0xff594433"
],
[
"0xffF1E8B4",
"0xffB2BB91",
"0xffD7BF5E",
"0xffD16344",
"0xff83555E"
],
[
"0xff000000",
"0xff001F36",
"0xff1C5560",
"0xff79AE92",
"0xffFBFFCD"
],
[
"0xffE0DC8B",
"0xffF6AA3D",
"0xffED4C57",
"0xff574435",
"0xff6CC4B9"
],
[
"0xff42393B",
"0xff75C9A3",
"0xffBAC99A",
"0xffFFC897",
"0xffF7EFA2"
],
[
"0xffF2CC67",
"0xffF38264",
"0xffF40034",
"0xff5F051F",
"0xff75BAA8"
],
[
"0xffFBFEE5",
"0xffC91842",
"0xff98173D",
"0xff25232D",
"0xffA8E7CA"
],
[
"0xff998496",
"0xffF7E0AE",
"0xffFA748F",
"0xff2D2C26",
"0xffC3B457"
],
[
"0xffDBD9B7",
"0xffC1C9C8",
"0xffA5B5AB",
"0xff949A8E",
"0xff615566"
],
[
"0xffF3E6BC",
"0xffF1C972",
"0xffF5886B",
"0xff72AE95",
"0xff5A3226"
],
[
"0xffD9D4A8",
"0xffD15C57",
"0xffCC3747",
"0xff5C374B",
"0xff4A5F67"
],
[
"0xff674F23",
"0xffE48B69",
"0xffE1B365",
"0xffE5DB84",
"0xffFFEEAC"
],
[
"0xffBF2A23",
"0xffA6AD3C",
"0xffF0CE4E",
"0xffCF872E",
"0xff8A211D"
],
[
"0xff84C1B1",
"0xffAD849A",
"0xffD64783",
"0xffFD135A",
"0xff40202A"
],
[
"0xff020304",
"0xff541F14",
"0xff938172",
"0xffCC9E61",
"0xff626266"
],
[
"0xff002C2B",
"0xffFF3D00",
"0xffFFBC11",
"0xff0A837F",
"0xff076461"
]
]

View File

@@ -62,7 +62,6 @@ class MenuStore with ChangeNotifier {
?.label;
void selectMenuPath(String path) {
print("======================selectMenuPath:$path======${_shouldAddHistory}==============");
MenuNode root = MenuNode(
path: '',
label: '',
@@ -160,6 +159,14 @@ class MenuStore with ChangeNotifier {
}
void select(MenuNode menu) {
bool hasHistory = _history.where((e) => e.menuPath==menu.path).isNotEmpty;
if(hasHistory){
_shouldAddHistory = false;
selectMenuPath(menu.path);
notifyListeners();
return;
}
if (menu.isLeaf) {
_state = state.copyWith(activeMenu: menu.path);
goRouter.go(menu.path);

View File

@@ -1,4 +1,5 @@
import 'package:components/components.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:iroute/navigation/router/routers/anima.dart';
@@ -7,13 +8,13 @@ import 'package:iroute/navigation/router/routers/render.dart';
import 'package:iroute/navigation/router/routers/scroll.dart';
import 'package:iroute/navigation/router/routers/touch.dart';
import '../../transition/size_clip_transition.dart';
import '../../views/app_navigation.dart';
import '../../../pages/empty/empty_panel.dart';
import 'dashboard.dart';
import 'draw.dart';
import 'layout.dart';
final RouteBase appRoute = ShellRoute(
builder: (BuildContext context, GoRouterState state, Widget child) {
return TolyBookNavigation(content: child);
@@ -29,9 +30,27 @@ final RouteBase appRoute = ShellRoute(
animaRouters,
GoRoute(
path: '/code',
builder: (BuildContext context, GoRouterState state) {
pageBuilder: (BuildContext context, GoRouterState state) {
String? path = state.uri.queryParameters['path'];
return CodeView(path: path??'',);
return CustomTransitionPage(
transitionDuration: const Duration(milliseconds: 500),
reverseTransitionDuration: const Duration(milliseconds: 500),
child: CodeView(
path: path ?? '',
),
transitionsBuilder: (_, a1, a2, child) =>
SizeClipTransition(
animation: a1,
secondaryAnimation: a2,
child: child,
)
// CupertinoPageTransition(
// primaryRouteAnimation: a1,
// secondaryRouteAnimation: a2,
// linearTransition: true,
// child: child,
// ),
);
},
),
GoRoute(

View File

@@ -5,32 +5,76 @@ import 'package:iroute/pages/empty/empty_panel.dart';
final RouteBase drawRouters = GoRoute(
path: '/draw/chapter:index',
builder: (BuildContext context, GoRouterState state) {
pageBuilder: (BuildContext context, GoRouterState state) {
String? index = state.pathParameters['index'];
switch(index){
Widget child = const EmptyPanel(msg: '暂未实现');
switch (index) {
case '1':
return const P01Page();
child = const P01Page();
break;
case '2':
return const P02Page();
child = const P02Page();
break;
case '3':
return const P03Page();
child = const P03Page();
break;
case '4':
return const P04Page();
child = const P04Page();
break;
case '5':
return const P05Page();
child = const P05Page();
break;
case '6':
return const P06Page();
child = const P06Page();
break;
case '7':
return const P07Page();
child = const P07Page();
break;
case '8':
return const P08Page();
child = const P08Page();
break;
case '9':
return const P09Page();
child = const P09Page();
break;
case '10':
return const P10Page();
case '11':
return const P11Page();
child = const P10Page();
break;
case '11':
child = const P11Page();
break;
case '12':
child = const P12Page();
break;
case '13':
child = const P13Page();
break;
case '14':
child = const P14Page();
break;
case '15':
child = const P15Page();
break;
case '16':
child = const P16Page();
break;
case '17':
child = const P17Page();
break;
case '18':
child = const P18Page();
break;
}
return const EmptyPanel(msg: '暂未实现');
return CustomTransitionPage(
child: child,
transitionsBuilder: (ctx, a1, a2, child) => FadeTransition(
opacity: a1.drive(CurveTween(curve: Curves.easeIn)),
child: SlideTransition(
position: Tween<Offset>(
begin: Offset.zero, end: const Offset(-1.0, 0.0))
.animate(a2),
child: child,
),
));
},
);

View File

@@ -1,43 +1,19 @@
import 'package:flutter/material.dart';
class FadePageTransitionsBuilder extends PageTransitionsBuilder {
const FadePageTransitionsBuilder();
@override
Widget buildTransitions<T>(
PageRoute<T>? route,
BuildContext? context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return _FadePagePageTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
PageRoute<T>? route,
BuildContext? context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return FadeTransition(
opacity: animation.drive(CurveTween(curve: Curves.easeIn)),
child: child,
);
}
}
class _FadePagePageTransition extends StatelessWidget {
const _FadePagePageTransition({
required this.animation,
required this.secondaryAnimation,
required this.child,
});
final Animation<double> animation;
final Animation<double> secondaryAnimation;
final Widget child;
@override
Widget build(BuildContext context) {
var curveTween = CurveTween(curve: Curves.easeIn);
return FadeTransition(
opacity: animation.drive(curveTween),
child: child,
);
}
}

View File

@@ -0,0 +1,105 @@
import 'dart:math';
import 'package:flutter/cupertino.dart';
class SizeClipTransition extends StatelessWidget {
final Animation<double> animation;
final Animation<double> secondaryAnimation;
final Widget child;
const SizeClipTransition({
super.key,
required this.animation,
required this.secondaryAnimation,
required this.child,
});
@override
Widget build(BuildContext context) {
return ClipPath(
clipper: CirclePathClipper(Curves.easeIn.transform(animation.value)),
child: child,
);
}
}
class SizePathClipper extends CustomClipper<Path> {
final double progress;
SizePathClipper(this.progress);
@override
Path getClip(Size size) {
Rect box = Rect.fromLTWH(0, 0, size.width, size.height);
Rect center = Rect.fromCenter(
center: Offset(size.width / 2, size.height / 2),
width: size.width * (1 - progress),
height: size.height,
);
return Path()
..addRect(box)
..addRect(center)
..fillType = PathFillType.evenOdd;
}
@override
bool shouldReclip(covariant SizePathClipper oldClipper) {
return oldClipper.progress != progress;
}
}
class ScalePathClipper extends CustomClipper<Path> {
final double progress;
ScalePathClipper(this.progress);
@override
Path getClip(Size size) {
Rect box = Rect.fromLTWH(0, 0, size.width, size.height);
Rect center = Rect.fromCenter(
center: Offset(size.width / 2, size.height / 2),
width: size.width * (1 - progress),
height: size.height* (1 - progress),
);
return Path()
..addRect(box)
..addRect(center)
..fillType = PathFillType.evenOdd;
}
@override
bool shouldReclip(covariant ScalePathClipper oldClipper) {
return oldClipper.progress != progress;
}
}
class CirclePathClipper extends CustomClipper<Path> {
final double progress;
CirclePathClipper(this.progress);
@override
Path getClip(Size size) {
print('progress:$progress');
if(progress==0){
return Path();
}
Rect box = Rect.fromLTWH(0, 0, size.width, size.height);
Rect center = Rect.fromCircle(
center: Offset(size.width , 0),
radius: sqrt(size.width*size.width+size.height*size.height) * (progress),
);
Path zone = Path()..addRect(box);
Path cliper = Path()..addOval(center);
return Path.combine(PathOperation.intersect, zone, cliper );
}
@override
bool shouldReclip(covariant CirclePathClipper oldClipper) {
return oldClipper.progress != progress;
}
}

View File

@@ -13,6 +13,7 @@ class TolyBookNavigation extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [

View File

@@ -10,23 +10,26 @@ class AppTopBar extends StatelessWidget {
Widget build(BuildContext context) {
// String? lable = MenuScope.of(context).currentNode?.label;
return DragToMoveWrap(
child: Stack(
alignment: Alignment.centerRight,
children: [Row(
children: [
SizedBox(width: 20,),
RouteBackIndicator(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: RouterIndicator(),
),
// Text(
// '$lable',
// style: TextStyle(fontSize: 14),
// ),
Spacer(),
],
),WindowButtons()]
child: SizedBox(
height: 36,
child: Stack(
alignment: Alignment.centerRight,
children: [Row(
children: [
SizedBox(width: 20,),
RouteBackIndicator(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: RouterIndicator(),
),
// Text(
// '$lable',
// style: TextStyle(fontSize: 14),
// ),
Spacer(),
],
),WindowButtons()]
),
),
);
}

View File

@@ -18,18 +18,20 @@ class _CodeViewState extends State<CodeView> {
@override
void initState() {
// TODO: implement initState
super.initState();
_loadContent();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Material(
child: CodeWidget(code:'${content}', style: HighlighterStyle.fromColors(HighlighterStyle.lightColor),),
return ColoredBox(
color: Colors.white,
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20,vertical: 10),
child: Material(
child: CodeWidget(code:'${content}', style: HighlighterStyle.fromColors(HighlighterStyle.lightColor),),
),
),
),
);

View File

@@ -14,12 +14,11 @@ class Coordinate {
Coordinate(
{this.step = 20,
this.strokeWidth = .5,
this.axisColor = Colors.blue,
this.gridColor = Colors.grey});
this.strokeWidth = .5,
this.axisColor = Colors.blue,
this.gridColor = Colors.grey});
final Paint _gridPaint = Paint();
final Path _gridPath = Path();
void paint(Canvas canvas, Size size) {
canvas.save();
@@ -30,6 +29,8 @@ class Coordinate {
canvas.restore();
}
void _drawAxis(Canvas canvas, Size size) {
_gridPaint
..color = Colors.blue
@@ -49,28 +50,30 @@ class Coordinate {
}
void _drawGridLine(Canvas canvas, Size size) {
final Path path = Path();
_gridPaint
..style = PaintingStyle.stroke
..strokeWidth = .5
..color = Colors.grey;
for (int i = 0; i < size.width / 2 / step; i++) {
_gridPath.moveTo(step * i, -size.height / 2 );
_gridPath.relativeLineTo(0, size.height);
path.moveTo(step * i, -size.height / 2 );
path.relativeLineTo(0, size.height);
_gridPath.moveTo(-step * i, -size.height / 2 );
_gridPath.relativeLineTo(0, size.height);
path.moveTo(-step * i, -size.height / 2 );
path.relativeLineTo(0, size.height);
}
for (int i = 0; i < size.height / 2 / step; i++) {
_gridPath.moveTo(-size.width / 2,step * i );
_gridPath.relativeLineTo(size.width,0 );
path.moveTo(-size.width / 2,step * i );
path.relativeLineTo(size.width,0 );
_gridPath.moveTo(-size.width / 2,-step * i, );
_gridPath.relativeLineTo(size.width,0 );
path.moveTo(-size.width / 2,-step * i, );
path.relativeLineTo(size.width,0 );
}
canvas.drawPath(_gridPath, _gridPaint);
canvas.drawPath(path, _gridPaint);
}
void _drawAxisText(Canvas canvas, String str,
@@ -160,4 +163,3 @@ class Coordinate {
canvas.restore();
}
}

View File

@@ -11,3 +11,10 @@ export 'p08/p08.dart';
export 'p09/p09.dart';
export 'p10/p10.dart';
export 'p11/p11.dart';
export 'p12/p12.dart';
export 'p13/p13.dart';
export 'p14/p14.dart';
export 'p15/p15.dart';
export 'p16/p16.dart';
export 'p17/p17.dart';
export 'p18/p18.dart';

View File

@@ -0,0 +1 @@
export 'p12_page.dart';

View File

@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:components/components.dart';
import 's01.dart' as s1;
import 's02.dart' as s2;
import 's03.dart' as s3;
import 's04.dart' as s4;
class P12Page extends StatelessWidget {
const P12Page({super.key});
@override
Widget build(BuildContext context) {
return const DemoShower(
srcCodeDir: 'draw/p12',
demos: [
s1.Paper(),
s2.Paper(),
s3.Paper(),
s4.Paper(),
],
);
}
}

View File

@@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/3
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: HandleWidget(),
);
}
}
class HandleWidget extends StatefulWidget {
final double size;
final double handleRadius;
HandleWidget({Key? key, this.size = 160.0, this.handleRadius = 20.0})
: super(key: key);
@override
_HandleWidgetState createState() => _HandleWidgetState();
}
class _HandleWidgetState extends State<HandleWidget> {
@override
Widget build(BuildContext context) {
return CustomPaint(
size: Size(widget.size, widget.size),
painter: _HandlePainter(handleR: widget.handleRadius));
}
}
class _HandlePainter extends CustomPainter {
var _paint = Paint();
var handleR;
_HandlePainter({this.handleR}) {
_paint
..color = Colors.blue
..style = PaintingStyle.fill
..isAntiAlias = true;
}
@override
void paint(Canvas canvas, Size size) {
canvas.clipRect(Offset.zero & size);
final bgR = size.width / 2 - handleR;
canvas.translate(size.width / 2, size.height / 2);
_paint.color = _paint.color.withAlpha(100);
canvas.drawCircle(Offset(0, 0), bgR, _paint);
_paint.color = _paint.color.withAlpha(150);
canvas.drawCircle(Offset(0, 0), handleR, _paint);
}
@override
bool shouldRepaint(_HandlePainter oldDelegate) =>
oldDelegate.handleR != handleR;
}

View File

@@ -0,0 +1,109 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import '../components/coordinate_pro.dart';
/// create by 张风捷特烈 on 2020-03-19
/// contact me by email 1981462002@qq.com
/// 说明: 纸
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: HandleWidget(),
);
}
}
class HandleWidget extends StatefulWidget {
final double size;
final double handleRadius;
HandleWidget({Key? key, this.size = 160, this.handleRadius = 20.0})
: super(key: key);
@override
_HandleWidgetState createState() => _HandleWidgetState();
}
class _HandleWidgetState extends State<HandleWidget> {
ValueNotifier<Offset> _offset = ValueNotifier(Offset.zero);
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanEnd: reset,
onPanUpdate: parser,
child: CustomPaint(
size: Size(widget.size, widget.size),
painter: _HandlePainter(
color: Colors.green,
handleR: widget.handleRadius,
offset: _offset)));
}
reset(DragEndDetails details) {
_offset.value = Offset.zero;
}
parser(DragUpdateDetails details) {
final offset = details.localPosition;
double dx = 0.0;
double dy = 0.0;
dx = offset.dx - widget.size / 2;
dy = offset.dy - widget.size / 2;
var rad = atan2(dx, dy);
if (dx < 0) {
rad += 2 * pi;
}
var bgR = widget.size / 2 - widget.handleRadius;
var thta = rad - pi / 2; //旋转坐标系90度
if (sqrt(dx * dx + dy * dy) > bgR) {
dx = bgR * cos(thta);
dy = -bgR * sin(thta);
}
_offset.value = Offset(dx, dy);
}
}
class _HandlePainter extends CustomPainter {
final _paint = Paint();
final ValueNotifier<Offset> offset;
final Color color;
var handleR;
_HandlePainter({this.handleR,required this.offset, this.color = Colors.blue})
: super(repaint: offset) ;
@override
void paint(Canvas canvas, Size size) {
canvas.clipRect(Offset.zero & size);
final bgR = size.width / 2 - handleR;
canvas.translate(size.width / 2, size.height / 2);
_paint.style = PaintingStyle.fill;
_paint.color = color.withAlpha(100);
canvas.drawCircle(Offset(0, 0), bgR, _paint);
_paint.color = color.withAlpha(150);
canvas.drawCircle(
Offset(offset.value.dx, offset.value.dy), handleR, _paint);
_paint.color = color;
_paint.style = PaintingStyle.stroke;
canvas.drawLine(Offset.zero, offset.value, _paint);
}
@override
bool shouldRepaint(_HandlePainter oldDelegate) =>
oldDelegate.offset != offset ||
oldDelegate.color != color ||
oldDelegate.handleR != handleR;
}

View File

@@ -0,0 +1,153 @@
import 'dart:async';
import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../components/coordinate_pro.dart';
/// create by 张风捷特烈 on 2020-03-19
/// contact me by email 1981462002@qq.com
/// 说明: 纸
class Paper extends StatefulWidget{
const Paper({super.key});
@override
State<Paper> createState() => _PaperState();
}
class _PaperState extends State<Paper> {
double _rotate = 0;
@override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Transform.rotate(
angle: _rotate,
child:
Container(
color: Colors.blue,
alignment: Alignment.center,
width: 100,
height: 100,
),
),
HandleWidget(
onMove: _onMove,
),
],
),
);
}
void _onMove(double rotate, double distance) {
setState(() {
_rotate= rotate;
});
}
}
class HandleWidget extends StatefulWidget {
final double size;
final double handleRadius;
final void Function(double rotate, double distance) onMove;
HandleWidget(
{Key? key,
this.size = 160,
this.handleRadius = 20.0,
required this.onMove})
: super(key: key);
@override
_HandleWidgetState createState() => _HandleWidgetState();
}
class _HandleWidgetState extends State<HandleWidget> {
ValueNotifier<Offset> _offset = ValueNotifier(Offset.zero);
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: GestureDetector(
onPanEnd: reset,
onPanUpdate: parser,
child: CustomPaint(
size: Size(widget.size, widget.size),
painter: _HandlePainter(
color: Colors.green,
handleR: widget.handleRadius,
offset: _offset))),
);
}
reset(DragEndDetails details) {
_offset.value = Offset.zero;
widget.onMove(0, 0);
}
parser(DragUpdateDetails details) {
final offset = details.localPosition;
double dx = 0.0;
double dy = 0.0;
dx = offset.dx - widget.size / 2;
dy = offset.dy - widget.size / 2;
var rad = atan2(dx, dy);
if (dx < 0) {
rad += 2 * pi;
}
var bgR = widget.size / 2 - widget.handleRadius;
var thta = rad - pi / 2; //旋转坐标系90度
var d = sqrt(dx * dx + dy * dy);
if (d > bgR) {
dx = bgR * cos(thta);
dy = -bgR * sin(thta);
}
widget.onMove(thta, d);
_offset.value = Offset(dx, dy);
}
}
class _HandlePainter extends CustomPainter {
var _paint = Paint();
final ValueNotifier<Offset> offset;
final Color color;
var handleR;
_HandlePainter({this.handleR,required this.offset, this.color = Colors.blue})
: super(repaint: offset);
@override
void paint(Canvas canvas, Size size) {
canvas.clipRect(Offset.zero & size);
final bgR = size.width / 2 - handleR;
canvas.translate(size.width / 2, size.height / 2);
_paint.style = PaintingStyle.fill;
_paint.color = color.withAlpha(100);
canvas.drawCircle(Offset(0, 0), bgR, _paint);
_paint.color = color.withAlpha(150);
canvas.drawCircle(
Offset(offset.value.dx, offset.value.dy), handleR, _paint);
_paint.color = color;
_paint.style = PaintingStyle.stroke;
canvas.drawLine(Offset.zero, offset.value, _paint);
}
@override
bool shouldRepaint(_HandlePainter oldDelegate) =>
oldDelegate.offset != offset ||
oldDelegate.color != color ||
oldDelegate.handleR != handleR;
}

View File

@@ -0,0 +1,167 @@
import 'dart:async';
import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../components/coordinate_pro.dart';
/// create by 张风捷特烈 on 2020-03-19
/// contact me by email 1981462002@qq.com
/// 说明: 纸
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: RulerChooser(
onChanged: (value) {},
),
);
}
}
class RulerChooser extends StatefulWidget {
final Size size;
final void Function(double) onChanged;
final int min;
final int max;
const RulerChooser(
{Key? key,
required this.onChanged,
this.max = 200,
this.min = 100,
this.size = const Size(240.0, 60)})
: super(key: key);
@override
_RulerChooserState createState() => _RulerChooserState();
}
class _RulerChooserState extends State<RulerChooser> {
ValueNotifier<double> _dx = ValueNotifier(0.0);
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanUpdate: _parser,
child: CustomPaint(
size: widget.size,
painter: _HandlePainter(dx: _dx, max: widget.max, min: widget.min)),
);
}
double dx = 0;
void _parser(DragUpdateDetails details) {
dx += details.delta.dx;
if (dx > 0) {
dx = 0.0;
}
var limitMax = -(widget.max - widget.min) * (_kSpacer + _kStrokeWidth);
if (dx < limitMax) {
dx = limitMax;
}
_dx.value = dx;
if (widget.onChanged != null) {
widget.onChanged(details.delta.dx / (_kSpacer + _kStrokeWidth));
}
}
}
const double _kHeightLevel1 = 20; // 短线长
const double _kHeightLevel2 = 25; // 5 线长
const double _kHeightLevel3 = 30; //10 线长
const double _kPrefixOffSet = 5; // 左侧偏移
const double _kVerticalOffSet = 12; // 线顶部偏移
const double _kStrokeWidth = 2; // 刻度宽
const double _kSpacer = 4; // 刻度间隙
const List<Color> _kRulerColors = [
// 渐变色
Color(0xFF1426FB), Color(0xFF6080FB), Color(0xFFBEE0FB),
];
const List<double> _kRulerColorStops = [0.0, 0.2, 0.8];
class _HandlePainter extends CustomPainter {
var _paint = Paint();
Paint _pointPaint = Paint();
final ValueNotifier<double> dx;
final int max;
final int min;
_HandlePainter({required this.dx, required this.max, required this.min})
: super(repaint: dx) {
_paint
..strokeWidth = _kStrokeWidth
..shader = ui.Gradient.radial(
Offset(0, 0), 25, _kRulerColors, _kRulerColorStops, TileMode.mirror);
_pointPaint
..color = Colors.purple
..strokeWidth = 4
..strokeCap = StrokeCap.round;
}
@override
void paint(Canvas canvas, Size size) {
canvas.clipRect(Offset.zero & size);
drawArrow(canvas);
canvas.translate(dx.value, 0);
drawRuler(canvas);
}
// 绘制刻度
void drawRuler(Canvas canvas) {
double y = _kHeightLevel1;
for (int i = min; i < max + 5; i++) {
if (i % 5 == 0 && i % 10 != 0) {
y = _kHeightLevel2;
} else if (i % 10 == 0) {
y = _kHeightLevel3;
_simpleDrawText(canvas, i.toString(),
offset: Offset(-3, _kHeightLevel3 + 2));
} else {
y = _kHeightLevel1;
}
canvas.drawLine(Offset.zero, Offset(0, y), _paint);
canvas.translate(_kStrokeWidth + _kSpacer, 0);
}
}
// 绘制三角形尖角
void drawArrow(Canvas canvas) {
var path = Path()
..moveTo(_kStrokeWidth / 2 + _kPrefixOffSet, 3)
..relativeLineTo(-3, 0)
..relativeLineTo(3, _kPrefixOffSet)
..relativeLineTo(3, -_kPrefixOffSet)
..close();
canvas.drawPath(path, _pointPaint);
canvas.translate(_kStrokeWidth / 2 + _kPrefixOffSet, _kVerticalOffSet);
}
void _simpleDrawText(Canvas canvas, String str,
{Offset offset = Offset.zero}) {
var builder = ui.ParagraphBuilder(ui.ParagraphStyle())
..pushStyle(
ui.TextStyle(
color: Colors.black, textBaseline: ui.TextBaseline.alphabetic),
)
..addText(str);
canvas.drawParagraph(
builder.build()
..layout(ui.ParagraphConstraints(width: 11.0 * str.length)),
offset);
}
@override
bool shouldRepaint(_HandlePainter oldDelegate) =>
oldDelegate.dx != dx || oldDelegate.min != min || oldDelegate.max != max;
}

View File

@@ -0,0 +1 @@
export 'p13_page.dart';

View File

@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:components/components.dart';
import 's01.dart' as s1;
import 's02.dart' as s2;
import 's03.dart' as s3;
import 's04.dart' as s4;
class P13Page extends StatelessWidget {
const P13Page({super.key});
@override
Widget build(BuildContext context) {
return const DemoShower(
srcCodeDir: 'draw/p13',
demos: [
s1.Paper(),
s2.Paper(),
s3.Paper(),
s4.Paper(),
],
);
}
}

View File

@@ -0,0 +1,114 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:ui'as ui;
import '../components/coordinate_pro.dart';
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
// 使用CustomPaint
painter: PaperPainter(),
),
);
}
}
class PaperPainter extends CustomPainter {
final Coordinate coordinate = Coordinate();
final List<Offset> points = [];
final double step = 1;
final double min = 0;
final double max = 360;
void initPoints() {
for (double x = min; x <= max; x += step) {
points.add(Offset(x, f(x)));
// double thta = (pi / 180 * x);
// var p = f(thta);
// points.add(Offset(p * cos(thta), p * sin(thta)));
}
}
void initPointsWithPolar() {
for (double x = min; x <= max; x += step) {
double thta = (pi / 180 * x); // 角度转化为弧度
var p = f(thta);
points.add(Offset(p * cos(thta), p * sin(thta)));
}
}
// double f(double x) {
// double y = -x * x / 200 + 100;
// return y;
// }
// double f(double thta) {
// double p = 10 * thta;
// return p;
// }
// double f(double thta) {
// double p = 100 * (1-cos(thta));
// return p;
// }
// double f(double thta) {
// double p = 150*sin(5*thta).abs();
// return p;
// }
double f(double thta) { // 100*(1-4*sinθ)
double p = 50*(pow(e,cos(thta)) - 2 * cos(4 * thta) +pow(sin(thta / 12), 5));
return p;
}
@override
void paint(Canvas canvas, Size size) {
coordinate.paint(canvas, size);
canvas.translate(size.width / 2, size.height / 2);
// initPoints();
initPointsWithPolar();
Path path = Path();
Paint paint = Paint()
..color = Colors.red
..strokeWidth = 1.5
..style = PaintingStyle.stroke;
var colors = [
Color(0xFFF60C0C),
Color(0xFFF3B913),
Color(0xFFE7F716),
Color(0xFF3DF30B),
Color(0xFF0DF6EF),
Color(0xFF0829FB),
Color(0xFFB709F4),
];
var pos = [1.0 / 7, 2.0 / 7, 3.0 / 7, 4.0 / 7, 5.0 / 7, 6.0 / 7, 1.0];
paint.shader = ui.Gradient.linear(
Offset(0, 0), Offset(100, 0), colors, pos,TileMode.mirror);
// canvas.drawPoints(PointMode.points, points, paint);
canvas.drawPoints(PointMode.polygon, points, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,123 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'dart:ui'as ui;
import '../components/coordinate_pro.dart';
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget{
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
// 使用CustomPaint
painter: PaperPainter(),
),
);
}
}
class PaperPainter extends CustomPainter {
final Coordinate coordinate = Coordinate();
final List<Offset> points = [];
final double step = 6;
final double min = -240;
final double max = 240;
void initPoints() {
for (double x = min; x < max; x += step) {
points.add(Offset(x, f(x)));
}
points.add(Offset(max, f(max)));
points.add(Offset(max, f(max)));
}
void initPointsWithPolar() {
for (double x = min; x < max; x += step) {
double thta = (pi / 180 * x); // 角度转化为弧度
var p = f(thta);
points.add(Offset(p * cos(thta), p * sin(thta)));
}
}
// double f(double x) {
// double y = -x * x / 200 + 100;
// return y;
// }
// double f(double thta) {
// double p = 10 * thta;
// return p;
// }
// double f(double thta) {
// double p = 100 * (1-cos(thta));
// return p;
// }
// double f(double thta) {
// double p = 150*sin(5*thta).abs();
// return p;
// }
double f(double thta) { // 100*(1-4*sinθ)
double p = 50*(pow(e,cos(thta)) - 2 * cos(4 * thta) +pow(sin(thta / 12), 5));
return p;
}
@override
void paint(Canvas canvas, Size size) {
coordinate.paint(canvas, size);
canvas.translate(size.width / 2, size.height / 2);
// initPoints();
initPointsWithPolar();
Paint paint = Paint()
..color = Colors.red
..strokeWidth = 1.5
..style = PaintingStyle.stroke;
var colors = [
Color(0xFFF60C0C),
Color(0xFFF3B913),
Color(0xFFE7F716),
Color(0xFF3DF30B),
Color(0xFF0DF6EF),
Color(0xFF0829FB),
Color(0xFFB709F4),
];
var pos = [1.0 / 7, 2.0 / 7, 3.0 / 7, 4.0 / 7, 5.0 / 7, 6.0 / 7, 1.0];
paint.shader = ui.Gradient.linear(
Offset(0, 0), Offset(100, 0), colors, pos,TileMode.mirror);
Offset p1 = points[0];
Path path = Path()..moveTo(p1.dx, p1.dy);
for (var i = 1; i < points.length-1; i++) {
double xc = (points[i].dx + points[i + 1].dx) / 2;
double yc = (points[i].dy + points[i + 1].dy) / 2;
Offset p2 = points[i];
path.quadraticBezierTo(p2.dx, p2.dy, xc, yc);
}
canvas.drawPath(path, paint);
// canvas.drawPoints(PointMode.points, points, paint..shader=null..strokeWidth=3);
// canvas.drawPoints(PointMode.polygon, points, paint..shader=null..strokeWidth=1..color=Colors.blue);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,130 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatefulWidget {
const Paper({super.key});
@override
_PaperState createState() => _PaperState();
}
class _PaperState extends State<Paper> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 15),
vsync: this,
)..repeat()
;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
painter: PaperPainter(_controller),
),
);
}
}
class PaperPainter extends CustomPainter {
// final Coordinate coordinate = Coordinate();
final Animation<double> repaint;
PaperPainter(this.repaint) : super(repaint: repaint) {
initPointsWithPolar();
}
final List<Offset> points = [];
final Path path = Path();
final double step = 4;
final double min = -240;
final double max = 240;
void initPointsWithPolar() {
for (double x = min; x < max; x += step) {
double thta = (pi / 180 * x); // 角度转化为弧度
var p = f(thta);
points.add(Offset(p * cos(thta), p * sin(thta)));
}
double thta = (pi / 180 * max);
points.add(Offset(f(thta) * cos(thta), f(thta) * sin(thta)));
points.add(Offset(f(thta) * cos(thta), f(thta) * sin(thta)));
}
double f(double thta) {
double p = 150*sin(5*thta).abs();
return p;
}
@override
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
Paint paint = Paint()
..color = Colors.red
..strokeWidth = 1.5
..style = PaintingStyle.stroke;
var colors = [
Color(0xFFF60C0C),
Color(0xFFF3B913),
Color(0xFFE7F716),
Color(0xFF3DF30B),
Color(0xFF0DF6EF),
Color(0xFF0829FB),
Color(0xFFB709F4),
];
var pos = [1.0 / 7, 2.0 / 7, 3.0 / 7, 4.0 / 7, 5.0 / 7, 6.0 / 7, 1.0];
paint.shader = ui.Gradient.linear(
Offset(0, 0), Offset(100, 0), colors, pos, TileMode.mirror);
Offset p1 = points[0];
path.reset();
path..moveTo(p1.dx, p1.dy);
for (var i = 1; i < points.length - 1; i++) {
double xc = (points[i].dx + points[i + 1].dx) / 2;
double yc = (points[i].dy + points[i + 1].dy) / 2;
Offset p2 = points[i];
path.quadraticBezierTo(p2.dx, p2.dy, xc, yc);
}
canvas.drawPath(path, paint);
PathMetrics pms = path.computeMetrics();
pms.forEach((pm) {
Tangent? tangent = pm.getTangentForOffset(pm.length * repaint.value);
if(tangent == null) return;
canvas.drawCircle(
tangent.position, 5, Paint()..color = Colors.blue);
});
}
@override
bool shouldRepaint(PaperPainter oldDelegate) =>
oldDelegate.repaint != repaint;
}

View File

@@ -0,0 +1,129 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatefulWidget {
const Paper({super.key});
@override
_PaperState createState() => _PaperState();
}
class _PaperState extends State<Paper> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 15),
vsync: this,
)..repeat()
;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
painter: PaperPainter(_controller),
),
);
}
}
class PaperPainter extends CustomPainter {
final Animation<double> repaint;
PaperPainter(this.repaint) : super(repaint: repaint) {
initPointsWithPolar();
}
final List<Offset> points = [];
final Path path = Path();
final double step = 4;
final double min = 0;
final double max = 360;
void initPointsWithPolar() {
for (double x = min; x < max; x += step) {
double thta = (pi / 180 * x); // 角度转化为弧度
var p = f(thta);
points.add(Offset(p * cos(thta), p * sin(thta)));
}
double thta = (pi / 180 * max);
points.add(Offset(f(thta) * cos(thta), f(thta) * sin(thta)));
points.add(Offset(f(thta) * cos(thta), f(thta) * sin(thta)));
}
double f(double thta) {
double p = 150*sin(5*thta).abs();
return p;
}
@override
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
Paint paint = Paint()
..color = Colors.red
..strokeWidth = 1.5
..style = PaintingStyle.stroke;
var colors = [
Color(0xFFF60C0C),
Color(0xFFF3B913),
Color(0xFFE7F716),
Color(0xFF3DF30B),
Color(0xFF0DF6EF),
Color(0xFF0829FB),
Color(0xFFB709F4),
];
var pos = [1.0 / 7, 2.0 / 7, 3.0 / 7, 4.0 / 7, 5.0 / 7, 6.0 / 7, 1.0];
paint.shader = ui.Gradient.linear(
Offset(0, 0), Offset(100, 0), colors, pos, TileMode.mirror);
Offset p1 = points[0];
path.reset();
path..moveTo(p1.dx, p1.dy);
for (var i = 1; i < points.length - 1; i++) {
double xc = (points[i].dx + points[i + 1].dx) / 2;
double yc = (points[i].dy + points[i + 1].dy) / 2;
Offset p2 = points[i];
path.quadraticBezierTo(p2.dx, p2.dy, xc, yc);
}
PathMetrics pms = path.computeMetrics();
pms.forEach((pm) {
Tangent? tangent = pm.getTangentForOffset(pm.length * repaint.value);
if(tangent == null) return;
canvas.drawPath(pm.extractPath(0, pm.length * repaint.value), paint);
canvas.drawCircle(
tangent.position, 5, Paint()..color = Colors.blue);
});
}
@override
bool shouldRepaint(PaperPainter oldDelegate) =>
oldDelegate.repaint != repaint;
}

View File

@@ -0,0 +1 @@
export 'p14_page.dart';

View File

@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:components/components.dart';
import 's01.dart' as s1;
import 's02.dart' as s2;
import 's03.dart' as s3;
import 's04.dart' as s4;
import 's05.dart' as s5;
class P14Page extends StatelessWidget {
const P14Page({super.key});
@override
Widget build(BuildContext context) {
return const DemoShower(
srcCodeDir: 'draw/p14',
demos: [
s1.Paper(),
s2.Paper(),
s3.Paper(),
s4.Paper(),
s5.Paper(),
],
);
}
}

View File

@@ -0,0 +1,62 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import '../components/coordinate_pro.dart';
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
// 使用CustomPaint
painter: PaperPainter(),
),
);
}
}
class PaperPainter extends CustomPainter {
final Coordinate coordinate = Coordinate();
Offset p1 = Offset(100, 100);
Offset p2 = Offset(120, -60);
Paint _helpPaint = Paint();
@override
void paint(Canvas canvas, Size size) {
coordinate.paint(canvas, size);
canvas.translate(size.width / 2, size.height / 2);
Paint paint = Paint()
..color = Colors.orange
..style = PaintingStyle.stroke
..strokeWidth = 2;
Path path = Path();
path.quadraticBezierTo(p1.dx, p1.dy, p2.dx, p2.dy);
canvas.drawPath(path, paint);
_drawHelp(canvas);
}
void _drawHelp(Canvas canvas) {
_helpPaint
..color = Colors.purple
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
canvas.drawPoints(PointMode.lines,[Offset.zero, p1, p1,p2], _helpPaint..strokeWidth=1);
canvas.drawPoints(PointMode.points, [Offset.zero, p1, p1, p2],
_helpPaint..strokeWidth = 8);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,131 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import '../components/coordinate_pro.dart';
import 'touch_info.dart';
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatefulWidget {
const Paper({super.key});
@override
_PaperState createState() => _PaperState();
}
class _PaperState extends State<Paper> {
final TouchInfo touchInfo = TouchInfo();
@override
void dispose() {
touchInfo.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanDown: _onPanDown,
onPanUpdate: _onPanUpdate,
child: Container(
color: Colors.white,
child: CustomPaint(
painter: PaperPainter(repaint: touchInfo),
),
),
);
}
void _onPanDown(DragDownDetails details) {
if (touchInfo.points.length < 3) {
touchInfo.addPoint(details.localPosition);
}else{
judgeZone(details.localPosition);
}
}
void _onPanUpdate(DragUpdateDetails details) {
judgeZone(details.localPosition,update: true);
}
///判断出是否在某点的半径为r圆范围内
bool judgeCircleArea(Offset src, Offset dst, double r) =>
(src - dst).distance <= r;
//判断哪个点被选中
void judgeZone(Offset src,{bool update =false}) {
for (int i = 0; i < touchInfo.points.length; i++) {
if (judgeCircleArea(src, touchInfo.points[i], 15)) {
touchInfo.selectIndex = i;
if(update){
touchInfo.updatePoint(i, src);
}
}
}
}
}
class PaperPainter extends CustomPainter {
final Coordinate coordinate = Coordinate();
Paint _helpPaint = Paint()
..color = Colors.purple
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
final TouchInfo repaint;
PaperPainter({ required this.repaint}) : super(repaint: repaint);
List<Offset> pos = [];
@override
void paint(Canvas canvas, Size size) {
coordinate.paint(canvas, size);
canvas.translate(size.width / 2, size.height / 2);
pos = repaint.points
.map((e) => e.translate(-size.width / 2, -size.height / 2))
.toList();
Path path = Path();
Paint paint = Paint()
..color = Colors.orange
..style = PaintingStyle.stroke
..strokeWidth = 2;
if (pos.length < 3) {
canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8);
} else {
path.moveTo(pos[0].dx, pos[0].dy);
path.quadraticBezierTo(pos[1].dx, pos[1].dy, pos[2].dx, pos[2].dy);
canvas.drawPath(path, paint);
_drawHelp(canvas);
_drawSelectPos(canvas,size);
}
}
void _drawHelp(Canvas canvas) {
_helpPaint..color = Colors.purple;
canvas.drawPoints(PointMode.polygon, pos, _helpPaint..strokeWidth = 1);
canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8);
}
void _drawSelectPos(Canvas canvas,Size size) {
Offset? selectPos = repaint.selectPoint;
if (selectPos == null) return;
selectPos = selectPos.translate(-size.width / 2, -size.height / 2);
canvas.drawCircle(
selectPos,
10,
_helpPaint
..color = Colors.green
..strokeWidth = 2);
}
@override
bool shouldRepaint(PaperPainter oldDelegate) =>
oldDelegate.repaint != repaint;
}

View File

@@ -0,0 +1,127 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import '../components/coordinate_pro.dart';
import 'touch_info.dart';
class Paper extends StatefulWidget {
const Paper({super.key});
@override
_PaperState createState() => _PaperState();
}
class _PaperState extends State<Paper> {
final TouchInfo touchInfo = TouchInfo();
@override
void dispose() {
touchInfo.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanDown: _onPanDown,
onPanUpdate: _onPanUpdate,
child: Container(
color: Colors.white,
child: CustomPaint(
painter: PaperPainter(repaint: touchInfo),
),
),
);
}
void _onPanDown(DragDownDetails details) {
if (touchInfo.points.length < 4) {
touchInfo.addPoint(details.localPosition);
}else{
judgeZone(details.localPosition);
}
}
void _onPanUpdate(DragUpdateDetails details) {
judgeZone(details.localPosition,update: true);
}
///判断出是否在某点的半径为r圆范围内
bool judgeCircleArea(Offset src, Offset dst, double r) =>
(src - dst).distance <= r;
//判断哪个点被选中
void judgeZone(Offset src,{bool update =false}) {
for (int i = 0; i < touchInfo.points.length; i++) {
if (judgeCircleArea(src, touchInfo.points[i], 15)) {
touchInfo.selectIndex = i;
if(update){
touchInfo.updatePoint(i, src);
}
}
}
}
}
class PaperPainter extends CustomPainter {
final Coordinate coordinate = Coordinate();
Paint _helpPaint = Paint()
..color = Colors.purple
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
final TouchInfo repaint;
PaperPainter({required this.repaint}) : super(repaint: repaint);
List<Offset> pos = [];
@override
void paint(Canvas canvas, Size size) {
coordinate.paint(canvas, size);
canvas.translate(size.width / 2, size.height / 2);
pos = repaint.points
.map((e) => e.translate(-size.width / 2, -size.height / 2))
.toList();
Path path = Path();
Paint paint = Paint()
..color = Colors.orange
..style = PaintingStyle.stroke
..strokeWidth = 2;
if (pos.length < 4) {
canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8);
} else {
path.moveTo(pos[0].dx, pos[0].dy);
path.cubicTo(pos[1].dx, pos[1].dy, pos[2].dx, pos[2].dy, pos[3].dx, pos[3].dy);
canvas.drawPath(path, paint);
_drawHelp(canvas);
_drawSelectPos(canvas,size);
}
}
void _drawHelp(Canvas canvas) {
_helpPaint..color = Colors.purple;
canvas.drawPoints(PointMode.polygon, pos, _helpPaint..strokeWidth = 1);
canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8);
}
void _drawSelectPos(Canvas canvas,Size size) {
Offset? selectPos = repaint.selectPoint;
if (selectPos == null) return;
selectPos = selectPos.translate(-size.width / 2, -size.height / 2);
canvas.drawCircle(
selectPos,
10,
_helpPaint
..color = Colors.green
..strokeWidth = 2);
}
@override
bool shouldRepaint(PaperPainter oldDelegate) =>
oldDelegate.repaint != repaint;
}

View File

@@ -0,0 +1,170 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import '../components/coordinate_pro.dart';
import 'touch_info.dart';
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatefulWidget {
const Paper({super.key});
@override
_PaperState createState() => _PaperState();
}
class _PaperState extends State<Paper> {
final TouchInfo touchInfo = TouchInfo();
//单位圆(即半径为1)控制线长
final rate = 0.551915024494;
double _radius = 150;
@override
void initState() {
super.initState();
touchInfo.setPoints(_initPoints());
}
@override
void dispose() {
touchInfo.dispose();
super.dispose();
}
List<Offset> _initPoints() {
final List<Offset> pos = [];
//第一段线
pos.add(Offset(0, rate) * _radius);
pos.add(Offset(1 - rate, 1) * _radius);
pos.add(Offset(1, 1) * _radius);
//第二段线
pos.add(Offset(1 + rate, 1) * _radius);
pos.add(Offset(2, rate) * _radius);
pos.add(Offset(2, 0) * _radius);
//第三段线
pos.add(Offset(2, -rate) * _radius);
pos.add(Offset(1 + rate, -1) * _radius);
pos.add(Offset(1, -1) * _radius);
//第四段线
pos.add(Offset(1 - rate, -1) * _radius);
pos.add(Offset(0, -rate) * _radius);
pos.add(Offset(0, 0));
return pos;
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder:(_,cts)=> GestureDetector(
onPanDown: (details)=>_onPanDown(details,cts.biggest),
onPanUpdate: (details)=>_onPanUpdate(details,cts.biggest),
child: Container(
color: Colors.white,
child: CustomPaint(
painter: PaperPainter(repaint: touchInfo),
),
),
),
);
}
void _onPanDown(DragDownDetails details,Size size) {
print("=====${details}===========${touchInfo.points.length}}");
if (touchInfo.points.length < 4) {
touchInfo.addPoint(details.localPosition);
} else {
judgeZone(details.localPosition,size);
}
}
void _onPanUpdate(DragUpdateDetails details,Size size) {
judgeZone(details.localPosition,size, update: true);
}
///判断出是否在某点的半径为r圆范围内
bool judgeCircleArea(Offset src, Offset dst, double r) =>
(src - dst).distance <= r;
//判断哪个点被选中
void judgeZone(Offset src, Size size,{bool update = false}) {
src= src.translate(-size.width/2, -size.height/2);
for (int i = 0; i < touchInfo.points.length; i++) {
if (judgeCircleArea(src, touchInfo.points[i], 15)) {
touchInfo.selectIndex = i;
if (update) {
touchInfo.updatePoint(i, src);
}
}
}
}
}
class PaperPainter extends CustomPainter {
final Coordinate coordinate = Coordinate();
Paint _helpPaint = Paint()
..color = Colors.purple
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
final TouchInfo repaint;
PaperPainter({required this.repaint}) : super(repaint: repaint);
List<Offset> pos = [];
@override
void paint(Canvas canvas, Size size) {
coordinate.paint(canvas, size);
canvas.translate(size.width / 2, size.height / 2);
pos = repaint.points;
Path path = Path();
Paint paint = Paint()
..color = Colors.orange
..style = PaintingStyle.stroke
..strokeWidth = 2;
path.moveTo(0, 0);
for (int i = 0; i < pos.length / 3; i++) {
path.cubicTo(pos[3 * i + 0].dx, pos[3 * i + 0].dy, pos[3 * i + 1].dx,
pos[3 * i + 1].dy, pos[3 * i + 2].dx, pos[3 * i + 2].dy);
}
canvas.drawPath(path, paint);
_drawHelp(canvas);
_drawSelectPos(canvas, size);
}
void _drawHelp(Canvas canvas) {
_helpPaint..strokeWidth = 1..color=Colors.purple;
canvas.drawLine(pos[0], pos[11], _helpPaint);
canvas.drawLine(pos[1], pos[2], _helpPaint);
canvas.drawLine(pos[2], pos[3], _helpPaint);
canvas.drawLine(pos[4], pos[5], _helpPaint);
canvas.drawLine(pos[5], pos[6], _helpPaint);
canvas.drawLine(pos[7], pos[8], _helpPaint);
canvas.drawLine(pos[8], pos[9], _helpPaint);
canvas.drawLine(pos[10], pos[11], _helpPaint);
canvas.drawLine(pos[11], pos[0], _helpPaint);
canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8);
}
void _drawSelectPos(Canvas canvas, Size size) {
Offset? selectPos = repaint.selectPoint;
if (selectPos == null) return;
canvas.drawCircle(selectPos, 10,
_helpPaint..color = Colors.green..strokeWidth = 2);
}
@override
bool shouldRepaint(PaperPainter oldDelegate) =>
oldDelegate.repaint != repaint;
}

View File

@@ -0,0 +1,132 @@
import 'package:flutter/material.dart';
import '../components/coordinate_pro.dart';
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
// 使用CustomPaint
painter: PaperPainter(),
),
);
}
}
class PaperPainter extends CustomPainter {
final Coordinate coordinate = Coordinate();
List<Offset> points1 = [
Offset(0, 20),
Offset(40, 40) ,
Offset(80, -20),
Offset(120, -40),
Offset(160, -80),
Offset(200, -20),
Offset(240, -40),
];
List<Offset> points2 = [
Offset(0, 0),
Offset(40, -20) ,
Offset(80, -40),
Offset(120, -80),
Offset(160, -40),
Offset(200, 20),
Offset(240, 40),
];
List<Offset> helpPoints = [
];
Paint _helpPaint = Paint();
Path _linePath = Path();
@override
void paint(Canvas canvas, Size size) {
coordinate.paint(canvas, size);
canvas.translate(size.width / 2, size.height / 2);
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 1;
Path path = Path();
addBezierPathWithPoints(path, points2);
canvas.drawPath(path, paint);
addBezierPathWithPoints(_linePath, points1);
canvas.drawPath(_linePath, paint..color=Colors.orange);
_drawHelp(canvas);
}
void addBezierPathWithPoints(Path path, List<Offset> points) {
for (int i = 0; i < points.length - 1; i++) {
Offset current = points[i];
Offset next = points[i+1];
if (i == 0) {
path.moveTo(current.dx, current.dy);
// 控制点
double ctrlX = current.dx + (next.dx - current.dx) / 2;
double ctrlY = next.dy;
path.quadraticBezierTo(ctrlX, ctrlY, next.dx, next.dy);
} else if (i < points.length - 2) {
// 控制点 1
double ctrl1X = current.dx + (next.dx - current.dx) / 2;
double ctrl1Y = current.dy;
// 控制点 2
double ctrl2X = ctrl1X;
double ctrl2Y = next.dy;
path.cubicTo(ctrl1X,ctrl1Y,ctrl2X,ctrl2Y,next.dx,next.dy);
}else{
// 控制点
double ctrlX = (next.dx - current.dx) / 2;
double ctrlY = 0;
path.relativeQuadraticBezierTo(ctrlX, ctrlY, next.dx-current.dx, next.dy-current.dy);
}
}
}
void _drawHelp(Canvas canvas) {
_helpPaint
..style = PaintingStyle.stroke;
points1.forEach((element) {
canvas.drawCircle(element, 2, _helpPaint..strokeWidth=1..color=Colors.orange);
});
points2.forEach((element) {
canvas.drawCircle(element, 2, _helpPaint..strokeWidth=1..color=Colors.red);
});
// canvas.drawPoints(PointMode.polygon, points1, _helpPaint..strokeWidth=0.5..color=Colors.red);// _drawHelp(canvas);
// canvas.drawPoints(PointMode.polygon, points2, _helpPaint..strokeWidth=0.5..color=Colors.red);// _drawHelp(canvas);
// canvas.drawPoints(PointMode.polygon, helpPoints, _helpPaint..strokeWidth=0.5..color=Colors.red);// _drawHelp(canvas);
// _helpPaint
// ..color = Colors.purple
// ..style = PaintingStyle.stroke
// ..strokeCap = StrokeCap.round;
// canvas.drawPoints(PointMode.lines, [Offset.zero, p1, p1, p2],
// _helpPaint..strokeWidth = 1);
// canvas.drawPoints(PointMode.points, [Offset.zero, p1, p1, p2],
// _helpPaint..strokeWidth = 8);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/4
/// contact me by email 1981462002@qq.com
/// 说明:
class TouchInfo extends ChangeNotifier {
List<Offset> _points = [];
int _selectIndex = -1;
void setPoints(List<Offset> points) {
_points = points;
}
int get selectIndex => _selectIndex;
List<Offset> get points => _points;
set selectIndex(int value) {
assert(value != null);
if (_selectIndex == value) return;
_selectIndex = value;
notifyListeners();
}
void addPoint(Offset point) {
points.add(point);
notifyListeners();
}
void updatePoint(int index, Offset point) {
points[index] = point;
notifyListeners();
}
void reset() {
_points.clear();
_selectIndex = -1;
notifyListeners();
}
Offset? get selectPoint => _selectIndex == -1 ? null : _points[_selectIndex];
}

View File

@@ -0,0 +1 @@
export 'p15_page.dart';

View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:components/components.dart';
import 's01.dart' as s1;
import 's02.dart' as s2;
import 's03.dart' as s3;
import 's04.dart' as s4;
import 's05.dart' as s5;
import 's06.dart' as s6;
class P15Page extends StatelessWidget {
const P15Page({super.key});
@override
Widget build(BuildContext context) {
return const DemoShower(
srcCodeDir: 'draw/p15',
demos: [
s1.Paper(),
s2.Paper(),
s3.Paper(),
s4.Paper(),
s5.Paper(),
s6.Paper(),
],
);
}
}

View File

@@ -0,0 +1,68 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import '../components/coordinate_pro.dart';
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
// 使用CustomPaint
painter: PaperPainter(),
),
);
}
}
class PaperPainter extends CustomPainter {
final Coordinate coordinate = Coordinate();
Paint _helpPaint = Paint();
double waveWidth = 80;
double wrapHeight=0;
double waveHeight = 40;
@override
void paint(Canvas canvas, Size size) {
coordinate.paint(canvas, size);
canvas.translate(size.width / 2, size.height / 2);
Path path = Path();
Paint paint = Paint()
..color = Colors.orange
..style = PaintingStyle.fill
..strokeWidth = 2;
path.relativeQuadraticBezierTo( waveWidth / 2, -waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo( waveWidth / 2, waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo( waveWidth / 2, -waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo( waveWidth / 2, waveHeight * 2, waveWidth, 0);
canvas.drawPath(path, paint);
_drawHelp(canvas);
}
void _drawHelp(Canvas canvas) {
_helpPaint
..color = Colors.purple
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
canvas.drawPoints(PointMode.polygon,[Offset.zero, Offset(waveWidth / 2, -waveHeight * 2), Offset(waveWidth , 0)], _helpPaint..strokeWidth=1);
canvas.drawPoints(PointMode.points, [Offset.zero, Offset(waveWidth / 2, -waveHeight * 2), Offset(waveWidth , 0)],
_helpPaint..strokeWidth = 8);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,69 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import '../components/coordinate_pro.dart';
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
// 使用CustomPaint
painter: PaperPainter(),
),
);
}
}
class PaperPainter extends CustomPainter {
final Coordinate coordinate = Coordinate();
Paint _helpPaint = Paint();
double waveWidth = 80;
double wrapHeight =100;
double waveHeight = 40;
@override
void paint(Canvas canvas, Size size) {
coordinate.paint(canvas, size);
canvas.translate(size.width / 2, size.height / 2);
Path path = Path();
Paint paint = Paint()
..color = Colors.orange
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.save();
canvas.translate(-2*waveWidth, 0);
path.moveTo(0, 0);
path.relativeQuadraticBezierTo(waveWidth/2, -waveHeight*2, waveWidth, 0);
path.relativeQuadraticBezierTo(waveWidth/2, waveHeight*2, waveWidth, 0);
path.relativeQuadraticBezierTo(waveWidth/2, -waveHeight*2, waveWidth, 0);
path.relativeQuadraticBezierTo(waveWidth/2, waveHeight*2, waveWidth, 0);
path.close();
canvas.drawPath(path, paint..style=PaintingStyle.fill);
canvas.restore();
_drawHelp(canvas);
}
void _drawHelp(Canvas canvas) {
_helpPaint
..color = Colors.purple
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
canvas.drawRect(Rect.fromPoints(Offset(0,-100), Offset(160,100)), _helpPaint..strokeWidth=2);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import '../components/coordinate_pro.dart';
class Paper extends StatefulWidget {
const Paper({super.key});
@override
_PaperState createState() => _PaperState();
}
class _PaperState extends State<Paper> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
painter: PaperPainter(_controller),
),
);
}
}
class PaperPainter extends CustomPainter {
final Coordinate coordinate = Coordinate();
Paint _helpPaint = Paint();
final Animation<double> repaint;
PaperPainter(this.repaint) : super(repaint: repaint);
double waveWidth = 80;
double wrapHeight = 100;
double waveHeight = 20;
@override
void paint(Canvas canvas, Size size) {
coordinate.paint(canvas, size);
canvas.translate(size.width / 2, size.height / 2);
Path path = Path();
Paint paint = Paint()
..color = Colors.orange
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.save();
// canvas.translate(-2*waveWidth, 0);
canvas.translate(-2 * waveWidth + 2 * waveWidth * repaint.value, 0);
path.moveTo(0, 0);
path.relativeQuadraticBezierTo(
waveWidth / 2, -waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo(waveWidth / 2, waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo(
waveWidth / 2, -waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo(waveWidth / 2, waveHeight * 2, waveWidth, 0);
path.relativeLineTo(0, wrapHeight);
path.relativeLineTo(-waveWidth * 2 * 2.0, 0);
path.close();
canvas.drawPath(path, paint..style = PaintingStyle.fill);
canvas.restore();
_drawHelp(canvas);
}
void _drawHelp(Canvas canvas) {
_helpPaint
..color = Colors.purple
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
canvas.drawRect(Rect.fromPoints(Offset(0, -100), Offset(160, 100)),
_helpPaint..strokeWidth = 2);
}
@override
bool shouldRepaint(PaperPainter oldDelegate) =>
oldDelegate.repaint != repaint;
}

View File

@@ -0,0 +1,106 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import '../components/coordinate_pro.dart';
import 'touch_info.dart';
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatefulWidget {
const Paper({super.key});
@override
_PaperState createState() => _PaperState();
}
class _PaperState extends State<Paper> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
painter: PaperPainter(CurveTween(curve: Curves.easeInOutQuad).animate(_controller)),
),
);
}
}
class PaperPainter extends CustomPainter {
final Coordinate coordinate = Coordinate();
Paint _helpPaint = Paint();
final Animation<double> repaint;
PaperPainter(this.repaint) : super(repaint: repaint);
double waveWidth = 80;
double wrapHeight = 100;
double waveHeight = 20;
@override
void paint(Canvas canvas, Size size) {
coordinate.paint(canvas, size);
canvas.translate(size.width / 2, size.height / 2);
canvas.clipRect((Rect.fromCenter(
center: Offset( waveWidth, 0),width: waveWidth*2,height: 200.0)));
Path path = Path();
Paint paint = Paint()
..color = Colors.orange
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.save();
// canvas.translate(-2*waveWidth, 0);
canvas.translate(-2 * waveWidth + 2 * waveWidth * repaint.value, 0);
path.moveTo(0, 0);
path.relativeQuadraticBezierTo(
waveWidth / 2, -waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo(waveWidth / 2, waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo(
waveWidth / 2, -waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo(waveWidth / 2, waveHeight * 2, waveWidth, 0);
path.relativeLineTo(0, wrapHeight);
path.relativeLineTo(-waveWidth * 2 * 2.0, 0);
path.close();
canvas.drawPath(path, paint..style = PaintingStyle.fill);
canvas.restore();
_drawHelp(canvas);
}
void _drawHelp(Canvas canvas) {
_helpPaint
..color = Colors.purple
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
canvas.drawRect(Rect.fromPoints(Offset(0, -100), Offset(160, 100)),
_helpPaint..strokeWidth = 2);
}
@override
bool shouldRepaint(PaperPainter oldDelegate) =>
oldDelegate.repaint != repaint;
}

View File

@@ -0,0 +1,97 @@
import 'package:flutter/material.dart';
import '../components/coordinate_pro.dart';
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatefulWidget {
const Paper({super.key});
@override
_PaperState createState() => _PaperState();
}
class _PaperState extends State<Paper> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
)
..repeat()
;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: CustomPaint(
painter: PaperPainter(
CurveTween(curve: Curves.linear).animate(_controller)),
),
);
}
}
class PaperPainter extends CustomPainter {
final Animation<double> repaint;
PaperPainter(this.repaint) : super(repaint: repaint);
double waveWidth = 80;
double wrapHeight = 100;
double waveHeight = 10;
@override
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
canvas.clipRect((Rect.fromCenter(
center: Offset(waveWidth, 0), width: waveWidth * 2, height: 200.0)));
Paint paint = Paint();
Path path = getWavePath();
canvas.translate(-4 * waveWidth + 2 * waveWidth * repaint.value, 0);
canvas.drawPath(path, paint..color = Colors.orange);
canvas.translate(2*waveWidth* repaint.value, 0);
canvas.drawPath(path, paint..color = Colors.orange.withAlpha(88));
}
Path getWavePath() {
Path path = Path();
path.relativeQuadraticBezierTo(
waveWidth / 2, -waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo(
waveWidth / 2, waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo(
waveWidth / 2, -waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo(
waveWidth / 2, waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo(
waveWidth / 2, -waveHeight * 2, waveWidth, 0);
path.relativeQuadraticBezierTo(
waveWidth / 2, waveHeight * 2, waveWidth, 0);
path.relativeLineTo(0, wrapHeight);
path.relativeLineTo(-waveWidth * 3 * 2.0, 0);
return path;
}
@override
bool shouldRepaint(PaperPainter oldDelegate) =>
oldDelegate.repaint != repaint;
}

View File

@@ -0,0 +1,192 @@
import 'package:flutter/material.dart';
import '../components/coordinate_pro.dart';
/// create by 张风捷特烈 on 2020/5/1
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Center(child: Wrap(
spacing: 30,
runSpacing: 30,
children: List.generate(12, (v) => 0.1 * v+0.1)
.map((e) => TolyWaveLoading(
isOval: (e*10).toInt().isEven, // 是否椭圆裁切
progress: 0.5, // 进度
waveHeight: 3, //波浪高
color: [Colors.blue,Colors.red,Colors.green][(e*10).toInt()%3], //颜色
)).toList(),
),
);
}
}
class TolyWaveLoading extends StatefulWidget {
// 波高
final double waveHeight;
// 进度
final double progress;
// 颜色
final Color color;
// 尺寸
final Size size;
// 底波透明度
final int secondAlpha;
// 边线宽
final double strokeWidth;
// 圆角半径
final double borderRadius;
// 是否是椭圆
final bool isOval;
final Duration duration;
final Curve curve;
const TolyWaveLoading(
{Key? key,
this.waveHeight = 5,
this.progress = 0.5,
this.duration = const Duration(seconds: 1),
this.size = const Size(100, 100),
this.color = Colors.green,
this.secondAlpha = 88,
this.strokeWidth = 3,
this.curve = Curves.linear,
this.borderRadius = 20,
this.isOval = false})
: super(key: key);
@override
_TolyWaveLoadingState createState() => _TolyWaveLoadingState();
}
class _TolyWaveLoadingState extends State<TolyWaveLoading>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: widget.duration,
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return CustomPaint(
size: widget.size,
painter: PaperPainter(
waveHeight: widget.waveHeight,
secondAlpha: widget.secondAlpha,
color: widget.color,
borderRadius: widget.borderRadius,
isOval: widget.isOval,
progress: widget.progress,
strokeWidth: widget.strokeWidth,
repaint: CurveTween(curve: widget.curve).animate(_controller)),
);
}
}
class PaperPainter extends CustomPainter {
final Animation<double> repaint;
PaperPainter({
required this.repaint,
required this.waveHeight,
required this.color,
required this.progress,
required this.secondAlpha,
required this.borderRadius,
required this.isOval,
required this.strokeWidth,
}) : super(repaint: repaint);
final double waveHeight;
final double progress;
final Color color;
final double strokeWidth;
final int secondAlpha;
final double borderRadius;
final bool isOval;
Path path = Path();
Paint _mainPaint = Paint();
Path _mainPath = Path();
double waveWidth = 0;
double wrapHeight = 0;
@override
void paint(Canvas canvas, Size size) {
// 由于 使用 repaint 触发更新 path 和 _mainPath 不会重新创建
// 而导致刷新时不断为 path 和 _mainPath 添加路径,导致路径非常庞大,越来越卡,而掉帧
// 处理方案一: 绘制前 重置 path 或者
// 处理方案二: 不将 path 设为成员变量,每次绘制都新建 Path 对象,这样会造成大量 Path 对象创建,测试发现两种方案结果差不多。
path.reset();
_mainPath.reset();
waveWidth = size.width / 2;
wrapHeight = size.height;
_mainPaint..strokeWidth = strokeWidth..style = PaintingStyle.stroke..color = color;
if (!isOval) {
path.addRRect(RRect.fromRectXY(Offset(0, 0) & size, borderRadius, borderRadius));
canvas.clipPath(path);
canvas.drawPath(path,_mainPaint);
}
if (isOval) {
path.addOval(Offset(0, 0) & size);
canvas.clipPath(path);
canvas.drawPath(path, _mainPaint);
}
canvas.translate(-4 * waveWidth + 2 * waveWidth * repaint.value, wrapHeight + waveHeight);
drawWave(canvas);
canvas.drawPath(_mainPath, _mainPaint..style = PaintingStyle.fill..color = color);
canvas.translate(2 * waveWidth * repaint.value, 0);
drawWave(canvas);
canvas.drawPath(_mainPath, _mainPaint..color = color.withAlpha(88));
}
void drawWave(Canvas canvas) {
_mainPath.moveTo(0, 0);
_mainPath.relativeLineTo(0, -wrapHeight * progress);
_mainPath.relativeQuadraticBezierTo(waveWidth / 2, -waveHeight * 2, waveWidth, 0);
_mainPath.relativeQuadraticBezierTo(waveWidth / 2, waveHeight * 2, waveWidth, 0);
_mainPath.relativeQuadraticBezierTo(waveWidth / 2, -waveHeight * 2, waveWidth, 0);
_mainPath.relativeQuadraticBezierTo(waveWidth / 2, waveHeight * 2, waveWidth, 0);
_mainPath.relativeQuadraticBezierTo(waveWidth / 2, -waveHeight * 2, waveWidth, 0);
_mainPath.relativeQuadraticBezierTo(waveWidth / 2, waveHeight * 2, waveWidth, 0);
_mainPath.relativeLineTo(0, wrapHeight);
_mainPath.relativeLineTo(-waveWidth * 3 * 2.0, 0);
}
@override
bool shouldRepaint(PaperPainter oldDelegate) =>
oldDelegate.repaint != repaint|| oldDelegate.waveHeight != waveHeight||
oldDelegate.progress != progress|| oldDelegate.color != color||
oldDelegate.strokeWidth != strokeWidth|| oldDelegate.isOval != isOval||
oldDelegate.secondAlpha != secondAlpha|| oldDelegate.borderRadius != borderRadius;
}

View File

@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/4
/// contact me by email 1981462002@qq.com
/// 说明:
class TouchInfo extends ChangeNotifier {
List<Offset> _points = [];
int _selectIndex = -1;
void setPoints(List<Offset> points) {
_points = points;
}
int get selectIndex => _selectIndex;
List<Offset> get points => _points;
set selectIndex(int value) {
assert(value != null);
if (_selectIndex == value) return;
_selectIndex = value;
notifyListeners();
}
void addPoint(Offset point) {
points.add(point);
notifyListeners();
}
void updatePoint(int index, Offset point) {
points[index] = point;
notifyListeners();
}
void reset() {
_points.clear();
_selectIndex = -1;
notifyListeners();
}
Offset? get selectPoint => _selectIndex == -1 ? null : _points[_selectIndex];
}

View File

@@ -0,0 +1 @@
export 'p16_page.dart';

View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:components/components.dart';
import 's01.dart' as s1;
import 's02.dart' as s2;
import 's03.dart' as s3;
import 's04.dart' as s4;
import 's05.dart' as s5;
import 's06.dart' as s6;
class P16Page extends StatelessWidget {
const P16Page({super.key});
@override
Widget build(BuildContext context) {
return const DemoShower(
srcCodeDir: 'draw/p16',
demos: [
s1.Paper(),
s2.Paper(),
s3.Paper(),
s4.Paper(),
s5.Paper(),
s6.Paper(),
],
);
}
}

View File

@@ -0,0 +1,172 @@
import 'dart:math';
// import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const ICharts(),
);
}
}
class ICharts extends StatelessWidget {
const ICharts({super.key});
@override
Widget build(BuildContext context) {
return Stack(alignment: Alignment.topCenter, children: [
Container(
width: 350,
height: 250,
padding: EdgeInsets.only(top: 40, right: 20, bottom: 20, left: 20),
child: CustomPaint(
painter: ChartPainter(),
),
),
Padding(
padding: const EdgeInsets.only(top:8.0),
child: Text(
"捷特数学成绩统计图 - 2040 年",
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
),
)
]);
}
}
const double _kScaleHeight = 8; // 刻度高
const double _kBarPadding = 10; // 柱状图前间隔
class ChartPainter extends CustomPainter {
final TextPainter _textPainter =
TextPainter(textDirection: TextDirection.ltr);
final List<double> yData = [88, 98, 70, 80, 100, 75];
final List<String> xData = ["7月", "8月", "9月", "10月", "11月", "12月"];
// final List<String> xData = [ "语文","数学", "英语", "物理", "化学", "生物"];
Path axisPath = Path();
Paint axisPaint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 1;
Paint gridPaint = Paint()
..style = PaintingStyle.stroke
..color = Colors.grey
..strokeWidth = 0.5;
Paint fillPaint = Paint()..color = Colors.red;
double xStep = 0; // x 间隔
double yStep = 0; // y 间隔
double maxData = 0; // 数据最大值
ChartPainter() {
maxData = yData.reduce(max);
}
@override
void paint(Canvas canvas, Size size) {
// canvas.drawRect(
// Offset.zero & size, Paint()..color = Colors.black.withAlpha(22));
canvas.translate(0, size.height);
canvas.translate(_kScaleHeight, -_kScaleHeight);
axisPath.moveTo(-_kScaleHeight, 0);
axisPath.relativeLineTo(size.width, 0);
axisPath.moveTo(0, _kScaleHeight);
axisPath.relativeLineTo(0, -size.height);
canvas.drawPath(axisPath, axisPaint);
drawYText(canvas, size);
drawXText(canvas, size);
drawBarChart(canvas, size);
}
void drawXText(Canvas canvas, Size size) {
xStep = (size.width - _kScaleHeight) / xData.length;
canvas.save();
canvas.translate(xStep, 0);
for (int i = 0; i < xData.length; i++) {
canvas.drawLine(Offset(0, 0), Offset(0, _kScaleHeight), axisPaint);
_drawAxisText(canvas, xData[i],
alignment: Alignment.center, offset: Offset(-xStep / 2, 10));
canvas.translate(xStep, 0);
}
canvas.restore();
}
void drawBarChart(Canvas canvas, Size size) {
canvas.save();
canvas.translate(xStep, 0);
for (int i = 0; i < xData.length; i++) {
canvas.drawRect(
Rect.fromLTWH(_kBarPadding, 0, xStep - 2 * _kBarPadding,
-(yData[i] / maxData * (size.height - _kScaleHeight)))
.translate(-xStep, 0),
fillPaint);
canvas.translate(xStep, 0);
}
canvas.restore();
}
void drawYText(Canvas canvas, Size size) {
canvas.save();
yStep = (size.height - _kScaleHeight) / 5;
double numStep = maxData / 5;
for (int i = 0; i <= 5; i++) {
if (i == 0) {
_drawAxisText(canvas, '0', offset: Offset(-10, 2));
canvas.translate(0, -yStep);
continue;
}
canvas.drawLine(
Offset(0, 0), Offset(size.width - _kScaleHeight, 0), gridPaint);
canvas.drawLine(Offset(-_kScaleHeight, 0), Offset(0, 0), axisPaint);
String str = '${(numStep * i).toStringAsFixed(0)}';
_drawAxisText(canvas, str, offset: Offset(-10, 2));
canvas.translate(0, -yStep);
}
canvas.restore();
}
void _drawAxisText(Canvas canvas, String str,
{Color color = Colors.black,
bool x = false,
Alignment alignment = Alignment.centerRight,
Offset offset = Offset.zero}) {
TextSpan text = TextSpan(
text: str,
style: TextStyle(
fontSize: 11,
color: color,
));
_textPainter.text = text;
_textPainter.layout(); // 进行布局
Size size = _textPainter.size;
Offset offsetPos = Offset(-size.width / 2, -size.height / 2)
.translate(-size.width / 2 * alignment.x + offset.dx, 0.0 + offset.dy);
_textPainter.paint(canvas, offsetPos);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,197 @@
import 'dart:math';
// import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const ICharts(),
);
}
}
class ICharts extends StatefulWidget {
const ICharts({super.key});
@override
_IChartsState createState() => _IChartsState();
}
class _IChartsState extends State<ICharts> with SingleTickerProviderStateMixin{
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
)..forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(alignment: Alignment.topCenter, children: [
Container(
width: 350,
height: 250,
padding: EdgeInsets.only(top: 40, right: 20, bottom: 20, left: 20),
child: CustomPaint(
painter: ChartPainter(repaint: _controller),
),
),
Padding(
padding: const EdgeInsets.only(top:8.0),
child: Text(
"捷特数学成绩统计图 - 2040 年",
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
),
)
]);
}
}
const double _kScaleHeight = 8; // 刻度高
const double _kBarPadding = 10; // 柱状图前间隔
class ChartPainter extends CustomPainter {
final TextPainter _textPainter =
TextPainter(textDirection: TextDirection.ltr);
final List<double> yData = [88, 98, 70, 80, 100, 75];
final List<String> xData = ["7月", "8月", "9月", "10月", "11月", "12月"];
// final List<String> xData = [ "语文","数学", "英语", "物理", "化学", "生物"];
final Animation<double> repaint;
Path axisPath = Path();
Paint axisPaint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 1;
Paint gridPaint = Paint()
..style = PaintingStyle.stroke
..color = Colors.grey
..strokeWidth = 0.5;
Paint fillPaint = Paint()..color = Colors.red;
double xStep = 0; // x 间隔
double yStep = 0; // y 间隔
double maxData = 0; // 数据最大值
ChartPainter({required this.repaint}):super(repaint: repaint) {
maxData = yData.reduce(max);
}
@override
void paint(Canvas canvas, Size size) {
canvas.translate(0, size.height);
canvas.translate(_kScaleHeight, -_kScaleHeight);
axisPath.moveTo(-_kScaleHeight, 0);
axisPath.relativeLineTo(size.width, 0);
axisPath.moveTo(0, _kScaleHeight);
axisPath.relativeLineTo(0, -size.height);
canvas.drawPath(axisPath, axisPaint);
drawYText(canvas, size);
drawXText(canvas, size);
drawBarChart(canvas,size);
}
void drawXText(Canvas canvas, Size size) {
xStep = (size.width - _kScaleHeight) / xData.length;
canvas.save();
canvas.translate(xStep, 0);
for (int i = 0; i < xData.length; i++) {
canvas.drawLine(Offset(0, 0), Offset(0, _kScaleHeight), axisPaint);
_drawAxisText(canvas, xData[i],
alignment: Alignment.center, offset: Offset(-xStep / 2, 10));
canvas.translate(xStep, 0);
}
canvas.restore();
}
void drawBarChart(Canvas canvas, Size size) {
canvas.save();
canvas.translate(xStep, 0);
for (int i = 0; i < xData.length; i++) {
double dataHeight = -(yData[i] / maxData * (size.height - _kScaleHeight));
canvas.drawRect(
Rect.fromLTWH(
_kBarPadding, 0, xStep-2*_kBarPadding, dataHeight*repaint.value)
.translate(-xStep, 0),
fillPaint);
canvas.translate(xStep, 0);
}
canvas.restore();
}
void drawYText(Canvas canvas, Size size) {
canvas.save();
yStep = (size.height - _kScaleHeight) / 5;
double numStep = maxData / 5;
for (int i = 0; i <= 5; i++) {
if (i == 0) {
_drawAxisText(canvas, '0', offset: Offset(-10, 2));
canvas.translate(0, -yStep);
continue;
}
canvas.drawLine(
Offset(0, 0), Offset(size.width - _kScaleHeight, 0), gridPaint);
canvas.drawLine(Offset(-_kScaleHeight, 0), Offset(0, 0), axisPaint);
String str = '${(numStep * i).toStringAsFixed(0)}';
_drawAxisText(canvas, str, offset: Offset(-10, 2));
canvas.translate(0, -yStep);
}
canvas.restore();
}
void _drawAxisText(Canvas canvas, String str,
{Color color = Colors.black,
bool x = false,
Alignment alignment = Alignment.centerRight,
Offset offset = Offset.zero}) {
TextSpan text = TextSpan(
text: str,
style: TextStyle(
fontSize: 11,
color: color,
));
_textPainter.text = text;
_textPainter.layout(); // 进行布局
Size size = _textPainter.size;
Offset offsetPos = Offset(-size.width / 2, -size.height / 2)
.translate(-size.width / 2 * alignment.x + offset.dx, 0.0 + offset.dy);
_textPainter.paint(canvas, offsetPos);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,215 @@
import 'dart:math';
import 'dart:ui';
// import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const ICharts(),
);
}
}
class ICharts extends StatefulWidget {
const ICharts({super.key});
@override
_IChartsState createState() => _IChartsState();
}
class _IChartsState extends State<ICharts> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
)
..forward()
;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(alignment: Alignment.topCenter, children: [
Container(
width: 350,
height: 250,
padding: EdgeInsets.only(top: 40, right: 20, bottom: 20, left: 20),
child: CustomPaint(
painter: ChartPainter(repaint: _controller),
),
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
"捷特数学成绩统计图 - 2040 年",
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
),
)
]);
}
}
const double _kScaleHeight = 8; // 刻度高
const double _kBarPadding = 10; // 柱状图前间隔
class ChartPainter extends CustomPainter {
final TextPainter _textPainter =
TextPainter(textDirection: TextDirection.ltr);
final List<double> yData = [88, 98, 70, 80, 100, 75];
final List<String> xData = ["7月", "8月", "9月", "10月", "11月", "12月"];
// final List<String> xData = [ "语文","数学", "英语", "物理", "化学", "生物"];
final Animation<double> repaint;
Path axisPath = Path();
Paint axisPaint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 1;
Paint gridPaint = Paint()
..style = PaintingStyle.stroke
..color = Colors.grey
..strokeWidth = 0.5;
Paint fillPaint = Paint()..color = Colors.red;
Paint linePaint = Paint()
..color = Colors.red
..strokeCap=StrokeCap.round
..style = PaintingStyle.stroke;
double xStep = 0; // x 间隔
double yStep = 0; // y 间隔
double maxData = 0; // 数据最大值
final List<Offset> line = []; // 折线点位信息
ChartPainter({required this.repaint}) : super(repaint: repaint) {
maxData = yData.reduce(max);
}
@override
void paint(Canvas canvas, Size size) {
xStep = (size.width - _kScaleHeight) / xData.length;
yStep = (size.height - _kScaleHeight) / 5;
canvas.translate(0, size.height);
canvas.translate(_kScaleHeight, -_kScaleHeight);
axisPath.moveTo(-_kScaleHeight, 0);
axisPath.relativeLineTo(size.width, 0);
axisPath.moveTo(0, _kScaleHeight);
axisPath.relativeLineTo(0, -size.height);
canvas.drawPath(axisPath, axisPaint);
drawYText(canvas, size);
drawXText(canvas, size);
collectPoints(canvas, size);
drawLineChart(canvas);
}
void drawXText(Canvas canvas, Size size) {
canvas.save();
canvas.translate(xStep, 0);
for (int i = 0; i < xData.length; i++) {
canvas.drawLine(Offset(0, 0), Offset(0, _kScaleHeight), axisPaint);
_drawAxisText(canvas, xData[i],
alignment: Alignment.center, offset: Offset(-xStep / 2, 10));
canvas.translate(xStep, 0);
}
canvas.restore();
}
void collectPoints(Canvas canvas, Size size) {
line.clear();
canvas.translate(xStep, 0);
for (int i = 0; i < xData.length; i++) {
double dataHeight = -(yData[i] / maxData * (size.height - _kScaleHeight));
line.add(Offset(xStep * i-xStep/2, dataHeight));
}
}
void drawLineChart(Canvas canvas){
canvas.drawPoints(PointMode.points, line, linePaint..strokeWidth=5);
Offset p1 = line[0];
Path path = Path()..moveTo(p1.dx, p1.dy);
for (var i = 1; i < line.length; i++) {
path.lineTo(line[i].dx, line[i].dy);
}
linePaint..strokeWidth=1;
PathMetrics pms = path.computeMetrics();
pms.forEach((pm) {
canvas.drawPath(pm.extractPath(0, pm.length * repaint.value), linePaint);
});
}
void drawYText(Canvas canvas, Size size) {
canvas.save();
double numStep = maxData / 5;
for (int i = 0; i <= 5; i++) {
if (i == 0) {
_drawAxisText(canvas, '0', offset: Offset(-10, 2));
canvas.translate(0, -yStep);
continue;
}
canvas.drawLine(
Offset(0, 0), Offset(size.width - _kScaleHeight, 0), gridPaint);
canvas.drawLine(Offset(-_kScaleHeight, 0), Offset(0, 0), axisPaint);
String str = '${(numStep * i).toStringAsFixed(0)}';
_drawAxisText(canvas, str, offset: Offset(-10, 2));
canvas.translate(0, -yStep);
}
canvas.restore();
}
void _drawAxisText(Canvas canvas, String str,
{Color color = Colors.black,
bool x = false,
Alignment alignment = Alignment.centerRight,
Offset offset = Offset.zero}) {
TextSpan text = TextSpan(
text: str,
style: TextStyle(
fontSize: 11,
color: color,
));
_textPainter.text = text;
_textPainter.layout(); // 进行布局
Size size = _textPainter.size;
Offset offsetPos = Offset(-size.width / 2, -size.height / 2)
.translate(-size.width / 2 * alignment.x + offset.dx, 0.0 + offset.dy);
_textPainter.paint(canvas, offsetPos);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,237 @@
import 'dart:math';
import 'dart:ui';
// import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const ICharts(),
);
}
}
class ICharts extends StatefulWidget {
const ICharts({super.key});
@override
_IChartsState createState() => _IChartsState();
}
class _IChartsState extends State<ICharts> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
)
..forward()
;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(alignment: Alignment.topCenter, children: [
Container(
width: 350,
height: 250,
padding: EdgeInsets.only(top: 40, right: 20, bottom: 20, left: 20),
child: CustomPaint(
painter: ChartPainter(repaint: _controller),
),
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
"捷特数学成绩统计图 - 2040 年",
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
),
)
]);
}
}
const double _kScaleHeight = 8; // 刻度高
const double _kBarPadding = 10; // 柱状图前间隔
class ChartPainter extends CustomPainter {
final TextPainter _textPainter =
TextPainter(textDirection: TextDirection.ltr);
final List<double> yData = [88, 98, 70, 80, 100, 75];
final List<String> xData = ["7月", "8月", "9月", "10月", "11月", "12月"];
// final List<String> xData = [ "语文","数学", "英语", "物理", "化学", "生物"];
final Animation<double> repaint;
Path axisPath = Path();
Paint axisPaint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 1;
Paint gridPaint = Paint()
..style = PaintingStyle.stroke
..color = Colors.grey
..strokeWidth = 0.5;
Paint fillPaint = Paint()..color = Colors.red;
Paint linePaint = Paint()
..color = Colors.orange
..strokeCap=StrokeCap.round
..style = PaintingStyle.stroke;
double xStep = 0; // x 间隔
double yStep = 0; // y 间隔
double maxData = 0; // 数据最大值
final List<Offset> line = []; // 折线点位信息
ChartPainter({required this.repaint}) : super(repaint: repaint) {
maxData = yData.reduce(max);
}
@override
void paint(Canvas canvas, Size size) {
xStep = (size.width - _kScaleHeight) / xData.length;
yStep = (size.height - _kScaleHeight) / 5;
canvas.translate(0, size.height);
canvas.translate(_kScaleHeight, -_kScaleHeight);
axisPath.moveTo(-_kScaleHeight, 0);
axisPath.relativeLineTo(size.width, 0);
axisPath.moveTo(0, _kScaleHeight);
axisPath.relativeLineTo(0, -size.height);
canvas.drawPath(axisPath, axisPaint);
drawYText(canvas, size);
drawXText(canvas, size);
collectPoints(canvas, size);
drawLineChart(canvas);
}
void drawXText(Canvas canvas, Size size) {
canvas.save();
canvas.translate(xStep, 0);
for (int i = 0; i < xData.length; i++) {
canvas.drawLine(Offset(0, 0), Offset(0, _kScaleHeight), axisPaint);
_drawAxisText(canvas, xData[i],
alignment: Alignment.center, offset: Offset(-xStep / 2, 10));
canvas.translate(xStep, 0);
}
canvas.restore();
}
void collectPoints(Canvas canvas, Size size) {
line.clear();
canvas.translate(xStep, 0);
for (int i = 0; i < xData.length; i++) {
double dataHeight = -(yData[i] / maxData * (size.height - _kScaleHeight));
line.add(Offset(xStep * i-xStep/2, dataHeight));
}
}
void drawLineChart(Canvas canvas){
canvas.drawPoints(PointMode.points, line, linePaint..strokeWidth=5);
Path path = Path();
addBezierPathWithPoints(path, line);
linePaint..strokeWidth=1;
PathMetrics pms = path.computeMetrics();
pms.forEach((pm) {
canvas.drawPath(pm.extractPath(0, pm.length * repaint.value), linePaint);
});
}
void drawYText(Canvas canvas, Size size) {
canvas.save();
double numStep = maxData / 5;
for (int i = 0; i <= 5; i++) {
if (i == 0) {
_drawAxisText(canvas, '0', offset: Offset(-10, 2));
canvas.translate(0, -yStep);
continue;
}
canvas.drawLine(
Offset(0, 0), Offset(size.width - _kScaleHeight, 0), gridPaint);
canvas.drawLine(Offset(-_kScaleHeight, 0), Offset(0, 0), axisPaint);
String str = '${(numStep * i).toStringAsFixed(0)}';
_drawAxisText(canvas, str, offset: Offset(-10, 2));
canvas.translate(0, -yStep);
}
canvas.restore();
}
void addBezierPathWithPoints(Path path, List<Offset> points) {
for (int i = 0; i < points.length - 1; i++) {
Offset current = points[i];
Offset next = points[i+1];
if (i == 0) {
path.moveTo(current.dx, current.dy);
// 控制点
double ctrlX = current.dx + (next.dx - current.dx) / 2;
double ctrlY = next.dy;
path.quadraticBezierTo(ctrlX, ctrlY, next.dx, next.dy);
} else if (i < points.length - 2) {
// 控制点 1
double ctrl1X = current.dx + (next.dx - current.dx) / 2;
double ctrl1Y = current.dy;
// 控制点 2
double ctrl2X = ctrl1X;
double ctrl2Y = next.dy;
path.cubicTo(ctrl1X,ctrl1Y,ctrl2X,ctrl2Y,next.dx,next.dy);
}else{
// 控制点
double ctrlX = (next.dx - current.dx) / 2;
double ctrlY = 0;
path.relativeQuadraticBezierTo(ctrlX, ctrlY, next.dx-current.dx, next.dy-current.dy);
}
}
}
void _drawAxisText(Canvas canvas, String str,
{Color color = Colors.black,
bool x = false,
Alignment alignment = Alignment.centerRight,
Offset offset = Offset.zero}) {
TextSpan text = TextSpan(
text: str,
style: TextStyle(
fontSize: 11,
color: color,
));
_textPainter.text = text;
_textPainter.layout(); // 进行布局
Size size = _textPainter.size;
Offset offsetPos = Offset(-size.width / 2, -size.height / 2)
.translate(-size.width / 2 * alignment.x + offset.dx, 0.0 + offset.dy);
_textPainter.paint(canvas, offsetPos);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,232 @@
import 'dart:math';
import 'dart:ui';
// import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const ICharts(),
);
}
}
class ICharts extends StatefulWidget {
const ICharts({super.key});
@override
_IChartsState createState() => _IChartsState();
}
class _IChartsState extends State<ICharts> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
)
..forward()
;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(alignment: Alignment.topCenter, children: [
Container(
width: 350,
height: 250,
padding: EdgeInsets.only(top: 40, right: 20, bottom: 20, left: 20),
child: CustomPaint(
painter: ChartPainter(repaint: _controller),
),
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
"捷特数学成绩统计图 - 2040 年",
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
),
)
]);
}
}
const double _kScaleHeight = 8; // 刻度高
const double _kBarPadding = 10; // 柱状图前间隔
class ChartPainter extends CustomPainter {
final TextPainter _textPainter =
TextPainter(textDirection: TextDirection.ltr);
final List<double> yData = [88, 98, 70, 80, 100, 75];
final List<String> xData = ["7月", "8月", "9月", "10月", "11月", "12月"];
// final List<String> xData = [ "语文","数学", "英语", "物理", "化学", "生物"];
final Animation<double> repaint;
Path axisPath = Path();
Paint axisPaint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 1;
Paint gridPaint = Paint()
..style = PaintingStyle.stroke
..color = Colors.grey
..strokeWidth = 0.5;
Paint fillPaint = Paint()..color = Colors.red;
Paint linePaint = Paint()
..color = Colors.red
..strokeCap=StrokeCap.round
..style = PaintingStyle.stroke;
double xStep = 0; // x 间隔
double yStep = 0; // y 间隔
double maxData = 0; // 数据最大值
final List<Offset> line = []; // 折线点位信息
ChartPainter({required this.repaint}) : super(repaint: repaint) {
maxData = yData.reduce(max);
}
@override
void paint(Canvas canvas, Size size) {
xStep = (size.width - _kScaleHeight) / xData.length;
yStep = (size.height - _kScaleHeight) / 5;
canvas.translate(0, size.height);
canvas.translate(_kScaleHeight, -_kScaleHeight);
axisPath.moveTo(-_kScaleHeight, 0);
axisPath.relativeLineTo(size.width, 0);
axisPath.moveTo(0, _kScaleHeight);
axisPath.relativeLineTo(0, -size.height);
canvas.drawPath(axisPath, axisPaint);
drawYText(canvas, size);
drawXText(canvas, size);
drawBarChart(canvas,size);
collectPoints(canvas, size);
drawLineChart(canvas);
}
void drawXText(Canvas canvas, Size size) {
canvas.save();
canvas.translate(xStep, 0);
for (int i = 0; i < xData.length; i++) {
canvas.drawLine(Offset(0, 0), Offset(0, _kScaleHeight), axisPaint);
_drawAxisText(canvas, xData[i],
alignment: Alignment.center, offset: Offset(-xStep / 2, 10));
canvas.translate(xStep, 0);
}
canvas.restore();
}
void collectPoints(Canvas canvas, Size size) {
line.clear();
canvas.translate(xStep, 0);
for (int i = 0; i < xData.length; i++) {
double dataHeight = -(yData[i] / maxData * (size.height - _kScaleHeight));
line.add(Offset(xStep * i-xStep/2, dataHeight));
}
}
void drawBarChart(Canvas canvas, Size size) {
fillPaint..color=Colors.lightBlue;
canvas.save();
canvas.translate(xStep, 0);
for (int i = 0; i < xData.length; i++) {
double dataHeight = -(yData[i] / maxData * (size.height - _kScaleHeight));
canvas.drawRect(
Rect.fromLTWH(
_kBarPadding, 0, xStep-2*_kBarPadding, dataHeight*repaint.value)
.translate(-xStep, 0),
fillPaint);
canvas.translate(xStep, 0);
}
canvas.restore();
}
void drawLineChart(Canvas canvas){
canvas.drawPoints(PointMode.points, line, linePaint..strokeWidth=5);
Offset p1 = line[0];
Path path = Path()..moveTo(p1.dx, p1.dy);
for (var i = 1; i < line.length; i++) {
path.lineTo(line[i].dx, line[i].dy);
}
linePaint..strokeWidth=1;
PathMetrics pms = path.computeMetrics();
pms.forEach((pm) {
canvas.drawPath(pm.extractPath(0, pm.length * repaint.value), linePaint);
});
}
void drawYText(Canvas canvas, Size size) {
canvas.save();
double numStep = maxData / 5;
for (int i = 0; i <= 5; i++) {
if (i == 0) {
_drawAxisText(canvas, '0', offset: Offset(-10, 2));
canvas.translate(0, -yStep);
continue;
}
canvas.drawLine(
Offset(0, 0), Offset(size.width - _kScaleHeight, 0), gridPaint);
canvas.drawLine(Offset(-_kScaleHeight, 0), Offset(0, 0), axisPaint);
String str = '${(numStep * i).toStringAsFixed(0)}';
_drawAxisText(canvas, str, offset: Offset(-10, 2));
canvas.translate(0, -yStep);
}
canvas.restore();
}
void _drawAxisText(Canvas canvas, String str,
{Color color = Colors.black,
bool x = false,
Alignment alignment = Alignment.centerRight,
Offset offset = Offset.zero}) {
TextSpan text = TextSpan(
text: str,
style: TextStyle(
fontSize: 11,
color: color,
));
_textPainter.text = text;
_textPainter.layout(); // 进行布局
Size size = _textPainter.size;
Offset offsetPos = Offset(-size.width / 2, -size.height / 2)
.translate(-size.width / 2 * alignment.x + offset.dx, 0.0 + offset.dy);
_textPainter.paint(canvas, offsetPos);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,254 @@
import 'dart:math';
import 'dart:ui';
// import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const ICharts(),
);
}
}
class ICharts extends StatefulWidget {
const ICharts({super.key});
@override
_IChartsState createState() => _IChartsState();
}
class _IChartsState extends State<ICharts> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
)
..forward()
;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(alignment: Alignment.topCenter, children: [
Container(
width: 400,
height: 300,
padding: EdgeInsets.only(top: 40, right: 20, bottom: 20, left: 20),
child: CustomPaint(
painter: ChartPainter(repaint: _controller),
),
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
"捷特 3 月支出统计图 - 2040 年",
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
),
)
]);
}
}
const double _kScaleHeight = 8; // 刻度高
const double _kBarPadding = 10; // 柱状图前间隔
const double _kPiePadding = 20; // 饼状图边距
class ChartPainter extends CustomPainter {
final TextPainter _textPainter =
TextPainter(textDirection: TextDirection.ltr);
final List<double> yData = [0.12, 0.25, 0.1, 0.18, 0.15, 0.2];
final List<Color> colors = [
Colors.red,
Colors.orangeAccent,
Colors.blue,
Colors.green,
Colors.purple,
Colors.pink
];
final List<String> xData = ["学习资料", "伙食费", "话费", "游玩", "游戏", "其他"];
final Animation<double> repaint;
Path axisPath = Path();
Paint axisPaint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 1;
Paint gridPaint = Paint()
..style = PaintingStyle.stroke
..color = Colors.grey
..strokeWidth = 0.5;
Paint fillPaint = Paint()..color = Colors.red;
Paint linePaint = Paint()
..color = Colors.red
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
double radius = 0; // 饼图半径
double yStep = 0; // y 间隔
double maxData = 0; // 数据最大值
final List<Offset> line = []; // 折线点位信息
ChartPainter({required this.repaint}) : super(repaint: repaint) {
maxData = yData.reduce(max);
}
@override
void paint(Canvas canvas, Size size) {
radius = size.shortestSide / 2 - _kPiePadding;
canvas.translate(size.width / 2, size.height / 2);
canvas.rotate(-pi / 2);
Path clipPath = Path();
clipPath.lineTo(radius, 0);
clipPath.arcTo(
Rect.fromCenter(center: Offset.zero, width: radius * 4, height: radius * 4),
0, 2 * pi * repaint.value, false);
clipPath.close();
if(repaint.value!=1.0){
canvas.clipPath(clipPath);
}
drawPieChart(canvas);
drawInfo(canvas);
}
void drawInfo(Canvas canvas) {
for (int i = 0; i < yData.length; i++) {
Color color = colors[i % colors.length];
canvas.save();
canvas.rotate(2 * pi * yData[i] / 2);
_drawAxisText(canvas, "${(yData[i] * 100).toStringAsFixed(1)}%",
color: Colors.white,
alignment: Alignment.center,
offset: Offset(radius / 2 + 5, 0));
Path showPath = Path();
showPath.moveTo(radius, 0);
showPath.relativeLineTo(15, 0);
showPath.relativeLineTo(5, 10);
canvas.drawPath(
showPath,
linePaint..color = color,
);
_drawAxisText(canvas, xData[i],
color: color,
fontSize: 9,
alignment: Alignment.centerLeft,
offset: Offset(radius + 5, 18));
canvas.restore();
canvas.rotate(2 * pi * yData[i]);
}
}
void drawPieChart(Canvas canvas) {
for (int i = 0; i < yData.length; i++) {
Color color = colors[i % colors.length];
Path path = Path();
path.lineTo(radius, 0);
path.arcTo(
Rect.fromCenter(
center: Offset.zero, width: radius * 2, height: radius * 2),
0,
2 * pi * yData[i],
false);
path.close();
canvas.drawPath(
path,
fillPaint
..style = PaintingStyle.fill
..color = color);
canvas.rotate(2 * pi * yData[i]);
}
}
void drawLineChart(Canvas canvas) {
canvas.drawPoints(PointMode.points, line, linePaint..strokeWidth = 5);
Offset p1 = line[0];
Path path = Path()..moveTo(p1.dx, p1.dy);
for (var i = 1; i < line.length; i++) {
path.lineTo(line[i].dx, line[i].dy);
}
linePaint..strokeWidth = 1;
PathMetrics pms = path.computeMetrics();
pms.forEach((pm) {
canvas.drawPath(pm.extractPath(0, pm.length * repaint.value), linePaint);
});
}
void drawYText(Canvas canvas, Size size) {
canvas.save();
double numStep = maxData / 5;
for (int i = 0; i <= 5; i++) {
if (i == 0) {
_drawAxisText(canvas, '0', offset: Offset(-10, 2));
canvas.translate(0, -yStep);
continue;
}
canvas.drawLine(
Offset(0, 0), Offset(size.width - _kScaleHeight, 0), gridPaint);
canvas.drawLine(Offset(-_kScaleHeight, 0), Offset(0, 0), axisPaint);
String str = '${(numStep * i).toStringAsFixed(0)}';
_drawAxisText(canvas, str, offset: Offset(-10, 2));
canvas.translate(0, -yStep);
}
canvas.restore();
}
void _drawAxisText(Canvas canvas, String str,
{Color color = Colors.black,
double fontSize = 11,
bool x = false,
Alignment alignment = Alignment.centerRight,
Offset offset = Offset.zero}) {
TextSpan text = TextSpan(
text: str,
style: TextStyle(
fontSize: fontSize,
color: color,
));
_textPainter.text = text;
_textPainter.layout(); // 进行布局
Size size = _textPainter.size;
Offset offsetPos = Offset(-size.width / 2, -size.height / 2)
.translate(-size.width / 2 * alignment.x + offset.dx, 0.0 + offset.dy);
_textPainter.paint(canvas, offsetPos);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1 @@
export 'p17_page.dart';

View File

@@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
import 'package:components/components.dart';
import 's01.dart' as s1;
import 's02.dart' as s2;
class P17Page extends StatelessWidget {
const P17Page({super.key});
@override
Widget build(BuildContext context) {
return const DemoShower(
srcCodeDir: 'draw/p17',
demos: [
s1.Paper(),
// s2.Paper(),
],
);
}
}

View File

@@ -0,0 +1,271 @@
import 'dart:math';
import 'dart:ui';
// import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const ICharts(),
);
}
}
class ICharts extends StatefulWidget {
const ICharts({super.key});
@override
_IChartsState createState() => _IChartsState();
}
class _IChartsState extends State<ICharts> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 1500),
vsync: this,
)
..forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(alignment: Alignment.topCenter, children: [
Container(
width: 500,
height: 380,
padding: EdgeInsets.only(top: 20, right: 20, bottom: 20, left: 20),
child: CustomPaint(
painter: ChartPainter(repaint: _controller),
),
),
// Padding(
// padding: const EdgeInsets.only(top: 8.0),
// child: Text(
// "捷特 3 月支出统计图 - 2040 年",
// style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
// ),
// )
]);
}
}
const double _kPiePadding = 20; // 圆边距
const double _kStrokeWidth = 12; // 圆弧宽
const double _kAngle = 270; // 圆弧角度
const int _kMax = 220; // 最大刻度值
const int _kMin = 0; // 最小刻度值
const double _kColorStopRate = 0.2; // 颜色变化分率
const double _kScaleHeightLever1 = 14; // 短刻度线
const double _kScaleHeightLever2 = 18; // 逢5线
const double _kScaleHeightLever3 = 20; // 逢10线
const double _kScaleDensity = 0.5; // 密度
const double _kScaleTextStep = 10; // 刻度文字步长
const List<Color> _kColors = [
// 颜色列表
Colors.green,
Colors.blue,
Colors.red,
];
class ChartPainter extends CustomPainter {
final TextPainter _textPainter =
TextPainter(textDirection: TextDirection.ltr);
double value = 150; //指针数值
double radius = 0; // 圆半径
final Animation<double> repaint;
Paint fillPaint = Paint();
Paint stokePaint = Paint()
..strokeWidth = 1
..style = PaintingStyle.stroke;
double get initAngle => (360 - _kAngle) / 2;
ChartPainter({required this.repaint}) : super(repaint: repaint);
@override
void paint(Canvas canvas, Size size) {
radius = size.shortestSide / 2 - _kPiePadding; // 当前圆半径
canvas.translate(size.width / 2, size.height / 2); // 画布原点移至中点
drawText(canvas);
drawArrow(canvas); // 绘制指针
canvas.rotate(pi / 2); // 将起始点旋转到 y 轴正方向
drawScale(canvas); // 绘制刻度
drawOutline(canvas); // 绘制弧线
}
void drawOutline(Canvas canvas) {
Path path = Path()..moveTo(radius, 0);
path.arcTo(
Rect.fromCenter(
center: Offset.zero, width: radius * 2, height: radius * 2),
pi / 180 * initAngle,
pi / 180 * _kAngle,
true);
PathMetrics pms = path.computeMetrics();
stokePaint..strokeWidth = _kStrokeWidth;
pms.forEach((PathMetric pm) {
canvas.drawPath(pm.extractPath(0, pm.length * _kColorStopRate),
stokePaint..color = _kColors[0]);
canvas.drawPath(
pm.extractPath(
pm.length * _kColorStopRate, pm.length * (1 - _kColorStopRate)),
stokePaint..color = _kColors[1]);
canvas.drawPath(
pm.extractPath(pm.length * (1 - _kColorStopRate), pm.length),
stokePaint..color = _kColors[2]);
});
}
void drawScale(Canvas canvas) {
canvas.save();
canvas.rotate(pi / 180 * initAngle);
double len = 0;
Color color = Colors.red;
int count = (_kMax * _kScaleDensity).toInt(); // 格线个数
for (int i = _kMin; i <= count; i++) {
if (i % 5 == 0 && i % 10 != 0) {
len = _kScaleHeightLever2;
} else if (i % 10 == 0) {
len = _kScaleHeightLever3;
} else {
len = _kScaleHeightLever1;
}
if (i < count * _kColorStopRate) {
color = Colors.green;
} else if (i < count * (1 - _kColorStopRate)) {
color = Colors.blue;
} else {
color = Colors.red;
}
canvas.drawLine(Offset(radius + _kStrokeWidth / 2, 0),
Offset(radius - len, 0), stokePaint..color = color..strokeWidth=1);
canvas.rotate(pi / 180 / _kMax * _kAngle / _kScaleDensity);
}
canvas.restore();
}
void drawArrow(Canvas canvas) {
var nowPer = value / _kMax;
Color color = Colors.red;
canvas.save();
double radians = pi / 180 * (-_kAngle / 2 + nowPer * _kAngle*repaint.value);
canvas.rotate(radians);
Path arrowPath = Path();
arrowPath.moveTo(0, 18);
arrowPath.relativeLineTo(-6, -10);
arrowPath.relativeLineTo(6, -radius + 10);
arrowPath.relativeLineTo(6, radius - 10);
arrowPath.close();
if (nowPer < _kColorStopRate) {
color = _kColors[0];
} else if (nowPer < (1 - _kColorStopRate)) {
color = _kColors[1];
} else {
color = _kColors[2];
}
canvas.drawPath(arrowPath, fillPaint..color = color);
canvas.drawCircle(Offset.zero, 3, stokePaint..color = Colors.yellow..strokeWidth=1);
canvas.drawCircle(Offset.zero, 3, fillPaint..color = Colors.white);
canvas.restore();
}
void drawText(Canvas canvas) {
_drawAxisText(canvas, 'km/s',
fontSize: 20,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold,
alignment: Alignment.center,
color: Colors.black,
offset: Offset(0, -radius / 2));
_drawAxisText(canvas, '${value.toStringAsFixed(1)}',
fontSize: 16,
alignment: Alignment.center,
color: Colors.black,
offset: Offset(0, radius / 2));
int count = (_kMax - _kMin) * _kScaleDensity ~/ _kScaleTextStep;
Color color = Colors.red;
for (int i = _kMin; i <= count; i++) {
var thta = pi / 180 * (90 + initAngle + (_kAngle / count) * i);
if (i < count * _kColorStopRate) {
color = _kColors[0];
} else if (i < count * (1 - _kColorStopRate)) {
color = _kColors[1];
} else {
color = _kColors[2];
}
Rect rect = Rect.fromLTWH((radius - 40) * cos(thta) - 12,
(radius - 40) * sin(thta) - 8, 24, 16);
canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(3)),
fillPaint..color = color);
_drawAxisText(canvas, '${i * _kScaleTextStep ~/ _kScaleDensity}',
fontSize: 11,
alignment: Alignment.center,
color: Colors.white,
offset: Offset((radius - 40) * cos(thta), (radius - 40) * sin(thta)));
}
}
void _drawAxisText(Canvas canvas, String str,
{Color color = Colors.black,
double fontSize = 11,
FontStyle fontStyle = FontStyle.normal,
Alignment alignment = Alignment.centerRight,
FontWeight fontWeight = FontWeight.normal,
Offset offset = Offset.zero}) {
TextSpan text = TextSpan(
text: str,
style: TextStyle(
fontSize: fontSize,
fontWeight: fontWeight,
fontStyle: fontStyle,
color: color,
));
_textPainter.text = text;
_textPainter.layout(); // 进行布局
Size size = _textPainter.size;
Offset offsetPos = Offset(-size.width / 2, -size.height / 2)
.translate(-size.width / 2 * alignment.x + offset.dx, 0.0 + offset.dy);
_textPainter.paint(canvas, offsetPos);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,432 @@
import 'dart:math';
// import 'dart:ui' as ui;
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:convert' show json;
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const ChinaMap(),
);
}
}
class ChinaMap extends StatefulWidget {
const ChinaMap({super.key});
@override
_ChinaMapState createState() => _ChinaMapState();
}
class _ChinaMapState extends State<ChinaMap> {
final String url = 'https://geo.datav.aliyun.com/areas_v2/bound/100000_full.json'; //全国点位详细信息
//请求点位信息地址
Future<MapRoot?> getMapRoot() async {
try {
final Response response = await Dio().get(url);
if (response.data != null) {
return MapRoot.fromJson(response.data);
} else {
return null;
}
} catch (e) {
print(e);
return null;
}
}
late Future<MapRoot?> _future;
@override
void initState() {
super.initState();
_future = getMapRoot();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<MapRoot?>(
future: _future,
builder: (context, async) {
print(async.hasData);
if (async.hasData) {
return CustomPaint(
size: Size(500, 400),
painter: MapPainter(mapRoot: async.data),
);
} else {
return CupertinoActivityIndicator();
}
},
);
}
}
class MapPainter extends CustomPainter {
final MapRoot? mapRoot; //点位信息
late Paint _paint;
final List<Color> colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green];
int colorIndex = 0;
MapPainter({required this.mapRoot}) {
_paint = Paint()
..strokeWidth = 0.1
..isAntiAlias = true;
}
@override
void paint(Canvas canvas, Size size) {
if(mapRoot == null) return;
if(mapRoot!.features ==null) return;
canvas.clipRect(
Rect.fromPoints(Offset.zero, Offset(size.width, size.height)));
canvas.translate(size.width / 2, size.height / 2);
double dx = mapRoot!.features![0]?.geometry?.coordinates[0][0][0].dx??0;
double dy = mapRoot!.features![0]?.geometry?.coordinates[0][0][0].dy??0;
canvas.translate( -dx, -dy);
double rate = 0.65;
canvas.translate(-700*rate, 350*rate);
canvas.scale(8*rate, -10.5*rate);
_drawMap(canvas, size);
}
void _drawMap(Canvas canvas, Size size) {
//全国省份循环
for (int i = 0; i < mapRoot!.features!.length; i++) {
var features = mapRoot!.features![i];
if(features ==null) return;
PaintingStyle style;
Color color = Colors.black;
style = PaintingStyle.fill;
Path path = Path();
if (features.properties?.name == "台湾省" ||
features.properties?.name == "海南省" ||
features.properties?.name == "河北省" ||
features.properties?.name == "") { //海南和台湾和九段线
Path otherPath = Path();
features.geometry?.coordinates.forEach((List<List<Offset>> lv3) {
for (var lv2 in lv3) {
otherPath.moveTo(lv2[0].dx, lv2[0].dy);
// for (var lv1 in lv2) {
// otherPath.lineTo(lv1.dx, lv1.dy); //优化一半点位
// print("========${lv1}==============================");
// }
}
});
path.addPath(otherPath, Offset.zero);
if (features.properties?.name == "") {
style = PaintingStyle.stroke;
color = Colors.black;
} else {
style = PaintingStyle.fill;
color = colors[colorIndex % 4];
}
colorIndex++;
} else {
final Offset first = features.geometry?.coordinates[0][0][0]??Offset.zero;
path.moveTo(first.dx, first.dy);
if(features.geometry ==null) return;
for (Offset d in features.geometry!.coordinates.first.first) {
path.lineTo(d.dx, d.dy);
}
style = PaintingStyle.fill;
color = colors[colorIndex % 4];
colorIndex++;
}
canvas.drawPath(path, _paint..color = color..style = style); //绘制地图
}
}
@override
bool shouldRepaint(MapPainter oldDelegate) => oldDelegate.mapRoot != mapRoot;
}
class MapRoot {
String type;
String name;
List<Features?>? features;
MapRoot({
required this.type,
required this.name,
required this.features,
});
static MapRoot? fromJson(jsonRes) {
if (jsonRes == null) return null;
List<Features?>? features = jsonRes['features'] is List ? [] : null;
if (features != null) {
for (var item in jsonRes['features']) {
if (item != null) {
features.add(Features.fromJson(item));
}
}
}
return MapRoot(
type: jsonRes['type'],
name: jsonRes['name'],
features: features,
);
}
Map<String, dynamic> toJson() => {
'type': type,
'name': name,
'features': features,
};
@override
String toString() {
return json.encode(this);
}
}
class Features {
String type;
Properties? properties;
Geometry? geometry;
Features({
required this.type,
required this.properties,
required this.geometry,
});
static Features? fromJson(jsonRes) => jsonRes == null
? null
: Features(
type: jsonRes['type'],
properties: Properties.fromJson(jsonRes['properties']),
geometry: Geometry.fromJson(jsonRes['geometry']),
);
Map<String, dynamic> toJson() => {
'type': type,
'properties': properties,
'geometry': geometry,
};
@override
String toString() {
return json.encode(this);
}
}
class Properties {
String? adcode;
String? name;
List<double>? center;
List<double>? centroid;
int? childrenNum;
String?level;
Parent? parent;
int? subFeatureIndex;
List<int>? acroutes;
Object? adchar;
Properties({
required this.adcode,
required this.name,
required this.center,
required this.centroid,
required this.childrenNum,
required this.level,
required this.parent,
required this.subFeatureIndex,
required this.acroutes,
required this.adchar,
});
static Properties? fromJson(jsonRes) {
if (jsonRes == null) return null;
List<double>? center = jsonRes['center'] is List ? [] : null;
if (center != null) {
for (var item in jsonRes['center']) {
if (item != null) {
center.add(item);
}
}
}
List<double>? centroid = jsonRes['centroid'] is List ? [] : null;
if (centroid != null) {
for (var item in jsonRes['centroid']) {
if (item != null) {
centroid.add(item);
}
}
}
List<int>? acroutes = jsonRes['acroutes'] is List ? [] : null;
if (acroutes != null) {
for (var item in jsonRes['acroutes']) {
if (item != null) {
acroutes.add(item);
}
}
}
return Properties(
adcode: jsonRes['adcode'],
name: jsonRes['name'],
center: center,
centroid: centroid,
childrenNum: jsonRes['childrenNum'],
level: jsonRes['level'],
parent: Parent.fromJson(jsonRes['parent']),
subFeatureIndex: jsonRes['subFeatureIndex'],
acroutes: acroutes,
adchar: jsonRes['adchar'],
);
}
Map<String, dynamic> toJson() => {
'adcode': adcode,
'name': name,
'center': center,
'centroid': centroid,
'childrenNum': childrenNum,
'level': level,
'parent': parent,
'subFeatureIndex': subFeatureIndex,
'acroutes': acroutes,
'adchar': adchar,
};
@override
String toString() {
return json.encode(this);
}
}
class Parent {
int adcode;
Parent({
required this.adcode,
});
static Parent? fromJson(jsonRes) => jsonRes == null
? null
: Parent(
adcode: jsonRes['adcode'],
);
Map<String, dynamic> toJson() => {
'adcode': adcode,
};
@override
String toString() {
return json.encode(this);
}
}
class Geometry {
String type;
List<List<List<Offset>>> coordinates;
Geometry({
required this.type,
required this.coordinates,
});
static Geometry? fromJson(jsonRes) {
if (jsonRes == null) return null;
List<List<List<Offset>>>? coordinates =
jsonRes['coordinates'] is List ? [] : null;
bool fourLever =false;
if (jsonRes['coordinates'] is List) {
if (jsonRes['coordinates'][0] is List){
if (jsonRes['coordinates'][0][0] is List){
if (jsonRes['coordinates'][0][0][0] is List){
fourLever =true;
}
}
}
}
if(!fourLever){
if (coordinates != null) {
for (var level0 in jsonRes['coordinates']) {
List<List<Offset>> lever0=[];
if (level0 != null) {
List<Offset> items1 = [];
for (var item1 in level0 is List ? level0 : []) {
if (item1 != null) {
Offset items2 = Offset(item1[0], item1[1]);
items1.add(items2);
}
lever0.add(items1);
}
}
coordinates.add(lever0);
}
}
}else{
if (coordinates != null) {
for (var level0 in jsonRes['coordinates']) {
if (level0 != null) {
List<List<Offset>> items1 = [];
for (var item1 in level0 is List ? level0 : []) {
if (item1 != null) {
List<Offset> items2 = [];
for (var item2 in item1 is List ? item1 : []) {
if (item2 != null && item2 is List) {
Offset items3 = Offset(item2[0], item2[1]);
items2.add(items3);
} else {
items2.add(Offset.zero);
}
items1.add(items2);
}
}
coordinates.add(items1);
}
}
}
}
}
return Geometry(
type: jsonRes['type'],
coordinates: coordinates??[],
);
}
Map<String, dynamic> toJson() => {
'type': type,
'coordinates': coordinates,
};
@override
String toString() {
return json.encode(this);
}
}

View File

@@ -0,0 +1 @@
export 'p18_page.dart';

View File

@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:components/components.dart';
import 's01.dart' as s1;
import 's02.dart' as s2;
import 's03.dart' as s3;
import 's04.dart' as s4;
import 's05.dart' as s5;
class P18Page extends StatelessWidget {
const P18Page({super.key});
@override
Widget build(BuildContext context) {
return const DemoShower(
srcCodeDir: 'draw/p18',
demos: [
s1.Paper(),
s2.Paper(),
s3.Paper(),
s4.Paper(),
// s5.Paper(),
],
);
}
}

View File

@@ -0,0 +1,201 @@
import 'dart:math';
// import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const World(),
);
}
}
class World extends StatefulWidget {
const World({super.key});
@override
_WorldState createState() => _WorldState();
}
class _WorldState extends State<World> with SingleTickerProviderStateMixin {
late AnimationController _controller;
ParticleManage pm = ParticleManage();
@override
void initState() {
super.initState();
initParticleManage();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)
..addListener(pm.tick)
// ..repeat()
;
}
void initParticleManage() {
pm.size = Size(300, 200);
Particle particle = Particle(x: 0, y: 0, vx: 3, vy: 0,ay: 0.05,
color: Colors.blue, size: 8);
pm.particles = [particle];
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
theWorld(){
if (_controller.isAnimating) {
_controller.stop();
} else {
_controller.repeat();
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: theWorld,
child: CustomPaint(
size: pm.size,
painter: WorldRender(manage: pm),
),
);
}
}
class ParticleManage with ChangeNotifier {
List<Particle> particles = [];
Size size;
ParticleManage({this.size = Size.zero});
void setParticles(List<Particle> particles) {
this.particles = particles;
}
void addParticle(Particle particle) {
particles.add(particle);
notifyListeners();
}
void tick() {
particles.forEach(doUpdate);
notifyListeners();
}
void doUpdate(Particle p) {
p.vy += p.ay; // y加速度变化
p.vx += p.ax; // x加速度变化
p.x += p.vx;
p.y += p.vy;
if (p.x > size.width) {
p.x = size.width;
p.vx = -p.vx;
}
if (p.x < 0) {
p.x = 0;
p.vx = -p.vx;
}
if (p.y > size.height) {
p.y = size.height;
p.vy = -p.vy;
}
if (p.y < 0) {
p.y = 0;
p.vy = -p.vy;
}
}
void reset() {
particles.forEach((p) {
p.x = 0;
});
notifyListeners();
}
}
class WorldRender extends CustomPainter {
final ParticleManage manage;
Paint fillPaint = Paint();
Paint stokePaint = Paint()
..strokeWidth = 0.5
..style = PaintingStyle.stroke;
WorldRender({required this.manage}) : super(repaint: manage);
@override
void paint(Canvas canvas, Size size) {
canvas.drawRect(Offset.zero & size, stokePaint);
manage.particles.forEach((particle) {
drawParticle(canvas, particle);
});
}
void drawParticle(Canvas canvas, Particle particle) {
fillPaint.color = particle.color;
canvas.drawCircle(Offset(particle.x, particle.y), particle.size, fillPaint);
}
@override
bool shouldRepaint(covariant WorldRender oldDelegate) =>
oldDelegate.manage != manage;
}
class Particle {
/// x 位移.
double x;
/// y 位移.
double y;
/// 粒子水平速度.
double vx;
// 粒子水平加速度
double ax;
// 粒子竖直加速度
double ay;
///粒子竖直速度.
double vy;
/// 粒子大小.
double size;
/// 粒子颜色.
Color color;
Particle({
this.x = 0,
this.y = 0,
this.ax = 0,
this.ay = 0,
this.vx = 0,
this.vy = 0,
this.size = 0,
this.color = Colors.black,
});
}

View File

@@ -0,0 +1,215 @@
import 'dart:math';
// import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const World(),
);
}
}
class World extends StatefulWidget {
const World({super.key});
@override
_WorldState createState() => _WorldState();
}
class _WorldState extends State<World> with SingleTickerProviderStateMixin {
late AnimationController _controller;
ParticleManage pm = ParticleManage();
Random random = Random();
@override
void initState() {
super.initState();
initParticleManage();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)..addListener(pm.tick)
// ..repeat()
;
}
void initParticleManage() {
pm.size = Size(300, 200);
for (var i = 0; i < 30; i++) {
pm.particles.add(Particle(
color: randomRGB(),
size: 5 + 4 * random.nextDouble(),
vx: 3 * random.nextDouble() * pow(-1, random.nextInt(20)),
vy: 3 * random.nextDouble() * pow(-1, random.nextInt(20)),
ay: 0.1,
x: 150,
y: 100));
}
}
Color randomRGB({int limitR = 0, int limitG = 0, int limitB = 0,}) {
var r = limitR + random.nextInt(256 - limitR); //红值
var g = limitG + random.nextInt(256 - limitG); //绿值
var b = limitB + random.nextInt(256 - limitB); //蓝值
return Color.fromARGB(255, r, g, b); //生成argb模式的颜色
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
theWorld() {
if (_controller.isAnimating) {
_controller.stop();
} else {
_controller.repeat();
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: theWorld,
child: CustomPaint(
size: pm.size,
painter: WorldRender(manage: pm),
),
);
}
}
class ParticleManage with ChangeNotifier {
List<Particle> particles = [];
Size size;
ParticleManage({this.size = Size.zero});
void setParticles(List<Particle> particles) {
this.particles = particles;
}
void addParticle(Particle particle) {
particles.add(particle);
notifyListeners();
}
void tick() {
particles.forEach(doUpdate);
notifyListeners();
}
void doUpdate(Particle p) {
p.vy += p.ay; // y加速度变化
p.vx += p.ax; // x加速度变化
p.x += p.vx;
p.y += p.vy;
if (p.x > size.width) {
p.x = size.width;
p.vx = -p.vx;
}
if (p.x < 0) {
p.x = 0;
p.vx = -p.vx;
}
if (p.y > size.height) {
p.y = size.height;
p.vy = -p.vy;
}
if (p.y < 0) {
p.y = 0;
p.vy = -p.vy;
}
}
void reset() {
particles.forEach((p) {
p.x = 0;
});
notifyListeners();
}
}
class WorldRender extends CustomPainter {
final ParticleManage manage;
Paint fillPaint = Paint();
Paint stokePaint = Paint()
..strokeWidth = 0.5
..style = PaintingStyle.stroke;
WorldRender({required this.manage}) : super(repaint: manage);
@override
void paint(Canvas canvas, Size size) {
canvas.drawRect(Offset.zero & size, stokePaint);
manage.particles.forEach((particle) {
drawParticle(canvas, particle);
});
}
void drawParticle(Canvas canvas, Particle particle) {
fillPaint.color = particle.color;
canvas.drawCircle(Offset(particle.x, particle.y), particle.size, fillPaint);
}
@override
bool shouldRepaint(covariant WorldRender oldDelegate) =>
oldDelegate.manage != manage;
}
class Particle {
/// x 位移.
double x;
/// y 位移.
double y;
/// 粒子水平速度.
double vx;
// 粒子水平加速度
double ax;
// 粒子竖直加速度
double ay;
///粒子竖直速度.
double vy;
/// 粒子大小.
double size;
/// 粒子颜色.
Color color;
Particle({
this.x = 0,
this.y = 0,
this.ax = 0,
this.ay = 0,
this.vx = 0,
this.vy = 0,
this.size = 0,
this.color = Colors.black,
});
}

View File

@@ -0,0 +1,218 @@
import 'dart:async';
import 'dart:math';
// import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const World(),
);
}
}
class World extends StatefulWidget {
const World({super.key});
@override
_WorldState createState() => _WorldState();
}
class _WorldState extends State<World> with SingleTickerProviderStateMixin {
late AnimationController _controller;
ParticleManage pm = ParticleManage();
Random random = Random();
late Timer timer;
@override
void initState() {
super.initState();
pm.size = Size(300, 200);
timer =Timer.periodic(Duration(seconds: 1), (timer) {
if(pm.particles.length>20){
timer.cancel();
}
pm.addParticle(Particle(
color: randomRGB(),
size: 5 + 4 * random.nextDouble(),
vx: 3 * random.nextDouble() * pow(-1, random.nextInt(20)),
vy: 3 * random.nextDouble() * pow(-1, random.nextInt(20)),
ay: 0.1,
x: 150,
y: 100));
});
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)..addListener(pm.tick)
..repeat();
}
Color randomRGB({int limitR = 0, int limitG = 0, int limitB = 0,}) {
var r = limitR + random.nextInt(256 - limitR); //红值
var g = limitG + random.nextInt(256 - limitG); //绿值
var b = limitB + random.nextInt(256 - limitB); //蓝值
return Color.fromARGB(255, r, g, b); //生成argb模式的颜色
}
@override
void dispose() {
_controller.dispose();
timer.cancel();
super.dispose();
}
theWorld() {
if (_controller.isAnimating) {
_controller.stop();
} else {
_controller.repeat();
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: theWorld,
child: CustomPaint(
size: pm.size,
painter: WorldRender(manage: pm),
),
);
}
}
class ParticleManage with ChangeNotifier {
List<Particle> particles = [];
Size size;
ParticleManage({this.size = Size.zero});
void setParticles(List<Particle> particles) {
this.particles = particles;
}
void addParticle(Particle particle) {
particles.add(particle);
notifyListeners();
}
void tick() {
particles.forEach(doUpdate);
notifyListeners();
}
void doUpdate(Particle p) {
p.vy += p.ay; // y加速度变化
p.vx += p.ax; // x加速度变化
p.x += p.vx;
p.y += p.vy;
if (p.x > size.width) {
p.x = size.width;
p.vx = -p.vx;
}
if (p.x < 0) {
p.x = 0;
p.vx = -p.vx;
}
if (p.y > size.height) {
p.y = size.height;
p.vy = -p.vy;
}
if (p.y < 0) {
p.y = 0;
p.vy = -p.vy;
}
}
void reset() {
particles.forEach((p) {
p.x = 0;
});
notifyListeners();
}
}
class WorldRender extends CustomPainter {
final ParticleManage manage;
Paint fillPaint = Paint();
Paint stokePaint = Paint()
..strokeWidth = 0.5
..style = PaintingStyle.stroke;
WorldRender({required this.manage}) : super(repaint: manage);
@override
void paint(Canvas canvas, Size size) {
canvas.drawRect(Offset.zero & size, stokePaint);
manage.particles.forEach((particle) {
drawParticle(canvas, particle);
});
}
void drawParticle(Canvas canvas, Particle particle) {
fillPaint.color = particle.color;
canvas.drawCircle(Offset(particle.x, particle.y), particle.size, fillPaint);
}
@override
bool shouldRepaint(covariant WorldRender oldDelegate) =>
oldDelegate.manage != manage;
}
class Particle {
/// x 位移.
double x;
/// y 位移.
double y;
/// 粒子水平速度.
double vx;
// 粒子水平加速度
double ax;
// 粒子竖直加速度
double ay;
///粒子竖直速度.
double vy;
/// 粒子大小.
double size;
/// 粒子颜色.
Color color;
Particle({
this.x = 0,
this.y = 0,
this.ax = 0,
this.ay = 0,
this.vx = 0,
this.vy = 0,
this.size = 0,
this.color = Colors.black,
});
}

View File

@@ -0,0 +1,207 @@
import 'dart:async';
import 'dart:math';
// import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const World(),
);
}
}
class World extends StatefulWidget {
const World({super.key});
@override
_WorldState createState() => _WorldState();
}
class _WorldState extends State<World> with SingleTickerProviderStateMixin {
late AnimationController _controller;
ParticleManage pm = ParticleManage();
Random random = Random();
@override
void initState() {
super.initState();
pm.size = Size(300, 200);
pm.addParticle(Particle(
color: Colors.blue,
size: 50,
vx: 4 * random.nextDouble() * pow(-1, random.nextInt(20)),
vy: 4 * random.nextDouble() * pow(-1, random.nextInt(20)),
ay: 0.1,
ax: 0.1,
x: 150,
y: 100));
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)
..addListener(pm.tick)
// ..repeat()
;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
theWorld() {
if (_controller.isAnimating) {
_controller.stop();
} else {
_controller.repeat();
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: theWorld,
child: CustomPaint(
size: pm.size,
painter: WorldRender(manage: pm),
),
);
}
}
class ParticleManage with ChangeNotifier {
List<Particle> particles = [];
Size size;
ParticleManage({this.size = Size.zero});
void setParticles(List<Particle> particles) {
this.particles = particles;
}
void addParticle(Particle particle) {
particles.add(particle);
notifyListeners();
}
void tick() {
particles.forEach(doUpdate);
notifyListeners();
}
void doUpdate(Particle p) {
p.vy += p.ay; // y加速度变化
p.vx += p.ax; // x加速度变化
p.x += p.vx;
p.y += p.vy;
if (p.x > size.width) {
p.x = size.width;
p.vx = -p.vx;
}
if (p.x < 0) {
p.x = 0;
p.vx = -p.vx;
}
if (p.y > size.height) {
p.y = size.height;
p.vy = -p.vy;
}
if (p.y < 0) {
p.y = 0;
p.vy = -p.vy;
}
}
void reset() {
particles.forEach((p) {
p.x = 0;
});
notifyListeners();
}
}
class WorldRender extends CustomPainter {
final ParticleManage manage;
Paint fillPaint = Paint();
Paint stokePaint = Paint()
..strokeWidth = 0.5
..style = PaintingStyle.stroke;
WorldRender({required this.manage}) : super(repaint: manage);
@override
void paint(Canvas canvas, Size size) {
canvas.drawRect(Offset.zero & size, stokePaint);
manage.particles.forEach((particle) {
drawParticle(canvas, particle);
});
}
void drawParticle(Canvas canvas, Particle particle) {
fillPaint.color = particle.color;
canvas.drawCircle(Offset(particle.x, particle.y), particle.size, fillPaint);
}
@override
bool shouldRepaint(covariant WorldRender oldDelegate) =>
oldDelegate.manage != manage;
}
class Particle {
/// x 位移.
double x;
/// y 位移.
double y;
/// 粒子水平速度.
double vx;
// 粒子水平加速度
double ax;
// 粒子竖直加速度
double ay;
///粒子竖直速度.
double vy;
/// 粒子大小.
double size;
/// 粒子颜色.
Color color;
Particle({
this.x = 0,
this.y = 0,
this.ax = 0,
this.ay = 0,
this.vx = 0,
this.vy = 0,
this.size = 0,
this.color = Colors.black,
});
}

View File

@@ -0,0 +1,244 @@
import 'dart:async';
import 'dart:math';
// import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image/image.dart' as image;
/// create by 张风捷特烈 on 2020/11/5
/// contact me by email 1981462002@qq.com
/// 说明:
class Paper extends StatelessWidget {
const Paper({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
alignment: Alignment.center,
child: const World(),
);
}
}
class World extends StatefulWidget {
const World({super.key});
@override
_WorldState createState() => _WorldState();
}
class _WorldState extends State<World> with SingleTickerProviderStateMixin {
late AnimationController _controller;
ParticleManage pm = ParticleManage();
Random random = Random();
@override
void initState() {
super.initState();
pm.size = Size(400, 260);
initParticles();
// pm.addParticle(Particle(
// color: Colors.blue,
// size: 50,
// vx: 4 * random.nextDouble() * pow(-1, random.nextInt(20)),
// vy: 4 * random.nextDouble() * pow(-1, random.nextInt(20)),
// ay: 0.1,
// ax: 0.1,
// x: 150,
// y: 100));
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)..addListener(pm.tick)
// ..repeat()
;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
theWorld() {
if (_controller.isAnimating) {
_controller.stop();
} else {
_controller.repeat();
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: theWorld,
child: CustomPaint(
size: pm.size,
painter: WorldRender(manage: pm),
),
);
}
void initParticles() async {
ByteData data = await rootBundle.load("assets/images/flutter.png");
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
image.Image? imageSrc = image.decodeImage(Uint8List.fromList(bytes));
if(imageSrc==null) return;
double offsetX= (pm.size.width-imageSrc.width)/2;
double offsetY= (pm.size.height-imageSrc.height)/2;
for (int i = 0; i < imageSrc.width; i++) {
for (int j = 0; j < imageSrc.height; j++) {
image.PixelUint8 pixel = imageSrc.getPixel(i, j) as image.PixelUint8;
if (pixel.toString() != '(255, 255, 255, 0)') {
// print('-($i,$j)----${imageSrc.getPixel(i, j)}---------------');
Particle particle = Particle(
x: i * 1.0+ offsetX,
y: j * 1.0+ offsetY,
vx: 4 * random.nextDouble() * pow(-1, random.nextInt(20)),
vy: 4 * random.nextDouble() * pow(-1, random.nextInt(20)),
ay: 0.1,
size: 0.5,
color: Colors.blue); //产生粒子---每个粒子拥有随机的一些属性信息
pm.particles.add(particle);
}
}
}
setState(() {
});
}
}
class ParticleManage with ChangeNotifier {
List<Particle> particles = [];
Size size;
ParticleManage({this.size = Size.zero});
void setParticles(List<Particle> particles) {
this.particles = particles;
}
void addParticle(Particle particle) {
particles.add(particle);
notifyListeners();
}
void tick() {
particles.forEach(doUpdate);
notifyListeners();
}
void doUpdate(Particle p) {
p.vy += p.ay; // y加速度变化
p.vx += p.ax; // x加速度变化
p.x += p.vx;
p.y += p.vy;
if (p.x > size.width) {
p.x = size.width;
p.vx = -p.vx;
}
if (p.x < 0) {
p.x = 0;
p.vx = -p.vx;
}
if (p.y > size.height) {
p.y = size.height;
p.vy = -p.vy;
}
if (p.y < 0) {
p.y = 0;
p.vy = -p.vy;
}
}
void reset() {
particles.forEach((p) {
p.x = 0;
});
notifyListeners();
}
}
class WorldRender extends CustomPainter {
final ParticleManage manage;
Paint fillPaint = Paint();
Paint stokePaint = Paint()
..strokeWidth = 0.5
..style = PaintingStyle.stroke;
WorldRender({required this.manage}) : super(repaint: manage);
@override
void paint(Canvas canvas, Size size) {
// canvas.drawRect(Offset.zero&size, stokePaint);
manage.particles.forEach((particle) {
drawParticle(canvas, particle);
});
}
void drawParticle(Canvas canvas, Particle particle) {
fillPaint.color = particle.color;
canvas.drawCircle(Offset(particle.x, particle.y), particle.size, fillPaint);
}
@override
bool shouldRepaint(covariant WorldRender oldDelegate) =>
oldDelegate.manage != manage;
}
class Particle {
/// x 位移.
double x;
/// y 位移.
double y;
/// 粒子水平速度.
double vx;
// 粒子水平加速度
double ax;
// 粒子竖直加速度
double ay;
///粒子竖直速度.
double vy;
/// 粒子大小.
double size;
/// 粒子颜色.
Color color;
Particle({
this.x = 0,
this.y = 0,
this.ax = 0,
this.ay = 0,
this.vx = 0,
this.vy = 0,
this.size = 0,
this.color = Colors.black,
});
}

View File

@@ -11,6 +11,7 @@ dependencies:
flutter:
sdk: flutter
image: ^4.1.3
dio: ^5.4.0
components:
path: ../components
dev_dependencies:

View File

@@ -80,6 +80,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.5"
dio:
dependency: transitive
description:
name: dio
sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.4.0"
fake_async:
dependency: transitive
description:
@@ -119,6 +127,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "12.1.1"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.2"
idraw:
dependency: "direct main"
description:
@@ -270,10 +286,11 @@ packages:
toly_menu:
dependency: "direct main"
description:
path: "E:\\Projects\\Flutter\\packages\\toly_menu"
relative: false
source: path
version: "0.0.2"
name: toly_menu
sha256: "0dbc80a3e7d2e18833a914e76411a03c3feff1f4baffbd382b180e8a0051b1e7"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.0.3"
typed_data:
dependency: transitive
description:

View File

@@ -68,6 +68,7 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/
- assets/palettes.json
- assets/draw/p01/
- assets/draw/p02/
- assets/draw/p03/