From bde7cad11db0302237c2547c9564eb48c83f94fc Mon Sep 17 00:00:00 2001 From: toly <1981462002@qq.com> Date: Fri, 22 Dec 2023 10:01:10 +0800 Subject: [PATCH] books --- assets/images/body.webp | Bin 0 -> 13296 bytes assets/images/hand.webp | Bin 0 -> 2938 bytes .../router/menus/menu_scope/menu_scope.dart | 8 +- lib/navigation/router/routers/draw.dart | 9 + lib/navigation/views/top_bar.dart | 3 - packages/idraw/lib/idraw.dart | 3 + packages/idraw/lib/p18/p18_page.dart | 2 +- packages/idraw/lib/p18/s05.dart | 9 +- packages/idraw/lib/p19/p19.dart | 1 + packages/idraw/lib/p19/p19_page.dart | 21 + packages/idraw/lib/p19/s01.dart | 301 +++++++++++ packages/idraw/lib/p19/s02.dart | 473 +++++++++++++++++ packages/idraw/lib/p20/p20.dart | 1 + packages/idraw/lib/p20/p20_page.dart | 21 + packages/idraw/lib/p20/s01.dart | 340 +++++++++++++ packages/idraw/lib/p20/s02.dart | 480 ++++++++++++++++++ packages/idraw/lib/p21/p21.dart | 1 + packages/idraw/lib/p21/p21_page.dart | 21 + packages/idraw/lib/p21/s01.dart | 241 +++++++++ packages/idraw/lib/p21/s02.dart | 291 +++++++++++ packages/idraw/pubspec.yaml | 1 + pubspec.lock | 8 + 22 files changed, 2223 insertions(+), 12 deletions(-) create mode 100644 assets/images/body.webp create mode 100644 assets/images/hand.webp create mode 100644 packages/idraw/lib/p19/p19.dart create mode 100644 packages/idraw/lib/p19/p19_page.dart create mode 100644 packages/idraw/lib/p19/s01.dart create mode 100644 packages/idraw/lib/p19/s02.dart create mode 100644 packages/idraw/lib/p20/p20.dart create mode 100644 packages/idraw/lib/p20/p20_page.dart create mode 100644 packages/idraw/lib/p20/s01.dart create mode 100644 packages/idraw/lib/p20/s02.dart create mode 100644 packages/idraw/lib/p21/p21.dart create mode 100644 packages/idraw/lib/p21/p21_page.dart create mode 100644 packages/idraw/lib/p21/s01.dart create mode 100644 packages/idraw/lib/p21/s02.dart diff --git a/assets/images/body.webp b/assets/images/body.webp new file mode 100644 index 0000000000000000000000000000000000000000..662e67a7c440525a17dd8ea4593eb0a6d5343551 GIT binary patch literal 13296 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 literal 0 HcmV?d00001 diff --git a/assets/images/hand.webp b/assets/images/hand.webp new file mode 100644 index 0000000000000000000000000000000000000000..b251dd54c9a25c3ccf59f2399edaf41a6b4e604a GIT binary patch literal 2938 zcmV-=3x)JjNk&F;3jhFDMM6+kP&il$0000G0002~006xK06|PpNTUP*00E!||Nr?o z`Xs4VscqY~ZQFLPwry+Mj&0j*W_EUFr+ZgsE_G_V4ZaUH+0F0w~|}76dx!uiaaTyMmcM;`i0*>HtOty4}T(L;Tt;#Mu0aeBJ_72_)x205pB&PjV2` zE|6S_DT#PUPXL=GU@cK1WwOyb@?*}t{A)s!@S>qpPcluQ?mw@}hk zr(={9c4~B;5^UD!UNK5?xb%@oO3*!4A4gtFF#Vo3vV{{YaA@P|&k0&Q)kPe|I6278ssr!}dlu^vz+Z|8?Hg8~j^ zKG)a~X3){8N<7Z7p(bK5#i>d>&dI6F#9+HJ@xI=a*Gx>BC}kpFPgk?>f<{-=$?qGD z6Z3+R(dy*;*}4{95E-S;_ilR`6EC=^PQ1@n7bYgt6?NiyvLq)j$m>)mo+pd4@q%`* z)QQ(MHytlndQYABo=&s!f@2zp_us#)CR(zd)7!9^ShR!D^W(Eq)wKK?}xd@n#%&O}SSC?}IwT zpjebT_q9ySpyvl=KQ||#2J4i)8v<^_9EyCms*r;Q_tgB{l9e3{eWk=TD2yI#k5%Bk zS<}o9RQ%YS2LxMg2k)KD$`ERO2-^L+7K&i|93=P2Tp)Swx8Qu76~+>F1;ulxxrrvr z>0o?6oD)1}ibcXys{5oBjE3~+sfX{0p$U=8#{GJUovxRms z@%eM9H25Cm4CH$;IgBt2iVE=aLRnDW`jH>svq?!fLw{GC-`6UE^#;)*@?lK`ZK!fZ ze9k^F{BQW*@W0`I!~cf=4gVYdH~eq--|)ZTf5ZQV|NmDm09H^qAgBfa05CQHodGKU z0KNe}kw~6QrKBRFA-4EXuo4MpZsB$TA6Q%QB5tRJt@!Xk+r7_C=P$m!y%By(+aLC? z#r&qXBYyY^%KKnbRwBm5~OBz1~=U^F>+v{$7rK%q#9_AiByj`iM z$n_2+9ZPK9Cb8={Zi#+Osv5tda5U2UFZCpZkEHlz;asn7v*;;*dP}r|rd&eMs5E?i z>?&TQkdgGHOM}k}eiJ#ey>SGw9HRe4kC?Nt@e2W4U)q{{LwU3vgyU^WtUC0J5nW4( z)~|yR6!T%QZixEGr#W#$#<=t1k=iT#)ZP)rKK*iG^;^*Y3P}kUK#=EOgobG$pstyS zd0_mH@T8DQ8wCsO2WDX3lXowyijE&(C0?YEk@b;L!|VG~Pm%rbNeLfW6&yalwKVx3;YlGQ>ms9v*Y>8L zBm5~OBzo^5}Ng{!WzODEx(%GhF>$v)|0%;|Ig9vHXtSH#vA-$ceFY z$0?m+c57zr)Qu68_?CfuQ5ON^u_uGR&}^0vk4*U==qT6@^Y3UX8vWp1F|aeoD7ZsW zwZW*beLC>q_sD)@$OaxoOC~Kz{<==h77tX#sb59gdbE}i+o!2-_|tj6)$TW)KS&oD(?)F#o zZ?|KGR~+0WXPqY-?G6!QIc+n<~@ zVfAWo*&E3 z_1QoFY!bEJ_>&qp{rG8cGuE@AMPQT1X=|TA!^JK%RERj> zCwV3&D{Ky`4>t!AX-oGo@t|%(*gjR8Ftp^ zYq>$RMo$Lmo)vRr zzk))sM?EZIz*2bxNO zox-4CFqKq+%ZXv!@GY+2?p6mAfPK(tv_j_!vGK}%6=5UB;;fOW$9TyKcw6?BIqV)o k?PmffRF<4`Cir088Cmj`aXcQirIS7B8~^|S000000O=mMd;kCd literal 0 HcmV?d00001 diff --git a/lib/navigation/router/menus/menu_scope/menu_scope.dart b/lib/navigation/router/menus/menu_scope/menu_scope.dart index 9b19280..bc9d817 100644 --- a/lib/navigation/router/menus/menu_scope/menu_scope.dart +++ b/lib/navigation/router/menus/menu_scope/menu_scope.dart @@ -62,6 +62,8 @@ class MenuStore with ChangeNotifier { ?.label; void selectMenuPath(String path) { + + MenuNode root = MenuNode( path: '', label: '', @@ -83,7 +85,7 @@ class MenuStore with ChangeNotifier { }else{ _shouldAddHistory = true; } - + print("=====selectMenuPath: ${result.last.path}==============="); _state = state.copyWith( activeMenu: result.last.path, expandMenus: expandMenus); } @@ -160,10 +162,10 @@ class MenuStore with ChangeNotifier { void select(MenuNode menu) { bool hasHistory = _history.where((e) => e.menuPath==menu.path).isNotEmpty; + print("=====select: ${hasHistory}==============="); if(hasHistory){ _shouldAddHistory = false; - selectMenuPath(menu.path); - notifyListeners(); + goRouter.go(menu.path); return; } diff --git a/lib/navigation/router/routers/draw.dart b/lib/navigation/router/routers/draw.dart index 6a9ddb4..8542133 100644 --- a/lib/navigation/router/routers/draw.dart +++ b/lib/navigation/router/routers/draw.dart @@ -63,6 +63,15 @@ final RouteBase drawRouters = GoRoute( case '18': child = const P18Page(); break; + case '19': + child = const P19Page(); + break; + case '20': + child = const P20Page(); + break; + case '21': + child = const P21Page(); + break; } return CustomTransitionPage( diff --git a/lib/navigation/views/top_bar.dart b/lib/navigation/views/top_bar.dart index def4058..e8f824c 100644 --- a/lib/navigation/views/top_bar.dart +++ b/lib/navigation/views/top_bar.dart @@ -117,9 +117,6 @@ class _RouterIndicatorState extends State { if(matches.isEmpty) return const SizedBox(); RouteMatch match = _delegate.currentConfiguration.matches.last; - print( - "=========_RouterIndicatorState:build==${match.matchedLocation}========"); - return TolyBreadcrumb( fontSize: 12, items: pathToBreadcrumbItems(context, match.matchedLocation), diff --git a/packages/idraw/lib/idraw.dart b/packages/idraw/lib/idraw.dart index 291f0f6..72651d5 100644 --- a/packages/idraw/lib/idraw.dart +++ b/packages/idraw/lib/idraw.dart @@ -18,3 +18,6 @@ export 'p15/p15.dart'; export 'p16/p16.dart'; export 'p17/p17.dart'; export 'p18/p18.dart'; +export 'p19/p19.dart'; +export 'p20/p20.dart'; +export 'p21/p21.dart'; diff --git a/packages/idraw/lib/p18/p18_page.dart b/packages/idraw/lib/p18/p18_page.dart index e7f6df5..c1f73ab 100644 --- a/packages/idraw/lib/p18/p18_page.dart +++ b/packages/idraw/lib/p18/p18_page.dart @@ -20,7 +20,7 @@ class P18Page extends StatelessWidget { s2.Paper(), s3.Paper(), s4.Paper(), - // s5.Paper(), + s5.Paper(), ], ); } diff --git a/packages/idraw/lib/p18/s05.dart b/packages/idraw/lib/p18/s05.dart index 4c253ac..481c21d 100644 --- a/packages/idraw/lib/p18/s05.dart +++ b/packages/idraw/lib/p18/s05.dart @@ -98,9 +98,10 @@ class _WorldState extends State with SingleTickerProviderStateMixin { for (int i = 0; i < imageSrc.width; i++) { for (int j = 0; j < imageSrc.height; j++) { image.PixelUint8 pixel = imageSrc.getPixel(i, j) as image.PixelUint8; + var color = Color.fromARGB(pixel.a.toInt(),pixel.r.toInt(),pixel.g.toInt(),pixel.b.toInt()); + print('-($i,$j)----${color.value}---------------'); - if (pixel.toString() != '(255, 255, 255, 0)') { - // print('-($i,$j)----${imageSrc.getPixel(i, j)}---------------'); + if (color.value == 4278190080) { Particle particle = Particle( x: i * 1.0+ offsetX, @@ -115,9 +116,7 @@ class _WorldState extends State with SingleTickerProviderStateMixin { } } } - setState(() { - - }); + pm.tick(); } } diff --git a/packages/idraw/lib/p19/p19.dart b/packages/idraw/lib/p19/p19.dart new file mode 100644 index 0000000..e697916 --- /dev/null +++ b/packages/idraw/lib/p19/p19.dart @@ -0,0 +1 @@ +export 'p19_page.dart'; \ No newline at end of file diff --git a/packages/idraw/lib/p19/p19_page.dart b/packages/idraw/lib/p19/p19_page.dart new file mode 100644 index 0000000..ed1cae7 --- /dev/null +++ b/packages/idraw/lib/p19/p19_page.dart @@ -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 P19Page extends StatelessWidget { + const P19Page({super.key}); + + @override + Widget build(BuildContext context) { + return const DemoShower( + srcCodeDir: 'draw/p19', + + demos: [ + s1.Paper(), + s2.Paper(), + ], + ); + } +} diff --git a/packages/idraw/lib/p19/s01.dart b/packages/idraw/lib/p19/s01.dart new file mode 100644 index 0000000..15424b5 --- /dev/null +++ b/packages/idraw/lib/p19/s01.dart @@ -0,0 +1,301 @@ +import 'dart:math'; + +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.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: LayoutBuilder( + builder: (ctx, constraints) => World(size: constraints.biggest), + ), + ); + } +} + +class World extends StatefulWidget { + final Size size; + + const World({Key? key, required this.size}) : super(key: key); + + @override + _WorldState createState() => _WorldState(); +} + +class _WorldState extends State with SingleTickerProviderStateMixin { + late AnimationController _controller; + ParticleManage pm = ParticleManage(); + Random random = Random(); + + @override + void initState() { + super.initState(); + loadImageFromAssets("assets/images/sword.png"); + pm.size = widget.size; + + initParticles(); + + _controller = AnimationController( + duration: const Duration(seconds: 1), + vsync: this, + ) + ..addListener(pm.tick) + ..repeat(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + theWorld() { + if (_controller.isAnimating) { + _controller.stop(); + } else { + _controller.repeat(); + } + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Container( + width: widget.size.width, + height: widget.size.height, + child: Image.asset( + 'assets/images/bg.jpeg', + fit: BoxFit.cover, + )), + GestureDetector( + onTap: theWorld, + child: CustomPaint( + size: pm.size, + painter: WorldRender( + manage: pm, + ), + ), + ), + ], + ); + } + + //读取 assets 中的图片 + void loadImageFromAssets(String path) async { + ByteData data = await rootBundle.load(path); + pm.setImage(await decodeImageFromList(data.buffer.asUint8List())); + } + + void initParticles() { + for (int i = 0; i < 60; i++) { + Particle particle = Particle( + x: pm.size.width / 60 * i, + y: 0, + vx: 1 * random.nextDouble() * pow(-1, random.nextInt(20)), + vy: 4 * random.nextDouble() + 1); + pm.particles.add(particle); + } + } +} + +class WorldRender extends CustomPainter { + final ParticleManage manage; + + Paint fillPaint = Paint() + ..colorFilter = ColorFilter.matrix([ + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0.4, + 0, + ]); + + Paint stokePaint = Paint() + ..strokeWidth = 0.5 + ..style = PaintingStyle.stroke; + + WorldRender({required this.manage}) : super(repaint: manage); + + @override + void paint(Canvas canvas, Size size) { + if (manage.image == null) return; + // canvas.drawRect(Offset.zero&size, stokePaint); + manage.particles.forEach((particle) { + drawParticle(canvas, particle); + }); + } + + void drawParticle(Canvas canvas, Particle particle) { + fillPaint.color = particle.color; + if (manage.image == null) return; + canvas.save(); + canvas.translate(particle.x, particle.y); + var dis = sqrt(particle.vy * particle.vy + particle.vx * particle.vx); + canvas.rotate(acos(particle.vx / dis) + pi + pi / 2); + canvas.drawImageRect( + manage.image!, + Rect.fromLTWH( + 0, 0, manage.image!.width * 1.0, manage.image!.height * 1.0), + Rect.fromLTWH( + 0, 0, manage.image!.width * 0.18, manage.image!.height * 0.18), + fillPaint); + canvas.restore(); + } + + @override + bool shouldRepaint(covariant WorldRender oldDelegate) => + oldDelegate.manage != manage; +} + +class ParticleManage with ChangeNotifier { + List particles = []; + Random random = Random(); + ui.Image? image; + + void setImage(ui.Image image) { + this.image = image; + notifyListeners(); + } + + Size size; + + ParticleManage({this.size = Size.zero}); + + void setParticles(List particles) { + this.particles = particles; + } + + void addParticle(Particle particle) { + particles.add(particle); + notifyListeners(); + } + + void tick() { + for (int i = 0; i < particles.length; i++) { + doUpdate(particles[i]); + } + notifyListeners(); + } + + void doUpdate(Particle p) { + p.vy += p.ay; // y加速度变化 + p.vx += p.ax; // x加速度变化 + p.x += p.vx; + p.y += p.vy; + + if (p.x > size.width) { + p.x = size.width; + p.vx = -p.vx; + } + + if (p.x < 0) { + p.x = 0; + p.vx = -p.vx; + } + + if (p.y > size.height) { + p.y = 0; + } + } + + void reset() { + particles.forEach((p) { + p.x = 0; + }); + notifyListeners(); + } + + Color randomRGB({ + int limitR = 0, + int limitG = 0, + int limitB = 0, + }) { + var r = limitR + random.nextInt(256 - limitR); //红值 + var g = limitG + random.nextInt(256 - limitG); //绿值 + var b = limitB + random.nextInt(256 - limitB); //蓝值 + return Color.fromARGB(255, r, g, b); //生成argb模式的颜色 + } +} + +class Particle { + /// x 位移. + double x; + + /// y 位移. + double y; + + /// 粒子水平速度. + double vx; + + // 粒子水平加速度 + double ax; + + // 粒子竖直加速度 + double ay; + + ///粒子竖直速度. + double vy; + + /// 粒子大小. + double size; + + /// 粒子颜色. + Color color; + + Particle({ + this.x = 0, + this.y = 0, + this.ax = 0, + this.ay = 0, + this.vx = 0, + this.vy = 0, + this.size = 0, + this.color = Colors.black, + }); + + Particle copyWith( + {double? x, + double? y, + double? ax, + double? ay, + double? vx, + double? vy, + double? size, + Color? color}) => + Particle( + x: x ?? this.x, + y: y ?? this.y, + ax: ax ?? this.ax, + ay: ay ?? this.ay, + vx: vx ?? this.vx, + vy: vy ?? this.vy, + size: size ?? this.size, + color: color ?? this.color); +} diff --git a/packages/idraw/lib/p19/s02.dart b/packages/idraw/lib/p19/s02.dart new file mode 100644 index 0000000..5b3a3d1 --- /dev/null +++ b/packages/idraw/lib/p19/s02.dart @@ -0,0 +1,473 @@ +import 'dart:convert'; +import 'dart:math'; +import 'dart:ui'; + +// import 'dart:ui' as ui; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.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: Screen(), + ); + } +} + + + + +final easingDelayDuration = Duration(seconds: 15); + +class BgFx extends ClockFx { + BgFx({required Size size, required DateTime time}) + : super( + size: size, + time: time, + numParticles: 120, + ); + + @override + void tick(Duration duration) { + var secFrac = 1 - (DateTime.now().millisecond / 1000); + + var vecSpeed = duration.compareTo(easingDelayDuration) > 0 + ? max(.2, Curves.easeInOutSine.transform(secFrac)) + : 1; + + particles.forEach((p) { + p.y -= p.vy * vecSpeed; + + if (p.y > height || p.y < 0 || p.life == 0) { + _activateParticle(p); + } + }); + + super.tick(duration); + } + + void _activateParticle(Particle p) { + var xRnd = Rnd.getDouble(0, width / 5); + p.x = Rnd.getBool() ? width - xRnd : 0 + xRnd; + p.y = Rnd.getDouble(0, height); + p.a = Rnd.ratio > .95 ? Rnd.getDouble(.6, .8) : Rnd.getDouble(.08, .4); + p.isFilled = Rnd.getBool(); + p.size = Rnd.getDouble(height / 20, height / 5); + + p.life = 1; + + p.vx = 0; + p.vy = Rnd.getDouble(-3, 3); + + double v = Rnd.getDouble(.1, .5); + + p.vx = 0; + p.vy *= v; + } +} + + +class ClockBgParticlePainter extends CustomPainter { + ClockFx fx; + + ClockBgParticlePainter({required this.fx}) : super(repaint: fx); + + @override + void paint(Canvas canvas, Size size) { + canvas.clipRect(Offset.zero&size); + fx.particles.forEach((p) { + var pos = Offset(p.x, p.y); + + var paint = Paint() + ..color = p.color.withAlpha((255 * p.a).floor()) + ..strokeWidth = p.size * .2 + ..style = p.isFilled ? PaintingStyle.fill : PaintingStyle.stroke; + + if (p.isFilled) { + var rect = Rect.fromCenter( + center: pos, + width: p.size, + height: p.size, + ); + + canvas.drawRect(rect, paint); + } else { + canvas.drawCircle(pos, p.size / 1.2, paint); + } + }); + } + + @override + bool shouldRepaint(_) => false; +} + + +final Color transparent = Color.fromARGB(0, 0, 0, 0); + +abstract class ClockFx with ChangeNotifier { + /// 可用画板宽度 + double width = 0; + + /// 可用画板盖度 + double height = 0; + + /// 宽高最小值 + double sizeMin = 0; + + /// 画板中心 + Offset center = Offset.zero; + + /// 产生新粒子的区域 + Rect spawnArea = Rect.zero; + + /// 绘制时的颜色集 + Palette? palette; + + /// 粒子集合 + late List particles; + + /// 最大粒子数 + int numParticles; + + /// Date and time used for rendering time-specific effects. + DateTime time; + + ClockFx({ + required Size size, + required this.time, + this.numParticles = 5000, + }) { + this.time = time; + + particles = List.filled(numParticles, Particle()); + // growableList.length = 3; + palette = Palette(components: [transparent, transparent]); + setSize(size); + } + + /// Initializes the particle effect by resetting all the particles and assigning a random color from the palette. + void init() { + if (palette == null) return; + for (int i = 0; i < numParticles; i++) { + var color = Rnd.getItem(palette!.components); + particles[i] = Particle(color: color); + resetParticle(i); + } + } + + /// Sets the palette used for coloring. + void setPalette(Palette palette) { + this.palette = palette; + var colors = palette.components.sublist(1); + var accentColor = colors[colors.length - 1]; + particles.where((p) => p != null).forEach((p) => p.color = + p.type == ParticleType.noise ? Rnd.getItem(colors) : accentColor); + } + + /// Sets the time used for time-specific effects. + void setTime(DateTime time) { + this.time = time; + } + + /// Sets the canvas size and updates dependent values. + void setSize(Size size) { + width = size.width; + height = size.height; + sizeMin = min(width, height); + center = Offset(width / 2, height / 2); + spawnArea = Rect.fromLTRB( + center.dx - sizeMin / 100, + center.dy - sizeMin / 100, + center.dx + sizeMin / 100, + center.dy + sizeMin / 100, + ); + init(); + } + + /// Resets a particle's values. + Particle resetParticle(int i) { + Particle p = particles[i]; + p.size = p.a = p.vx = p.vy = p.life = p.lifeLeft = 0; + p.x = center.dx; + p.y = center.dy; + return p; + } + + void tick(Duration duration) { + notifyListeners(); + } +} + + +/// Holds a list of colors. +class Palette { + + /// The palette's color members. All unique. + List components; + + Palette({required this.components}); + + /// Creates a new palette from JSON. + factory Palette.fromJson(List json) { + var components = json.map((c) => Color(int.tryParse(c)!)).toList(); + return Palette(components: components); + } +} + +class Screen extends StatefulWidget { + @override + _ScreenState createState() => _ScreenState(); +} + +class _ScreenState extends State with SingleTickerProviderStateMixin{ + late BgFx _bgFx; + late Ticker _ticker; + Palette? _palette; + + @override + void initState() { + _bgFx = BgFx( + size: Size(300, 200), + time: DateTime.now(), + ); + _init(); + + + super.initState(); + } + + + @override + void dispose() { + _ticker.dispose(); + super.dispose(); + } + + + + @override + Widget build(BuildContext context) { + return Center(child: Container( + color: Color(0xff98D9B6).withAlpha(22), + width: 300, + height: 200, + child: _buildBgBlurFx())); + } + + void _init() async{ + List palettes = await _loadPalettes(); + _palette = palettes[4]; + if(_palette==null) return; + _bgFx.setPalette(_palette!); + _ticker = createTicker(_tick)..start(); + + setState(() { + + }); + } + + Future> _loadPalettes() async { + String data = await DefaultAssetBundle.of(context).loadString("assets/palettes.json"); + var palettes = json.decode(data) as List; + return palettes.map((p) => Palette.fromJson(p)).toList(); + } + + + Widget _buildBgBlurFx() { + return + RepaintBoundary( + child: Stack( + children: [ + CustomPaint( + + painter: ClockBgParticlePainter( + fx: _bgFx, + ), + child: Container(), + ), + BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 300 * .05, + sigmaY: 0, + ), + // filter: ImageFilter.blur(sigmaX: 0, sigmaY: 0), + child: Container(child: Text(" ")), + ), + ], + ), + ); + } + + + void _tick(Duration duration) { + if (DateTime.now().second % 5 == 0) _bgFx.tick(duration); + } +} + +class Particle { + + /// 粒子 x 位置 + double x; + + /// 粒子 y 位置 + double y; + + /// 粒子角度(弧度制) + double a; + + /// 粒子水平速度 + double vx; + + /// 粒子垂直速度 + double vy; + + /// 距画布中心距离 + double dist; + + /// 到画布中心距离的百分比 (0-1). + double distFrac; + + /// 粒子大小 + double size; + + /// 粒子生命长度 (0-1). + double life; + + /// 粒子右侧粒子存货时间 (0-1). + double lifeLeft; + + /// I粒子是否填充. + bool isFilled; + + /// 粒子是否需要 "speed marks". + bool isFlowing; + + /// 粒子颜色. + Color color; + + /// 粒子描述 + int distribution; + + /// 粒子类型 + ParticleType type; + + Particle({ + this.x = 0, + this.y = 0, + this.a = 0, + this.vx = 0, + this.vy = 0, + this.dist = 0, + this.distFrac = 0, + this.size = 0, + this.life = 0, + this.lifeLeft = 0, + this.isFilled = false, + this.isFlowing = false, + this.color = Colors.black, + this.distribution = 0, + this.type = ParticleType.noise, + }); +} + +enum ParticleType { + hour, + minute, + noise, +} + + +class Rnd { + static int _seed = DateTime.now().millisecondsSinceEpoch; + static Random random = Random(_seed); + + static set seed(int val) => random = Random(_seed = val); + static int get seed => _seed; + + /// Gets the next double. + static get ratio => random.nextDouble(); + + /// Gets a random int between [min] and [max]. + static int getInt(int min, int max) { + return min + random.nextInt(max - min); + } + + /// Gets a random double between [min] and [max]. + static double getDouble(double min, double max) { + return min + random.nextDouble() * (max - min); + } + + /// Gets a random boolean with chance [chance]. + static bool getBool([double chance = 0.5]) { + return random.nextDouble() < chance; + } + + /// Randomize the positions of items in a list. + static List shuffle(List list) { + for (int i = 0, l = list.length; i < l; i++) { + int j = random.nextInt(l); + if (j == i) { + continue; + } + dynamic item = list[j]; + list[j] = list[i]; + list[i] = item; + } + return list; + } + + /// Randomly selects an item from a list. + static dynamic getItem(List list) { + return list[random.nextInt(list.length)]; + } + + /// Gets a random palette from a list of palettes and sorts its' colors by luminance. + /// + /// Given if [dark] or not, this method makes sure the luminance of the background color is valid. + static Palette getPalette(List palettes, bool dark) { + Palette? result; + + while (result == null) { + Palette palette = Rnd.getItem(palettes); + List colors = Rnd.shuffle(palette.components).map((e) => e).toList(); + + var luminance = colors[0].computeLuminance(); + + if (dark ? luminance <= .1 : luminance >= .1) { + var lumDiff = colors + .sublist(1) + .asMap() + .map( + (i, color) => MapEntry( + i, + [i, (luminance - color.computeLuminance()).abs()], + ), + ) + .values + .toList(); + + lumDiff.sort((List a, List b) { + return a[1].compareTo(b[1]); + }); + + List sortedColors = + lumDiff.map((d) => colors[(d[0] + 1).toInt()]).toList(); + + result = Palette( + components: [colors[0]] + sortedColors, + ); + } + } + return result; + } +} \ No newline at end of file diff --git a/packages/idraw/lib/p20/p20.dart b/packages/idraw/lib/p20/p20.dart new file mode 100644 index 0000000..ec8cfc6 --- /dev/null +++ b/packages/idraw/lib/p20/p20.dart @@ -0,0 +1 @@ +export 'p20_page.dart'; \ No newline at end of file diff --git a/packages/idraw/lib/p20/p20_page.dart b/packages/idraw/lib/p20/p20_page.dart new file mode 100644 index 0000000..f7459bc --- /dev/null +++ b/packages/idraw/lib/p20/p20_page.dart @@ -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 P20Page extends StatelessWidget { + const P20Page({super.key}); + + @override + Widget build(BuildContext context) { + return const DemoShower( + srcCodeDir: 'draw/p20', + + demos: [ + s1.Paper(), + s2.Paper(), + ], + ); + } +} diff --git a/packages/idraw/lib/p20/s01.dart b/packages/idraw/lib/p20/s01.dart new file mode 100644 index 0000000..2fe0069 --- /dev/null +++ b/packages/idraw/lib/p20/s01.dart @@ -0,0 +1,340 @@ +import 'dart:math'; + +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter/services.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 ClockPanel(), + ); + } +} + +class ClockManage with ChangeNotifier { + late List particles; + late DateTime datetime; // 时间 + + /// 粒子列表 + int numParticles; + + /// 最大粒子数 + Size size; // 尺寸 + + ClockManage({this.size= Size.zero, this.numParticles = 500}) { + particles = List.filled(numParticles,Particle()); + datetime = DateTime.now(); + } + + collectParticles(DateTime datetime) { + count = 0; + particles.forEach((Particle element) { + if(element!=null){ + element.active = false; + } + }); + + collectDigit(target: datetime.hour ~/ 10, offsetRate: 0); + collectDigit(target: datetime.hour % 10, offsetRate: 1); + collectDigit(target: 10, offsetRate: 3.2); + collectDigit(target: datetime.minute ~/ 10, offsetRate: 2.5); + collectDigit(target: datetime.minute % 10, offsetRate: 3.5); + collectDigit(target: 10, offsetRate: 7.25); + collectDigit(target: datetime.second ~/ 10, offsetRate: 5); + collectDigit(target: datetime.second % 10, offsetRate: 6); + } + + int count = 0; + double _radius = 4; + + void collectDigit({int target = 0, double offsetRate = 0}) { + if (target > 10 && count > numParticles) { + return; + } + + double space = _radius * 2; + double offSetX = (digits[target][0].length * 2 * (_radius + 1) + space) * + offsetRate; + + for (int i = 0; i < digits[target].length; i++) { + for (int j = 0; j < digits[target][j].length; j++) { + if (digits[target][i][j] == 1) { + double rX = j * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心横坐标 + double rY = i * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心纵坐标 + particles[count] = Particle(x: rX + offSetX, + y: rY, size: _radius, color: Colors.blue, + active: true); + count++; + } + } + } + } + + void tick(DateTime now) { + collectParticles(now); + notifyListeners(); + } + + void doUpdate(Particle p, DateTime datetime) { + + } +} + +class ClockPainter extends CustomPainter { + final ClockManage manage; + + ClockPainter({ required this.manage}) : super(repaint: manage); + + Paint clockPaint = Paint(); + + @override + void paint(Canvas canvas, Size size) { + manage.particles.where((e) => e!=null&&e.active).forEach((particle) { + clockPaint..color = particle.color; + canvas.drawCircle( + Offset(particle.x, particle.y), particle.size, clockPaint); + }); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} +class ClockPanel extends StatefulWidget { + const ClockPanel({super.key}); + + @override + _ClockPanelState createState() => _ClockPanelState(); +} + +class _ClockPanelState extends State + with SingleTickerProviderStateMixin { + late Ticker _ticker; + late ClockManage pm; + + @override + void initState() { + super.initState(); + pm = ClockManage(size: Size(550, 200)); + + _ticker = createTicker(_tick) + ..start(); + } + + @override + void dispose() { + _ticker.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return CustomPaint( + size: pm.size, + painter: ClockPainter(manage: pm), + + ); + } + + void _tick(Duration duration) { + DateTime now = DateTime.now(); + + if (now.millisecondsSinceEpoch - pm.datetime.millisecondsSinceEpoch > 1000) { + pm..datetime = now..tick(now); + } + } +} + +class Particle { + /// x 位移. + double x; + + /// y 位移. + double y; + + /// 粒子大小. + double size; + + /// 粒子颜色. + Color color; + + bool active; // 粒子是否可用 + + double vx; /// 粒子水平速度. + double ax; // 粒子水平加速度 + double ay; // 粒子竖直加速度 + double vy; ///粒子竖直速度. + + Particle({ + this.x = 0, + this.y = 0, + this.size = 0, + this.vx=0, + this.ax=0, + this.ay=0, + this.vy=0, + this.active = true, + this.color = Colors.black, + }); +} + +const colors = [ + Color(0x8833B5E5), + Color(0x880099CC), + Color(0x889933CC), + Color(0x8899CC00), + Color(0x88669900), + Color(0x88FFBB33), + Color(0x88FF8800), + Color(0x88FF4444), + Color(0x88CC0000) +]; + +const digits = [ + [ + [0, 0, 1, 1, 1, 0, 0], + [0, 1, 1, 0, 1, 1, 0], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 0, 1, 1, 0], + [0, 0, 1, 1, 1, 0, 0] + ], //0 + + [ + [0, 0, 0, 1, 1, 0, 0], + [0, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [1, 1, 1, 1, 1, 1, 1] + ], //1 + [ + [0, 1, 1, 1, 1, 1, 0], + [1, 1, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 1, 1, 0, 0, 0], + [0, 1, 1, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 1, 1, 1, 1, 1] + ], //2 + [ + [1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 1, 1, 0] + ], //3 + + [ + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 1, 1, 0], + [0, 0, 1, 1, 1, 1, 0], + [0, 1, 1, 0, 1, 1, 0], + [1, 1, 0, 0, 1, 1, 0], + [1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 1, 1, 1] + ], //4 + [ + [1, 1, 1, 1, 1, 1, 1], + [1, 1, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 1, 1, 0] + ], //5 + [ + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 1, 1, 0, 0, 0], + [0, 1, 1, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 0, 0], + [1, 1, 0, 1, 1, 1, 0], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 1, 1, 0] + ], //6 + [ + [1, 1, 1, 1, 1, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 0, 0, 0] + ], //7 + [ + [0, 1, 1, 1, 1, 1, 0], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 1, 1, 0], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 1, 1, 0] + ], //8 + [ + [0, 1, 1, 1, 1, 1, 0], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 1, 1, 0, 0, 0, 0] + ], //9 + [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 1, 1, 0], + [0, 1, 1, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 1, 1, 0], + [0, 1, 1, 0], + [0, 0, 0, 0], + [0, 0, 0, 0] + ] //: +]; + diff --git a/packages/idraw/lib/p20/s02.dart b/packages/idraw/lib/p20/s02.dart new file mode 100644 index 0000000..1bb4478 --- /dev/null +++ b/packages/idraw/lib/p20/s02.dart @@ -0,0 +1,480 @@ +import 'dart:convert'; +import 'dart:math'; +import 'dart:ui'; + +// import 'dart:ui' as ui; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.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 ClockPanel(), + ); + } +} + + + +class BgManage with ChangeNotifier { + late List particles; + late DateTime datetime; // 时间 + Random random = Random(); + + /// 粒子列表 + int numParticles; + + /// 最大粒子数 + Size size; // 尺寸 + + BgManage({required this.size, this.numParticles = 500}) { + particles = []; + datetime = DateTime.now(); + } + + checkParticles(DateTime now) { + //判断当前时间是否改变,再将点位放到集合中 + if ((datetime.hour ~/ 10) != (now.hour ~/ 10)) { + collectDigit(target: datetime.hour ~/ 10, offsetRate: 0); + } + if ((datetime.hour % 10) != (now.hour % 10)) { + collectDigit(target: datetime.hour % 10, offsetRate: 1); + } + if ((datetime.minute ~/ 10) != (now.minute ~/ 10)) { + collectDigit(target: datetime.minute ~/ 10, offsetRate: 2.5); + } + if ((datetime.minute % 10) != (now.minute % 10)) { + collectDigit(target: datetime.minute % 10, offsetRate: 3.5); + } + if ((datetime.second ~/ 10) != (now.second ~/ 10)) { + collectDigit(target: datetime.second ~/ 10, offsetRate: 5); + } + if ((datetime.second % 10) != (now.second % 10)) { + collectDigit(target: datetime.second % 10, offsetRate: 6); + datetime = now; + } + } + + double _radius = 4; + + void collectDigit({int target = 0, double offsetRate = 0}) { + if (target > 10) { + return; + } + + double space = _radius * 2; + double offSetX = + (digits[target][0].length * 2 * (_radius + 1) + space) * offsetRate; + + for (int i = 0; i < digits[target].length; i++) { + for (int j = 0; j < digits[target][j].length; j++) { + if (digits[target][i][j] == 1) { + double rX = j * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心横坐标 + double rY = i * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心纵坐标 + Particle particle = Particle( + x: rX + offSetX, + y: rY, + size: _radius, + color: randomColor(), + active: true, + vx: 2.5 * random.nextDouble() * pow(-1, random.nextInt(20)), + vy: 2 * random.nextDouble() + 1); + particles.add(particle); + } + } + } + } + + Color randomColor({ + int limitA = 120, + int limitR = 0, + int limitG = 0, + int limitB = 0, + }) { + var a = limitA + random.nextInt(256 - limitA); //透明度值 + var r = limitR + random.nextInt(256 - limitR); //红值 + var g = limitG + random.nextInt(256 - limitG); //绿值 + var b = limitB + random.nextInt(256 - limitB); //蓝值 + return Color.fromARGB(a, r, g, b); //生成argb模式的颜色 + } + + void tick(DateTime now) { + checkParticles(now); + + for (int i = 0; i < particles.length; i++) { + doUpdate(particles[i]); + } + notifyListeners(); + } + + void doUpdate(Particle p) { + p.vy += p.ay; // y加速度变化 + p.vx += p.ax; // x加速度变化 + p.x += p.vx; + p.y += p.vy; + + if (p.x > size.width) { + p.x = size.width; + p.vx = -p.vx; + } + + if (p.x < 0) { + p.x = 0; + p.vx = -p.vx; + } + + if (p.y > size.height) { + particles.remove(p); + } + } +} + +class BgPainter extends CustomPainter { + final BgManage manage; + + BgPainter({required this.manage}) : super(repaint: manage); + + Paint clockPaint = Paint(); + + @override + void paint(Canvas canvas, Size size) { + + manage.particles.forEach((particle) { + clockPaint..color = particle.color; + canvas.drawCircle( + Offset(particle.x, particle.y), particle.size, clockPaint); + }); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} +class ClockManage with ChangeNotifier { + late List particles; + late DateTime datetime; // 时间 + + /// 粒子列表 + int numParticles; + + /// 最大粒子数 + Size size; // 尺寸 + + ClockManage({required this.size, this.numParticles = 500}) { + particles = List.filled(numParticles,Particle()); + datetime = DateTime.now(); + } + + collectParticles(DateTime datetime) { + count = 0; + particles.forEach((Particle element) { + if(element!=null){ + element.active = false; + } + }); + + collectDigit(target: datetime.hour ~/ 10, offsetRate: 0); + collectDigit(target: datetime.hour % 10, offsetRate: 1); + collectDigit(target: 10, offsetRate: 3.2); + collectDigit(target: datetime.minute ~/ 10, offsetRate: 2.5); + collectDigit(target: datetime.minute % 10, offsetRate: 3.5); + collectDigit(target: 10, offsetRate: 7.25); + collectDigit(target: datetime.second ~/ 10, offsetRate: 5); + collectDigit(target: datetime.second % 10, offsetRate: 6); + } + + int count = 0; + double _radius = 4; + + void collectDigit({int target = 0, double offsetRate = 0}) { + if (target > 10 && count > numParticles) { + return; + } + + double space = _radius * 2; + double offSetX = (digits[target][0].length * 2 * (_radius + 1) + space) * + offsetRate; + + for (int i = 0; i < digits[target].length; i++) { + for (int j = 0; j < digits[target][j].length; j++) { + if (digits[target][i][j] == 1) { + double rX = j * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心横坐标 + double rY = i * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心纵坐标 + particles[count] = Particle(x: rX + offSetX, + y: rY, size: _radius, color: Colors.blue, + active: true); + count++; + } + } + } + } + + void tick(DateTime now) { + collectParticles(now); + notifyListeners(); + } + + void doUpdate(Particle p, DateTime datetime) { + + } +} + +class ClockPainter extends CustomPainter { + final ClockManage manage; + + ClockPainter({required this.manage}) : super(repaint: manage); + + Paint clockPaint = Paint(); + + @override + void paint(Canvas canvas, Size size) { + manage.particles.where((e) => e!=null&&e.active).forEach((particle) { + clockPaint..color = particle.color; + canvas.drawCircle( + Offset(particle.x, particle.y), particle.size, clockPaint); + }); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} + +class ClockPanel extends StatefulWidget { + const ClockPanel({super.key}); + + @override + _ClockPanelState createState() => _ClockPanelState(); +} + +class _ClockPanelState extends State + with SingleTickerProviderStateMixin { + late Ticker _ticker; + late ClockManage pm; + + late BgManage bgManage; + + @override + void initState() { + super.initState(); + pm = ClockManage(size: Size(550, 200)); + bgManage = BgManage(size: Size(550, 200)); + + _ticker = createTicker(_tick) + ..start(); + } + + @override + void dispose() { + _ticker.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return CustomPaint( + size: pm.size, + painter: ClockPainter(manage: pm), + foregroundPainter: BgPainter(manage: bgManage), + ); + } + + void _tick(Duration duration) { + DateTime now = DateTime.now(); + + if (now.millisecondsSinceEpoch - pm.datetime.millisecondsSinceEpoch > 1000) { + pm..datetime = now..tick(now); + } + bgManage..tick(now); + } +} + +class Particle { + /// x 位移. + double x; + + /// y 位移. + double y; + + /// 粒子大小. + double size; + + /// 粒子颜色. + Color color; + + bool active; // 粒子是否可用 + + double vx; /// 粒子水平速度. + double ax; // 粒子水平加速度 + double ay; // 粒子竖直加速度 + double vy; ///粒子竖直速度. + + Particle({ + this.x = 0, + this.y = 0, + this.size = 0, + this.vx=0, + this.ax=0, + this.ay=0, + this.vy=0, + this.active = true, + this.color = Colors.black, + }); +} + +const colors = [ + Color(0x8833B5E5), + Color(0x880099CC), + Color(0x889933CC), + Color(0x8899CC00), + Color(0x88669900), + Color(0x88FFBB33), + Color(0x88FF8800), + Color(0x88FF4444), + Color(0x88CC0000) +]; + +const digits = [ + [ + [0, 0, 1, 1, 1, 0, 0], + [0, 1, 1, 0, 1, 1, 0], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 0, 1, 1, 0], + [0, 0, 1, 1, 1, 0, 0] + ], //0 + + [ + [0, 0, 0, 1, 1, 0, 0], + [0, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [1, 1, 1, 1, 1, 1, 1] + ], //1 + [ + [0, 1, 1, 1, 1, 1, 0], + [1, 1, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 1, 1, 0, 0, 0], + [0, 1, 1, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 1, 1, 1, 1, 1] + ], //2 + [ + [1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 1, 1, 0] + ], //3 + + [ + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 1, 1, 0], + [0, 0, 1, 1, 1, 1, 0], + [0, 1, 1, 0, 1, 1, 0], + [1, 1, 0, 0, 1, 1, 0], + [1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 1, 1, 1] + ], //4 + [ + [1, 1, 1, 1, 1, 1, 1], + [1, 1, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 1, 1, 0] + ], //5 + [ + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 1, 1, 0, 0, 0], + [0, 1, 1, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 0, 0], + [1, 1, 0, 1, 1, 1, 0], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 1, 1, 0] + ], //6 + [ + [1, 1, 1, 1, 1, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 0, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 0, 0, 0] + ], //7 + [ + [0, 1, 1, 1, 1, 1, 0], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 1, 1, 0], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 1, 1, 0] + ], //8 + [ + [0, 1, 1, 1, 1, 1, 0], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 1, 0, 0], + [0, 1, 1, 0, 0, 0, 0] + ], //9 + [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 1, 1, 0], + [0, 1, 1, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 1, 1, 0], + [0, 1, 1, 0], + [0, 0, 0, 0], + [0, 0, 0, 0] + ] //: +]; diff --git a/packages/idraw/lib/p21/p21.dart b/packages/idraw/lib/p21/p21.dart new file mode 100644 index 0000000..fc9c20b --- /dev/null +++ b/packages/idraw/lib/p21/p21.dart @@ -0,0 +1 @@ +export 'p21_page.dart'; \ No newline at end of file diff --git a/packages/idraw/lib/p21/p21_page.dart b/packages/idraw/lib/p21/p21_page.dart new file mode 100644 index 0000000..1c124d7 --- /dev/null +++ b/packages/idraw/lib/p21/p21_page.dart @@ -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 P21Page extends StatelessWidget { + const P21Page({super.key}); + + @override + Widget build(BuildContext context) { + return const DemoShower( + srcCodeDir: 'draw/p21', + + demos: [ + s1.Paper(), + s2.Paper(), + ], + ); + } +} diff --git a/packages/idraw/lib/p21/s01.dart b/packages/idraw/lib/p21/s01.dart new file mode 100644 index 0000000..4bd9dcd --- /dev/null +++ b/packages/idraw/lib/p21/s01.dart @@ -0,0 +1,241 @@ +import 'dart:math'; + +import 'dart:ui' as ui; + +import 'package:dash_painter/dash_painter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter/services.dart'; + +/// create by 张风捷特烈 on 2020/11/5 +/// contact me by email 1981462002@qq.com +/// 说明: + +class Paper extends StatefulWidget { + const Paper({Key? key}) : super(key: key); + + @override + State createState() => _PaperState(); +} + +class _PaperState extends State + with SingleTickerProviderStateMixin { + Line line = Line(start: const Offset(20, 20), end: const Offset(50, 80)); + + late AnimationController ctrl; + + @override + void initState() { + super.initState(); + ctrl = AnimationController( + vsync: this, + duration: const Duration(seconds: 3), + )..addListener(_updateLine); + } + + @override + void dispose() { + line.dispose(); + ctrl.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + + return Center( + child: GestureDetector( + onTap: () { + // line.record(); + ctrl.forward(from: 0); + }, + child: CustomPaint( + painter: AnglePainter(line: line + // linker: linker + ), + child: Container( + color: Colors.grey.withOpacity(0.1), + height: 200, + width: 200, + ), + ), + ), + ); + } + + void _updateLine() { + // print("${ctrl.value * 2 * pi}"); + Offset center = line.percent(0.2); + line.rotate(ctrl.value * 2 * pi, centre: center); + // line.rotate(ctrl.value * 2* pi); + } +} + +class AnglePainter extends CustomPainter { + final DashPainter dashPainter = const DashPainter(span: 4, step: 4); + + AnglePainter({required this.line}) : super(repaint: line); + + final Paint pointPaint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1; + + final Paint helpPaint = Paint() + ..style = PaintingStyle.stroke + ..color = Colors.lightBlue + ..strokeWidth = 1; + + final TextPainter textPainter = TextPainter( + textAlign: TextAlign.center, + textDirection: TextDirection.ltr, + ); + + final Line line; + + @override + void paint(Canvas canvas, Size size) { + canvas.translate(size.width / 2, size.height / 2); + drawHelp(canvas, size); + line.paint(canvas); + } + + void drawHelp(Canvas canvas, Size size) { + Path helpPath = Path() + ..moveTo(-size.width / 2, 0) + ..relativeLineTo(size.width, 0) + ..moveTo(0, -size.height / 2) + ..relativeLineTo(0, size.height); + dashPainter.paint(canvas, helpPath, helpPaint); + + drawHelpText('0°', canvas, Offset(size.width / 2 - 20, 0)); + drawHelpText('p0', canvas, line.start.translate(-20, 0)); + drawHelpText('p1', canvas, line.end.translate(-20, 0)); + + // drawHelpText('p2', canvas, Offset(60, 40).translate(10, 0)); + // drawAnchor(canvas, Offset(60, 40)); + drawAnchor(canvas, line.percent(0.2)); + // drawAnchor(canvas, line.percent(0.5)); + + // drawAnchor(canvas, line.percent(0.8)); + + drawHelpText( + '角度: ${(line.positiveRad * 180 / pi).toStringAsFixed(2)}°', + canvas, + Offset( + -size.width / 2 + 10, + -size.height / 2 + 10, + ), + ); + + + + // canvas.drawArc( + // Rect.fromCenter(center: line.start, width: 20, height: 20), + // 0, + // line.positiveRad, + // false, + // helpPaint, + // ); + + // canvas.save(); + // Offset center = const Offset(60, 60); + // canvas.translate(center.dx, center.dy); + // canvas.rotate(line.positiveRad); + // canvas.translate(-center.dx, -center.dy); + // canvas.drawCircle(center, 4, helpPaint); + // canvas.drawRect( + // Rect.fromCenter(center: center, width: 30, height: 60), helpPaint); + // canvas.restore(); + } + + void drawAnchor(Canvas canvas, Offset offset) { + canvas.drawCircle(offset, 4, pointPaint..style = PaintingStyle.stroke); + canvas.drawCircle(offset, 2, pointPaint..style = PaintingStyle.fill); + } + + void drawHelpText( + String text, + Canvas canvas, + Offset offset, { + Color color = Colors.lightBlue, + }) { + textPainter.text = TextSpan( + text: text, + style: TextStyle(fontSize: 12, color: color), + ); + textPainter.layout(maxWidth: 200); + textPainter.paint(canvas, offset); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; + } +} + +class Line with ChangeNotifier { + Line({ + this.start = Offset.zero, + this.end = Offset.zero, + }); + + Offset start; + Offset end; + + final Paint pointPaint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1; + + void paint(Canvas canvas) { + canvas.drawLine(start, end, pointPaint); + drawAnchor(canvas, start); + drawAnchor(canvas, end); + } + + double get rad => (end - start).direction; + + double get positiveRad => rad < 0 ? 2 * pi + rad : rad; + + double get length => (end - start).distance; + + void drawAnchor(Canvas canvas, Offset offset) { + canvas.drawCircle(offset, 4, pointPaint..style = PaintingStyle.stroke); + canvas.drawCircle(offset, 2, pointPaint..style = PaintingStyle.fill); + } + + double detaRotate = 0; + + void rotate(double rotate, {Offset? centre}) { + detaRotate = rotate - detaRotate; + centre = centre ?? start; + Line p2p0 = Line(start: centre, end: start); + Line p2p1 = Line(start: centre, end: end); + p2p0._rotateByStart(detaRotate); + p2p1._rotateByStart(detaRotate); + start = p2p0.end; + end = p2p1.end; + detaRotate = rotate; + notifyListeners(); + } + + + + Offset percent(double percent){ + return Offset( + length*percent*cos(rad), + length*percent*sin(rad), + )+start; + } + + void _rotateByStart(double rotate) { + end = Offset( + length * cos(rad + rotate), + length * sin(rad + rotate), + ) + + start; + } + + void tick() { + notifyListeners(); + } +} \ No newline at end of file diff --git a/packages/idraw/lib/p21/s02.dart b/packages/idraw/lib/p21/s02.dart new file mode 100644 index 0000000..e80e4c3 --- /dev/null +++ b/packages/idraw/lib/p21/s02.dart @@ -0,0 +1,291 @@ +import 'dart:math'; + +import 'dart:ui' as ui; + +import 'package:dash_painter/dash_painter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter/services.dart'; + +/// create by 张风捷特烈 on 2020/11/5 +/// contact me by email 1981462002@qq.com +/// 说明: + +class Paper extends StatefulWidget { + const Paper({Key? key}) : super(key: key); + + + @override + State createState() => _PaperState(); +} + +class _PaperState extends State + with SingleTickerProviderStateMixin { + + Line line = Line(); + + late AnimationController ctrl; + ui.Image? _image; + ui.Image? _bgImage; + + void _loadImage() async { + ByteData data = await rootBundle.load('assets/images/hand.webp'); + List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + _image = await decodeImageFromList(Uint8List.fromList(bytes)); + line.attachImage(ImageZone( + rect: const Rect.fromLTRB(0, 93, 104, 212), + image: _image!, + )); + } + + void _loadImage2() async { + ByteData bgData = await rootBundle.load('assets/images/body.webp'); + List bgBytes = bgData.buffer.asUint8List(bgData.offsetInBytes, bgData.lengthInBytes); + _bgImage = await decodeImageFromList(Uint8List.fromList(bgBytes)); + setState(() { + + }); + } + + @override + void initState() { + super.initState(); + ctrl = AnimationController( + vsync: this, + duration: const Duration(seconds: 1), + )..addListener(_updateLine)..forward(); + _loadImage(); + _loadImage2(); + } + + @override + void dispose() { + line.dispose(); + ctrl.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + // line.start = Offset.zero; + // line.end = Offset(40, 0); + // line.rotate(2.4085543677521746); + + return Scaffold( + body: Center( + child: GestureDetector( + onTap: () { + // line.record(); + ctrl.repeat(reverse: true); + }, + child: + CustomPaint( + painter: AnglePainter(line: line, + // linker: linker + image:_bgImage), + child: Container( + // color: Colors.grey.withOpacity(0.1), + // height: 200, + // width: 200, + ), + ), + ), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } + + void _updateLine() { + // print("${ctrl.value * 2 * pi}"); + line.rotate(ctrl.value * 2* pi/50); + } +} +class ImageZone { + final ui.Image image; + final Rect rect; + + Line? _line; + + final Paint imagePaint = Paint()..filterQuality = FilterQuality.high; + + ImageZone({required this.image, this.rect = Rect.zero}); + + Line get line { + if (_line != null) { + return _line!; + } + Offset start = Offset( + -(image.width / 2 - rect.right), -(image.height / 2 - rect.bottom)); + Offset end = start.translate(-rect.width, -rect.height); + _line = Line(start: start, end: end); + return _line!; + } + + void paint(Canvas canvas, Line line) { + canvas.save(); + canvas.translate(line.start.dx, line.start.dy); + canvas.rotate(line.positiveRad - this.line.positiveRad); + canvas.translate(-line.start.dx, -line.start.dy); + canvas.drawImageRect( + image, + rect, + rect.translate(-image.width / 2, -image.height / 2), + imagePaint, + ); + canvas.restore(); + } +} +class AnglePainter extends CustomPainter { + final DashPainter dashPainter = const DashPainter(span: 4, step: 4); + ui.Image? image; + + AnglePainter({required this.line, this.image}) : super(repaint: line); + + final Paint helpPaint = Paint() + ..style = PaintingStyle.stroke + ..color = Colors.lightBlue + ..strokeWidth = 1; + + final TextPainter textPainter = TextPainter( + textAlign: TextAlign.center, + textDirection: TextDirection.ltr, + ); + + final Line line; + + @override + void paint(Canvas canvas, Size size) { + canvas.translate(size.width / 2, size.height / 2); + line.paint(canvas); + // drawHelp(canvas, size); + + if (image != null) { + canvas.drawImage( + image!, Offset(-image!.width / 2, -image!.height / 2), Paint()); + // drawHelp(canvas, Size(image!.width.toDouble() , image!.height.toDouble())); + } + } + + void drawHelp(Canvas canvas, Size size) { + + Path helpPath = Path() + ..moveTo(-size.width / 2, 0) + ..relativeLineTo(size.width, 0) + ..moveTo(0, -size.height / 2) + ..relativeLineTo(0, size.height); + + dashPainter.paint(canvas, helpPath, helpPaint); + + // drawHelpText('0°', canvas, Offset(size.width / 2 - 20, 0)); + // drawHelpText('p0', canvas, line.start.translate(-20, 0)); + // drawHelpText('p1', canvas, line.end.translate(-20, 0)); + // + // drawHelpText( + // '角度: ${(line.positiveRad * 180 / pi).toStringAsFixed(2)}°', + // canvas, + // Offset( + // -size.width / 2 + 10, + // -size.height / 2 + 10, + // ), + // ); + + // canvas.drawArc( + // Rect.fromCenter(center: line.start, width: 20, height: 20), + // 0, + // line.positiveRad, + // false, + // helpPaint, + // ); + + // canvas.save(); + // Offset center = const Offset(60, 60); + // canvas.translate(center.dx, center.dy); + // canvas.rotate(line.positiveRad); + // canvas.translate(-center.dx, -center.dy); + // canvas.drawCircle(center, 4, helpPaint); + // canvas.drawRect( + // Rect.fromCenter(center: center, width: 30, height: 60), helpPaint); + // canvas.restore(); + } + + void drawHelpText( + String text, + Canvas canvas, + Offset offset, { + Color color = Colors.lightBlue, + }) { + textPainter.text = TextSpan( + text: text, + style: TextStyle(fontSize: 12, color: color), + ); + textPainter.layout(maxWidth: 200); + textPainter.paint(canvas, offset); + } + + @override + bool shouldRepaint(covariant AnglePainter oldDelegate) { + return oldDelegate.image != image; + } +} + +class Line with ChangeNotifier { + Line({ + this.start = Offset.zero, + this.end = Offset.zero, + }); + + Offset start; + Offset end; + ImageZone? _zone; + + void attachImage(ImageZone zone) { + _zone = zone; + start = zone.line.start; + end = zone.line.end; + notifyListeners(); + } + + final Paint pointPaint = Paint() + ..style = PaintingStyle.fill + ..color = Colors.red + ..strokeWidth = 1; + + void paint(Canvas canvas) { + _zone?.paint(canvas, this); + // canvas.save(); + // canvas.translate(start.dx, start.dy); + // canvas.rotate(positiveRad); + // Path arrowPath = Path(); + // arrowPath + // ..relativeLineTo(length - 10, 3) + // ..relativeLineTo(0, 2) + // ..lineTo(length, 0) + // ..relativeLineTo(-10, -5) + // ..relativeLineTo(0, 2) + // ..close(); + // canvas.drawPath(arrowPath, pointPaint); + // canvas.restore(); + } + + double get rad => (end - start).direction; + + double get positiveRad => rad < 0 ? 2 * pi + rad : rad; + + double get length => (end - start).distance; + + double detaRotate = 0; + + void rotate(double rotate) { + detaRotate = rotate - detaRotate; + end = Offset( + length * cos(rad + detaRotate), + length * sin(rad + detaRotate), + ) + + start; + detaRotate = rotate; + notifyListeners(); + } + + void tick() { + notifyListeners(); + } +} \ No newline at end of file diff --git a/packages/idraw/pubspec.yaml b/packages/idraw/pubspec.yaml index 4d16f45..f650ba1 100644 --- a/packages/idraw/pubspec.yaml +++ b/packages/idraw/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: sdk: flutter image: ^4.1.3 dio: ^5.4.0 + dash_painter: ^1.0.2 components: path: ../components dev_dependencies: diff --git a/pubspec.lock b/pubspec.lock index 7cbdb5c..502740e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -80,6 +80,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.5" + dash_painter: + dependency: transitive + description: + name: dash_painter + sha256: e0b24070aed0549f9139ef1276ca70c155fe78960ec624d6dec3cdb0502f9a2a + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.2" dio: dependency: transitive description: