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

File diff suppressed because it is too large Load Diff

View File

@@ -62,7 +62,6 @@ class MenuStore with ChangeNotifier {
?.label; ?.label;
void selectMenuPath(String path) { void selectMenuPath(String path) {
print("======================selectMenuPath:$path======${_shouldAddHistory}==============");
MenuNode root = MenuNode( MenuNode root = MenuNode(
path: '', path: '',
label: '', label: '',
@@ -160,6 +159,14 @@ class MenuStore with ChangeNotifier {
} }
void select(MenuNode menu) { 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) { if (menu.isLeaf) {
_state = state.copyWith(activeMenu: menu.path); _state = state.copyWith(activeMenu: menu.path);
goRouter.go(menu.path); goRouter.go(menu.path);

View File

@@ -1,4 +1,5 @@
import 'package:components/components.dart'; import 'package:components/components.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:iroute/navigation/router/routers/anima.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/scroll.dart';
import 'package:iroute/navigation/router/routers/touch.dart'; import 'package:iroute/navigation/router/routers/touch.dart';
import '../../transition/size_clip_transition.dart';
import '../../views/app_navigation.dart'; import '../../views/app_navigation.dart';
import '../../../pages/empty/empty_panel.dart'; import '../../../pages/empty/empty_panel.dart';
import 'dashboard.dart'; import 'dashboard.dart';
import 'draw.dart'; import 'draw.dart';
import 'layout.dart'; import 'layout.dart';
final RouteBase appRoute = ShellRoute( final RouteBase appRoute = ShellRoute(
builder: (BuildContext context, GoRouterState state, Widget child) { builder: (BuildContext context, GoRouterState state, Widget child) {
return TolyBookNavigation(content: child); return TolyBookNavigation(content: child);
@@ -29,9 +30,27 @@ final RouteBase appRoute = ShellRoute(
animaRouters, animaRouters,
GoRoute( GoRoute(
path: '/code', path: '/code',
builder: (BuildContext context, GoRouterState state) { pageBuilder: (BuildContext context, GoRouterState state) {
String? path = state.uri.queryParameters['path']; 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( GoRoute(

View File

@@ -5,32 +5,76 @@ import 'package:iroute/pages/empty/empty_panel.dart';
final RouteBase drawRouters = GoRoute( final RouteBase drawRouters = GoRoute(
path: '/draw/chapter:index', path: '/draw/chapter:index',
builder: (BuildContext context, GoRouterState state) { pageBuilder: (BuildContext context, GoRouterState state) {
String? index = state.pathParameters['index']; String? index = state.pathParameters['index'];
switch(index){ Widget child = const EmptyPanel(msg: '暂未实现');
switch (index) {
case '1': case '1':
return const P01Page(); child = const P01Page();
break;
case '2': case '2':
return const P02Page(); child = const P02Page();
break;
case '3': case '3':
return const P03Page(); child = const P03Page();
break;
case '4': case '4':
return const P04Page(); child = const P04Page();
break;
case '5': case '5':
return const P05Page(); child = const P05Page();
break;
case '6': case '6':
return const P06Page(); child = const P06Page();
break;
case '7': case '7':
return const P07Page(); child = const P07Page();
break;
case '8': case '8':
return const P08Page(); child = const P08Page();
break;
case '9': case '9':
return const P09Page(); child = const P09Page();
break;
case '10': case '10':
return const P10Page(); child = const P10Page();
break;
case '11': case '11':
return const P11Page(); 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,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class FadePageTransitionsBuilder extends PageTransitionsBuilder { class FadePageTransitionsBuilder extends PageTransitionsBuilder {
const FadePageTransitionsBuilder(); const FadePageTransitionsBuilder();
@override @override
@@ -12,31 +11,8 @@ class FadePageTransitionsBuilder extends PageTransitionsBuilder {
Animation<double> secondaryAnimation, Animation<double> secondaryAnimation,
Widget child, Widget child,
) { ) {
return _FadePagePageTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
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( return FadeTransition(
opacity: animation.drive(curveTween), opacity: animation.drive(CurveTween(curve: Curves.easeIn)),
child: child, 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: Row( body: Row(
children: [ children: [

View File

@@ -10,6 +10,8 @@ class AppTopBar extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
// String? lable = MenuScope.of(context).currentNode?.label; // String? lable = MenuScope.of(context).currentNode?.label;
return DragToMoveWrap( return DragToMoveWrap(
child: SizedBox(
height: 36,
child: Stack( child: Stack(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
children: [Row( children: [Row(
@@ -28,6 +30,7 @@ class AppTopBar extends StatelessWidget {
], ],
),WindowButtons()] ),WindowButtons()]
), ),
),
); );
} }
} }

View File

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

View File

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

View File

@@ -11,3 +11,10 @@ export 'p08/p08.dart';
export 'p09/p09.dart'; export 'p09/p09.dart';
export 'p10/p10.dart'; export 'p10/p10.dart';
export 'p11/p11.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(),
],
);
}
}

Some files were not shown because too many files have changed in this diff Show More