目标:我想制作一个图像标记功能,即在图像上绘图以及放大和缩小功能。为此,我使用了带有图像的画布和颤振变换小部件来实现缩放和平移。我已经成功地使图像缩放和翻译。我也可以在该图像上进行绘图。
问题:问题是我只能在图像为原始大小时设置的区域上进行绘图,即当图像扩展时,我只能在新扩展(缩放)图像下的那个区域(原始图像区域)上绘图,当我移动图像时会出现同样的问题。
注意 - 我已将画布的大小设置为我的图像大小。
在下面的屏幕截图中 1.before_zoom 在我从资产加载时显示我的图像。在那我画了一个矩形,它显示可以在图像的任何部分进行绘图。在 after_zoom 图像中,我再次绘制了一个矩形,显示了可用的绘图屏幕。如果我尝试在该矩形之外执行绘图,则在移动图像时会出现同样的问题。我认为的问题是,当图像首次加载以及图像扩展或移动时,画布绘图空间得到修复,它只允许在图像加载时首次设置的区域上绘图。所以任何人都知道如何解决这些问题或任何其他方法来实现这些功能。
代码 :
/// DrawingScreen :
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import 'package:flutter/services.dart';
class _DrawingScreenState extends State<DrawingScreen> {
ui.Image _image;
Future _future;
Color _selectedColor;
double _strokeWidth;
List<TouchPoints> points = [];
@override
void initState() {
super.initState();
_selectedColor = Colors.yellow;
_strokeWidth = 14.0;
_future = load();
}
String imageLocation = 'images/2.jpg';
/// FUNCTION TO LOAD THE IMAGE FROM THE ASSETS FOLDER
Future<void> load() async {
ByteData data = await rootBundle.load(imageLocation);
ui.Codec codec1 = await ui.instantiateImageCodec(data.buffer.asUint8List());
ui.FrameInfo fi = await codec1.getNextFrame();
_image = fi.image;
tempIW = _image.width.toDouble();
tempIH = _image.height.toDouble();
}
double translateX = 0;
double translateY = 0;
double scaleFactor = 1;
double tempIW;
double tempIH;
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _future,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Scaffold(
bottomNavigationBar: Container(
color: Color(0xffF8F8FF),
child: Row(children: [
Flexible(
child: IconButton(
padding: EdgeInsets.zero,
iconSize: 20,
onPressed: () {
setState(() {
if (scaleFactor > 0.11)
setState(() {
scaleFactor = scaleFactor - 0.1;
});
});
},
icon: Icon(Icons.zoom_in),
),
),
Flexible(
child: IconButton(
iconSize: 20,
padding: EdgeInsets.zero,
onPressed: () {
setState(() {
if (scaleFactor < 0.91) {
scaleFactor = scaleFactor + 0.1;
}
});
},
icon: Icon(Icons.zoom_out),
),
),
Flexible(
child: IconButton(
iconSize: 20,
padding: EdgeInsets.zero,
onPressed: () {
setState(() {
translateX = translateX - 100;
});
},
icon: Icon(Icons.arrow_back),
),
),
Flexible(
child: IconButton(
iconSize: 20,
padding: EdgeInsets.zero,
onPressed: () {
setState(() {
translateX = translateX + 100;
});
},
icon: Icon(Icons.arrow_forward),
),
),
Flexible(
child: IconButton(
iconSize: 20,
padding: EdgeInsets.zero,
onPressed: () {
setState(() {
translateY = translateY - 100;
});
},
icon: Icon(Icons.arrow_upward),
),
),
Flexible(
child: IconButton(
iconSize: 20,
padding: EdgeInsets.zero,
onPressed: () {
setState(() {
translateY = translateY + 100;
});
},
icon: Icon(Icons.arrow_downward),
),
),
Flexible(
child: IconButton(
iconSize: 20,
padding: EdgeInsets.zero,
onPressed: () {
this.setState(() {
points.clear();
});
},
icon: Icon(Icons.layers_clear),
),
),
Flexible(
child: IconButton(
iconSize: 20,
padding: EdgeInsets.zero,
onPressed: () {
undo();
setState(() {
points;
});
},
icon: Icon(Icons.wifi_protected_setup),
),
),
]),
),
body: SafeArea(
child: Center(
child: FittedBox(
fit: BoxFit.fill,
child: Transform(
transform: Matrix4(
1, 0, 0, 0, //
0, 1, 0, 0, //
0, 0, 1, 0, //
translateX, translateY, 0, scaleFactor,
),
child: GestureDetector(
onPanStart: (details) {
setState(() {
if (details.localPosition.dx < _image.width.toDouble() && details.localPosition.dy < _image.height.toDouble())
points.add(
TouchPoints(
points: Offset(
details.localPosition.dx,
details.localPosition.dy,
),
paint: Paint()
..color = _selectedColor
..strokeWidth = _strokeWidth
..strokeCap = StrokeCap.round,
),
);
});
},
onPanUpdate: (details) {
setState(() {
if (details.localPosition.dx < _image.width.toDouble() && details.localPosition.dy < _image.height.toDouble())
points.add(
TouchPoints(
points: Offset(
details.localPosition.dx,
details.localPosition.dy,
),
paint: Paint()
..color = _selectedColor
..strokeWidth = _strokeWidth
..strokeCap = StrokeCap.round,
),
);
});
},
onPanEnd: (details) {
setState(() {
points.add(null);
});
print(details.velocity);
},
child: RepaintBoundary(
child: SizedBox(
width: _image.width.toDouble(),
height: _image.height.toDouble(),
child: ClipRect(
child: CustomPaint(
painter: FacePainter(
_image,
points,
),
),
),
),
),
),
),
),
),
),
);
} else {
return CircularProgressIndicator();
}
},
);
}
}
class FacePainter extends CustomPainter {
final ui.Image _image;
List<TouchPoints> pointsList = [];
FacePainter(
this._image,
this.pointsList,
) : super();
@override
void paint(Canvas canvas, Size size) async {
canvas.drawImage(_image, Offset(0.0, 0.0), Paint());
for (int i = 0; i < pointsList.length - 1; i++) {
if (pointsList[i] != null && pointsList[i + 1] != null) {
if (pointsList[i].points.dx < size.width && pointsList[i].points.dy < size.height)
canvas.drawLine(pointsList[i].points, pointsList[i + 1].points, pointsList[i].paint);
}
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
class TouchPoints {
Paint paint;
Offset points;
TouchPoints({this.paint, this.points});
}
图片供参考。 缩放前