books
This commit is contained in:
BIN
assets/images/body.webp
Normal file
BIN
assets/images/body.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/images/hand.webp
Normal file
BIN
assets/images/hand.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
@@ -62,6 +62,8 @@ class MenuStore with ChangeNotifier {
|
|||||||
?.label;
|
?.label;
|
||||||
|
|
||||||
void selectMenuPath(String path) {
|
void selectMenuPath(String path) {
|
||||||
|
|
||||||
|
|
||||||
MenuNode root = MenuNode(
|
MenuNode root = MenuNode(
|
||||||
path: '',
|
path: '',
|
||||||
label: '',
|
label: '',
|
||||||
@@ -83,7 +85,7 @@ class MenuStore with ChangeNotifier {
|
|||||||
}else{
|
}else{
|
||||||
_shouldAddHistory = true;
|
_shouldAddHistory = true;
|
||||||
}
|
}
|
||||||
|
print("=====selectMenuPath: ${result.last.path}===============");
|
||||||
_state = state.copyWith(
|
_state = state.copyWith(
|
||||||
activeMenu: result.last.path, expandMenus: expandMenus);
|
activeMenu: result.last.path, expandMenus: expandMenus);
|
||||||
}
|
}
|
||||||
@@ -160,10 +162,10 @@ class MenuStore with ChangeNotifier {
|
|||||||
|
|
||||||
void select(MenuNode menu) {
|
void select(MenuNode menu) {
|
||||||
bool hasHistory = _history.where((e) => e.menuPath==menu.path).isNotEmpty;
|
bool hasHistory = _history.where((e) => e.menuPath==menu.path).isNotEmpty;
|
||||||
|
print("=====select: ${hasHistory}===============");
|
||||||
if(hasHistory){
|
if(hasHistory){
|
||||||
_shouldAddHistory = false;
|
_shouldAddHistory = false;
|
||||||
selectMenuPath(menu.path);
|
goRouter.go(menu.path);
|
||||||
notifyListeners();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,15 @@ final RouteBase drawRouters = GoRoute(
|
|||||||
case '18':
|
case '18':
|
||||||
child = const P18Page();
|
child = const P18Page();
|
||||||
break;
|
break;
|
||||||
|
case '19':
|
||||||
|
child = const P19Page();
|
||||||
|
break;
|
||||||
|
case '20':
|
||||||
|
child = const P20Page();
|
||||||
|
break;
|
||||||
|
case '21':
|
||||||
|
child = const P21Page();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CustomTransitionPage(
|
return CustomTransitionPage(
|
||||||
|
|||||||
@@ -117,9 +117,6 @@ class _RouterIndicatorState extends State<RouterIndicator> {
|
|||||||
if(matches.isEmpty) return const SizedBox();
|
if(matches.isEmpty) return const SizedBox();
|
||||||
RouteMatch match = _delegate.currentConfiguration.matches.last;
|
RouteMatch match = _delegate.currentConfiguration.matches.last;
|
||||||
|
|
||||||
print(
|
|
||||||
"=========_RouterIndicatorState:build==${match.matchedLocation}========");
|
|
||||||
|
|
||||||
return TolyBreadcrumb(
|
return TolyBreadcrumb(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
items: pathToBreadcrumbItems(context, match.matchedLocation),
|
items: pathToBreadcrumbItems(context, match.matchedLocation),
|
||||||
|
|||||||
@@ -18,3 +18,6 @@ export 'p15/p15.dart';
|
|||||||
export 'p16/p16.dart';
|
export 'p16/p16.dart';
|
||||||
export 'p17/p17.dart';
|
export 'p17/p17.dart';
|
||||||
export 'p18/p18.dart';
|
export 'p18/p18.dart';
|
||||||
|
export 'p19/p19.dart';
|
||||||
|
export 'p20/p20.dart';
|
||||||
|
export 'p21/p21.dart';
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class P18Page extends StatelessWidget {
|
|||||||
s2.Paper(),
|
s2.Paper(),
|
||||||
s3.Paper(),
|
s3.Paper(),
|
||||||
s4.Paper(),
|
s4.Paper(),
|
||||||
// s5.Paper(),
|
s5.Paper(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,9 +98,10 @@ class _WorldState extends State<World> with SingleTickerProviderStateMixin {
|
|||||||
for (int i = 0; i < imageSrc.width; i++) {
|
for (int i = 0; i < imageSrc.width; i++) {
|
||||||
for (int j = 0; j < imageSrc.height; j++) {
|
for (int j = 0; j < imageSrc.height; j++) {
|
||||||
image.PixelUint8 pixel = imageSrc.getPixel(i, j) as image.PixelUint8;
|
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)') {
|
if (color.value == 4278190080) {
|
||||||
// print('-($i,$j)----${imageSrc.getPixel(i, j)}---------------');
|
|
||||||
|
|
||||||
Particle particle = Particle(
|
Particle particle = Particle(
|
||||||
x: i * 1.0+ offsetX,
|
x: i * 1.0+ offsetX,
|
||||||
@@ -115,9 +116,7 @@ class _WorldState extends State<World> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setState(() {
|
pm.tick();
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
packages/idraw/lib/p19/p19.dart
Normal file
1
packages/idraw/lib/p19/p19.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export 'p19_page.dart';
|
||||||
21
packages/idraw/lib/p19/p19_page.dart
Normal file
21
packages/idraw/lib/p19/p19_page.dart
Normal 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 P19Page extends StatelessWidget {
|
||||||
|
const P19Page({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const DemoShower(
|
||||||
|
srcCodeDir: 'draw/p19',
|
||||||
|
|
||||||
|
demos: [
|
||||||
|
s1.Paper(),
|
||||||
|
s2.Paper(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
301
packages/idraw/lib/p19/s01.dart
Normal file
301
packages/idraw/lib/p19/s01.dart
Normal file
@@ -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<World> 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(<double>[
|
||||||
|
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<Particle> 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<Particle> 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);
|
||||||
|
}
|
||||||
473
packages/idraw/lib/p19/s02.dart
Normal file
473
packages/idraw/lib/p19/s02.dart
Normal file
@@ -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<Particle> 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<Color> components;
|
||||||
|
|
||||||
|
Palette({required this.components});
|
||||||
|
|
||||||
|
/// Creates a new palette from JSON.
|
||||||
|
factory Palette.fromJson(List<dynamic> 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<Screen> 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<Palette> palettes = await _loadPalettes();
|
||||||
|
_palette = palettes[4];
|
||||||
|
if(_palette==null) return;
|
||||||
|
_bgFx.setPalette(_palette!);
|
||||||
|
_ticker = createTicker(_tick)..start();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Palette>> _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: <Widget>[
|
||||||
|
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<Palette?> palettes, bool dark) {
|
||||||
|
Palette? result;
|
||||||
|
|
||||||
|
while (result == null) {
|
||||||
|
Palette palette = Rnd.getItem(palettes);
|
||||||
|
List<Color> colors = Rnd.shuffle(palette.components).map<Color>((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<num> a, List<num> b) {
|
||||||
|
return a[1].compareTo(b[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
List<Color> sortedColors =
|
||||||
|
lumDiff.map((d) => colors[(d[0] + 1).toInt()]).toList();
|
||||||
|
|
||||||
|
result = Palette(
|
||||||
|
components: [colors[0]] + sortedColors,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
packages/idraw/lib/p20/p20.dart
Normal file
1
packages/idraw/lib/p20/p20.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export 'p20_page.dart';
|
||||||
21
packages/idraw/lib/p20/p20_page.dart
Normal file
21
packages/idraw/lib/p20/p20_page.dart
Normal 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 P20Page extends StatelessWidget {
|
||||||
|
const P20Page({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const DemoShower(
|
||||||
|
srcCodeDir: 'draw/p20',
|
||||||
|
|
||||||
|
demos: [
|
||||||
|
s1.Paper(),
|
||||||
|
s2.Paper(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
340
packages/idraw/lib/p20/s01.dart
Normal file
340
packages/idraw/lib/p20/s01.dart
Normal file
@@ -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<Particle> 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<ClockPanel>
|
||||||
|
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]
|
||||||
|
] //:
|
||||||
|
];
|
||||||
|
|
||||||
480
packages/idraw/lib/p20/s02.dart
Normal file
480
packages/idraw/lib/p20/s02.dart
Normal file
@@ -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<Particle> 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<Particle> 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<ClockPanel>
|
||||||
|
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]
|
||||||
|
] //:
|
||||||
|
];
|
||||||
1
packages/idraw/lib/p21/p21.dart
Normal file
1
packages/idraw/lib/p21/p21.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export 'p21_page.dart';
|
||||||
21
packages/idraw/lib/p21/p21_page.dart
Normal file
21
packages/idraw/lib/p21/p21_page.dart
Normal 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 P21Page extends StatelessWidget {
|
||||||
|
const P21Page({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const DemoShower(
|
||||||
|
srcCodeDir: 'draw/p21',
|
||||||
|
|
||||||
|
demos: [
|
||||||
|
s1.Paper(),
|
||||||
|
s2.Paper(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
241
packages/idraw/lib/p21/s01.dart
Normal file
241
packages/idraw/lib/p21/s01.dart
Normal file
@@ -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<Paper> createState() => _PaperState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PaperState extends State<Paper>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
291
packages/idraw/lib/p21/s02.dart
Normal file
291
packages/idraw/lib/p21/s02.dart
Normal file
@@ -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<Paper> createState() => _PaperState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PaperState extends State<Paper>
|
||||||
|
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<int> 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<int> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
image: ^4.1.3
|
image: ^4.1.3
|
||||||
dio: ^5.4.0
|
dio: ^5.4.0
|
||||||
|
dash_painter: ^1.0.2
|
||||||
components:
|
components:
|
||||||
path: ../components
|
path: ../components
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|||||||
@@ -80,6 +80,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
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:
|
dio:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
Reference in New Issue
Block a user