Implémentation du zoom et pan pour le mode ajout d'impact
corrigé les impactes en mode zoom
This commit is contained in:
@@ -17,13 +17,14 @@ class TargetOverlay extends StatelessWidget {
|
||||
final double targetRadius;
|
||||
final TargetType targetType;
|
||||
final int ringCount;
|
||||
final List<double>? ringRadii; // Individual ring radii multipliers
|
||||
final List<double>? ringRadii;
|
||||
final void Function(Shot shot)? onShotTapped;
|
||||
final void Function(double x, double y)? onAddShot;
|
||||
final double? groupingCenterX;
|
||||
final double? groupingCenterY;
|
||||
final double? groupingDiameter;
|
||||
final List<Shot>? referenceImpacts;
|
||||
final double zoomScale;
|
||||
|
||||
const TargetOverlay({
|
||||
super.key,
|
||||
@@ -40,70 +41,80 @@ class TargetOverlay extends StatelessWidget {
|
||||
this.groupingCenterY,
|
||||
this.groupingDiameter,
|
||||
this.referenceImpacts,
|
||||
this.zoomScale = 1.0,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTapUp: (details) {
|
||||
if (onAddShot != null) {
|
||||
final RenderBox box = context.findRenderObject() as RenderBox;
|
||||
final localPosition = details.localPosition;
|
||||
final relX = localPosition.dx / box.size.width;
|
||||
final relY = localPosition.dy / box.size.height;
|
||||
onAddShot!(relX, relY);
|
||||
}
|
||||
},
|
||||
child: CustomPaint(
|
||||
painter: _TargetOverlayPainter(
|
||||
shots: shots,
|
||||
targetCenterX: targetCenterX,
|
||||
targetCenterY: targetCenterY,
|
||||
targetRadius: targetRadius,
|
||||
targetType: targetType,
|
||||
ringCount: ringCount,
|
||||
ringRadii: ringRadii,
|
||||
groupingCenterX: groupingCenterX,
|
||||
groupingCenterY: groupingCenterY,
|
||||
groupingDiameter: groupingDiameter,
|
||||
referenceImpacts: referenceImpacts,
|
||||
),
|
||||
child: Stack(
|
||||
children: shots.map((shot) {
|
||||
return Positioned(
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final x = shot.x * constraints.maxWidth;
|
||||
final y = shot.y * constraints.maxHeight;
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
left: x - 15,
|
||||
top: y - 15,
|
||||
child: GestureDetector(
|
||||
onTap: () => onShotTapped?.call(shot),
|
||||
child: Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
shape: BoxShape.circle,
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTapUp: (details) {
|
||||
if (onAddShot != null) {
|
||||
// Utiliser les constraints pour un calcul précis
|
||||
final relX = details.localPosition.dx / constraints.maxWidth;
|
||||
final relY = details.localPosition.dy / constraints.maxHeight;
|
||||
onAddShot!(relX, relY);
|
||||
}
|
||||
},
|
||||
child: CustomPaint(
|
||||
painter: _TargetOverlayPainter(
|
||||
shots: shots,
|
||||
targetCenterX: targetCenterX,
|
||||
targetCenterY: targetCenterY,
|
||||
targetRadius: targetRadius,
|
||||
targetType: targetType,
|
||||
ringCount: ringCount,
|
||||
ringRadii: ringRadii,
|
||||
groupingCenterX: groupingCenterX,
|
||||
groupingCenterY: groupingCenterY,
|
||||
groupingDiameter: groupingDiameter,
|
||||
referenceImpacts: referenceImpacts,
|
||||
zoomScale: zoomScale,
|
||||
),
|
||||
child: Stack(
|
||||
children: shots.map((shot) {
|
||||
return Positioned(
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, innerConstraints) {
|
||||
final x = shot.x * innerConstraints.maxWidth;
|
||||
final y = shot.y * innerConstraints.maxHeight;
|
||||
// Zone de tap qui s'adapte au zoom (taille fixe à l'écran)
|
||||
final tapSize = 30 / zoomScale;
|
||||
final halfTapSize = tapSize / 2;
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
left: x - halfTapSize,
|
||||
top: y - halfTapSize,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () => onShotTapped?.call(shot),
|
||||
child: Container(
|
||||
width: tapSize,
|
||||
height: tapSize,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -120,6 +131,7 @@ class _TargetOverlayPainter extends CustomPainter {
|
||||
final double? groupingCenterY;
|
||||
final double? groupingDiameter;
|
||||
final List<Shot>? referenceImpacts;
|
||||
final double zoomScale;
|
||||
|
||||
_TargetOverlayPainter({
|
||||
required this.shots,
|
||||
@@ -133,6 +145,7 @@ class _TargetOverlayPainter extends CustomPainter {
|
||||
this.groupingCenterY,
|
||||
this.groupingDiameter,
|
||||
this.referenceImpacts,
|
||||
this.zoomScale = 1.0,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -266,26 +279,32 @@ class _TargetOverlayPainter extends CustomPainter {
|
||||
final x = shot.x * size.width;
|
||||
final y = shot.y * size.height;
|
||||
|
||||
// Tailles fixes divisées par le zoom pour rester constantes à l'écran
|
||||
final outerRadius = 10 / zoomScale;
|
||||
final innerRadius = 8 / zoomScale;
|
||||
final strokeWidth = 3 / zoomScale;
|
||||
final fontSize = 10 / zoomScale;
|
||||
|
||||
// Draw outer circle (white outline for visibility)
|
||||
final outlinePaint = Paint()
|
||||
..color = AppTheme.impactOutlineColor
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 3;
|
||||
canvas.drawCircle(Offset(x, y), 10, outlinePaint);
|
||||
..strokeWidth = strokeWidth;
|
||||
canvas.drawCircle(Offset(x, y), outerRadius, outlinePaint);
|
||||
|
||||
// Draw impact marker
|
||||
final impactPaint = Paint()
|
||||
..color = AppTheme.impactColor
|
||||
..style = PaintingStyle.fill;
|
||||
canvas.drawCircle(Offset(x, y), 8, impactPaint);
|
||||
canvas.drawCircle(Offset(x, y), innerRadius, impactPaint);
|
||||
|
||||
// Draw score number
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(
|
||||
text: '${shot.score}',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 10,
|
||||
fontSize: fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
@@ -302,26 +321,32 @@ class _TargetOverlayPainter extends CustomPainter {
|
||||
final x = ref.x * size.width;
|
||||
final y = ref.y * size.height;
|
||||
|
||||
// Tailles fixes divisées par le zoom pour rester constantes à l'écran
|
||||
final outerRadius = 12 / zoomScale;
|
||||
final innerRadius = 10 / zoomScale;
|
||||
final strokeWidth = 3 / zoomScale;
|
||||
final fontSize = 12 / zoomScale;
|
||||
|
||||
// Draw outer circle (white outline for visibility)
|
||||
final outlinePaint = Paint()
|
||||
..color = Colors.white
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 3;
|
||||
canvas.drawCircle(Offset(x, y), 12, outlinePaint);
|
||||
..strokeWidth = strokeWidth;
|
||||
canvas.drawCircle(Offset(x, y), outerRadius, outlinePaint);
|
||||
|
||||
// Draw reference marker (purple)
|
||||
final refPaint = Paint()
|
||||
..color = Colors.deepPurple
|
||||
..style = PaintingStyle.fill;
|
||||
canvas.drawCircle(Offset(x, y), 10, refPaint);
|
||||
canvas.drawCircle(Offset(x, y), innerRadius, refPaint);
|
||||
|
||||
// Draw "R" to indicate reference
|
||||
final textPainter = TextPainter(
|
||||
text: const TextSpan(
|
||||
text: TextSpan(
|
||||
text: 'R',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
fontSize: fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
@@ -345,6 +370,7 @@ class _TargetOverlayPainter extends CustomPainter {
|
||||
groupingCenterX != oldDelegate.groupingCenterX ||
|
||||
groupingCenterY != oldDelegate.groupingCenterY ||
|
||||
groupingDiameter != oldDelegate.groupingDiameter ||
|
||||
referenceImpacts != oldDelegate.referenceImpacts;
|
||||
referenceImpacts != oldDelegate.referenceImpacts ||
|
||||
zoomScale != oldDelegate.zoomScale;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user