From 709fe248a73b03e398e374efd3e566b690ca93a3 Mon Sep 17 00:00:00 2001 From: toly <1981462002@qq.com> Date: Fri, 23 Apr 2021 16:52:32 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=98=E5=88=B6=E9=9B=86=E5=BD=95-=E4=BA=95?= =?UTF-8?q?=E5=AD=97=E6=A3=8B=E7=BB=98=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{base => fun}/random_portrait.dart | 0 lib/painter_system/fun/stemp/stamp_data.dart | 145 +++++++++++ lib/painter_system/fun/stemp/stamp_paper.dart | 227 ++++++++++++++++++ lib/painter_system/gallery.dart | 2 +- lib/painter_system/gallery_factory.dart | 9 +- lib/views/pages/gallery/gallery_page.dart | 2 +- 6 files changed, 382 insertions(+), 3 deletions(-) rename lib/painter_system/{base => fun}/random_portrait.dart (100%) create mode 100644 lib/painter_system/fun/stemp/stamp_data.dart create mode 100644 lib/painter_system/fun/stemp/stamp_paper.dart diff --git a/lib/painter_system/base/random_portrait.dart b/lib/painter_system/fun/random_portrait.dart similarity index 100% rename from lib/painter_system/base/random_portrait.dart rename to lib/painter_system/fun/random_portrait.dart diff --git a/lib/painter_system/fun/stemp/stamp_data.dart b/lib/painter_system/fun/stemp/stamp_data.dart new file mode 100644 index 0000000..9b9a21f --- /dev/null +++ b/lib/painter_system/fun/stemp/stamp_data.dart @@ -0,0 +1,145 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +class StampData extends ChangeNotifier { + final List stamps = []; + + void push(Stamp stamp) { + stamps.add(stamp); + notifyListeners(); + } + + void removeLast() { + stamps.removeLast(); + notifyListeners(); + } + + void activeLast({Color color = Colors.blue}) { + stamps.last.color = color; + notifyListeners(); + } + + void clear() { + stamps.clear(); + notifyListeners(); + } + + void animateAt(int index, double radius) { + stamps[index].radius = radius; + stamps[index].rePath(); + notifyListeners(); + } + + GameState checkWin(double length){ + bool redWin = _checkWinByColor(length,Colors.red); + if(redWin) return GameState.redWin; + + bool blueWin = _checkWinByColor(length,Colors.blue); + if(blueWin) return GameState.blueWin; + + return GameState.doing; + } + + bool _checkWinByColor(double length,Color color) { + List red = stamps + .where((element) => element.color == color) + .map((e) => e.center) + .toList(); + List> redPoints = red + .map>((e) => Point(e.dx ~/ length, e.dy ~/ length)) + .toList(); + + return _checkWinInline(redPoints, 3); + } + + bool _checkWinInline(List> points, int max) { + if (points.length < max) return false; + for (int i = 0; i < points.length; i++) { + int x = points[i].x; + int y = points[i].y; + if (_check(x, y, points, CheckModel.horizontal,max)) { + return true; + } else if (_check(x, y, points, CheckModel.vertical,max)) { + return true; + } else if (_check(x, y, points, CheckModel.leftDiagonal,max)) { + return true; + } else if (_check(x, y, points, CheckModel.rightDiagonal,max)) { + return true; + } + } + return false; + } + + bool _check(int x, int y, List points, CheckModel checkModel,int max) { + int count = 1; + Point checkPoint; + for (int i = 1; i < max; i++) { + switch (checkModel) { + case CheckModel.horizontal: checkPoint = Point(x - i, y); break; + case CheckModel.vertical: checkPoint = Point(x, y - i); break; + case CheckModel.leftDiagonal: checkPoint = Point(x - i, y + i);break; + case CheckModel.rightDiagonal: checkPoint = Point(x + i, y + i); break; + } + if (points.contains(checkPoint)) {count++;} else {break;} + } + if (count == max) return true; + return false; + } +} + +enum CheckModel { + horizontal, // 横向判断 + vertical, // 竖向判断 + leftDiagonal, // 左斜判断 + rightDiagonal // 右斜判断 +} + +enum GameState{ + doing, // 进行中 + redWin, // 红胜 + blueWin // 蓝胜 +} + +class Stamp { + Color color; + Offset center; + double radius; + + Stamp({this.color = Colors.blue, this.center, this.radius = 20}); + + Path _path; + + Path get path { + if (_path == null) { + _path = Path(); + double r = radius; + double rad = 30 / 180 * pi; + + _path..moveTo(center.dx, center.dy); + _path.relativeMoveTo(r * cos(rad), -r * sin(rad)); + _path.relativeLineTo(-2 * r * cos(rad), 0); + _path.relativeLineTo(r * cos(rad), r + r * sin(rad)); + _path.relativeLineTo(r * cos(rad), -(r + r * sin(rad))); + + _path..moveTo(center.dx, center.dy); + _path.relativeMoveTo(0, -r); + _path.relativeLineTo(-r * cos(rad), r + r * sin(rad)); + _path.relativeLineTo(2 * r * cos(rad), 0); + _path.relativeLineTo(-r * cos(rad), -(r + r * sin(rad))); + + return _path; + } else { + return _path; + } + } + + set path(Path path) { + _path = path; + } + + void rePath() { + _path = null; + _path = path; + } +} diff --git a/lib/painter_system/fun/stemp/stamp_paper.dart b/lib/painter_system/fun/stemp/stamp_paper.dart new file mode 100644 index 0000000..3739d94 --- /dev/null +++ b/lib/painter_system/fun/stemp/stamp_paper.dart @@ -0,0 +1,227 @@ +import 'dart:math'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'stamp_data.dart'; + +class StampPaper extends StatefulWidget { + @override + _StampPaperState createState() => _StampPaperState(); +} + +class _StampPaperState extends State + with SingleTickerProviderStateMixin { + final StampData stamps = StampData(); + int gridCount = 3; + double radius = 0; + double width = 0; + GameState gameState = GameState.doing; + + bool get gameOver => gameState != GameState.doing; + + // 定义动画器 + AnimationController _controller; + final Duration animDuration = const Duration(milliseconds: 200); + + @override + void initState() { + super.initState(); + _controller = AnimationController(vsync: this, duration: animDuration) + ..addListener(_listenAnim); + } + + void _listenAnim() { + if (_controller.value == 1.0) { + _controller.reverse(); + } + double rate = (0.9 - 1) * _controller.value + 1; + stamps.animateAt(containsIndex, rate * radius); + } + + @override + Widget build(BuildContext context) { + width = MediaQuery.of(context).size.shortestSide * 0.8; + + return GestureDetector( + onTapDown: _onTapDown, + onTapUp: _onTapUp, + onDoubleTap: _clear, + onTapCancel: _removeLast, + child: CustomPaint( + foregroundPainter: StampPainter(stamps: stamps, count: gridCount), + painter: BackGroundPainter(count: gridCount), + size: Size(width, width), + ), + ); + } + + bool get contains => containsIndex != -1; + + void _onTapDown(TapDownDetails details) { + if (gameOver) return; + + containsIndex = checkZone(details.localPosition); + if (contains) { + _controller.forward(); + return; + } + + radius = width / 2 / gridCount * 0.618; + stamps.push(Stamp( + radius: radius, center: details.localPosition, color: Colors.grey)); + } + + int containsIndex = -1; + + int checkZone(Offset src) { + for (int i = 0; i < stamps.stamps.length; i++) { + Rect zone = Rect.fromCircle( + center: stamps.stamps[i].center, radius: width / gridCount / 2); + if (zone.contains(src)) { + return i; + } + } + return -1; + } + + void _onTapUp(TapUpDetails details) { + if (contains || gameOver) return; + + stamps.activeLast( + color: stamps.stamps.length % 2 == 0 ? Colors.red : Colors.blue); + + gameState = stamps.checkWin(width / gridCount); + if (gameState == GameState.redWin) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text("红棋获胜!"), + backgroundColor: Colors.red, + )); + } + + if (gameState == GameState.blueWin) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("蓝棋获胜!"), backgroundColor: Colors.blue)); + } + } + + void _clear() { + stamps.clear(); + gameState=GameState.doing; + } + + void _removeLast() { + if (contains || gameOver) return; + + stamps.removeLast(); + } + + @override + void dispose() { + stamps.dispose(); + super.dispose(); + } +} + +class BackGroundPainter extends CustomPainter { + BackGroundPainter({this.count = 3}); + + final int count; + + final Paint _pathPaint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1; + + static const List colors = [ + Color(0xFFF60C0C), + Color(0xFFF3B913), + Color(0xFFE7F716), + Color(0xFF3DF30B), + Color(0xFF0DF6EF), + Color(0xFF0829FB), + Color(0xFFB709F4), + ]; + + static const List pos = [ + 1.0 / 7, + 2.0 / 7, + 3.0 / 7, + 4.0 / 7, + 5.0 / 7, + 6.0 / 7, + 1.0 + ]; + + @override + void paint(Canvas canvas, Size size) { + Rect zone = Offset.zero & size; + canvas.clipRect(zone); + + _pathPaint.shader = ui.Gradient.sweep( + Offset(size.width / 2, size.height / 2), + colors, + pos, + TileMode.mirror, + pi / 2, + pi); + + canvas.save(); + for (int i = 0; i < count - 1; i++) { + canvas.translate(0, size.height / count); + canvas.drawLine(Offset.zero, Offset(size.width, 0), _pathPaint); + } + canvas.restore(); + + canvas.save(); + for (int i = 0; i < count - 1; i++) { + canvas.translate(size.width / count, 0); + canvas.drawLine(Offset.zero, Offset(0, size.height), _pathPaint); + } + canvas.restore(); + } + + @override + bool shouldRepaint(covariant BackGroundPainter oldDelegate) { + return count != oldDelegate.count; + } +} + +class StampPainter extends CustomPainter { + final StampData stamps; + final int count; + final Paint _paint = Paint(); + final Paint _pathPaint = Paint() + ..color = Colors.white + ..style = PaintingStyle.stroke; + + StampPainter({this.stamps, this.count = 3}) : super(repaint: stamps); + + @override + void paint(Canvas canvas, Size size) { + Rect zone = Offset.zero & size; + canvas.clipRect(zone); + + stamps.stamps.forEach((stamp) { + double length = size.width / count; + int x = stamp.center.dx ~/ (size.width / count); + int y = stamp.center.dy ~/ (size.width / count); + double strokeWidth = stamp.radius * 0.07; + + Offset center = Offset(length * x + length / 2, length * y + length / 2); + stamp.center = center; + canvas.drawCircle( + stamp.center, stamp.radius, _paint..color = stamp.color); + canvas.drawPath( + stamp.path, + _pathPaint + ..strokeWidth = strokeWidth + ..color = Colors.white); + canvas.drawCircle(stamp.center, stamp.radius + strokeWidth * 1.5, + _pathPaint..color = stamp.color); + }); + } + + @override + bool shouldRepaint(covariant StampPainter oldDelegate) { + return this.stamps != oldDelegate.stamps || this.count != oldDelegate.count; + } +} diff --git a/lib/painter_system/gallery.dart b/lib/painter_system/gallery.dart index 0aef72b..6d0f392 100644 --- a/lib/painter_system/gallery.dart +++ b/lib/painter_system/gallery.dart @@ -7,7 +7,7 @@ import 'base/draw_picture.dart'; import 'art/hypnotic_squares.dart'; import 'art/joy_division.dart'; import 'art/piet_mondrian.dart'; -import 'base/random_portrait.dart'; +import 'fun/random_portrait.dart'; import 'art/tiled_lines.dart'; import 'art/triangular_mesh.dart'; import 'art/un_deux_trois.dart'; diff --git a/lib/painter_system/gallery_factory.dart b/lib/painter_system/gallery_factory.dart index 5aff07c..4724c6f 100644 --- a/lib/painter_system/gallery_factory.dart +++ b/lib/painter_system/gallery_factory.dart @@ -14,7 +14,8 @@ import 'base/clock_widget.dart'; import 'base/draw_path_fun.dart'; import 'base/draw_grid_axis.dart'; import 'base/draw_picture.dart'; -import 'base/random_portrait.dart'; +import 'fun/random_portrait.dart'; +import 'fun/stemp/stamp_paper.dart'; import 'gallery.dart'; /// create by 张风捷特烈 on 2020/12/5 @@ -82,6 +83,12 @@ class GalleryFactory { info: " 本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。其中对点的随机生成和对称处理能让你练习对数据的控制力。", content: RandomPortrait()), + FrameShower( + title: "井字棋", + author: "张风捷特烈", + info: + " 本例通过井字棋的绘制与逻辑校验,集合了手势、绘制、动画、校验等重要的技能,是一个非常好的联系案例。", + content: StampPaper()), ]; case GalleryType.art: return [ diff --git a/lib/views/pages/gallery/gallery_page.dart b/lib/views/pages/gallery/gallery_page.dart index f56a443..be39d5b 100644 --- a/lib/views/pages/gallery/gallery_page.dart +++ b/lib/views/pages/gallery/gallery_page.dart @@ -7,7 +7,7 @@ import 'package:flutter_unit/painter_system/base/draw_picture.dart'; import 'package:flutter_unit/painter_system/art/hypnotic_squares.dart'; import 'package:flutter_unit/painter_system/art/joy_division.dart'; import 'package:flutter_unit/painter_system/art/piet_mondrian.dart'; -import 'package:flutter_unit/painter_system/base/random_portrait.dart'; +import 'package:flutter_unit/painter_system/fun/random_portrait.dart'; import 'package:flutter_unit/painter_system/art/tiled_lines.dart'; import 'package:flutter_unit/painter_system/art/triangular_mesh.dart'; import 'package:flutter_unit/painter_system/art/un_deux_trois.dart';