diff --git a/lib/screens/camera_exposure_painter.dart b/lib/screens/camera_exposure_painter.dart new file mode 100644 index 0000000..7f85c05 --- /dev/null +++ b/lib/screens/camera_exposure_painter.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; + +/// カメラ画面右端の露出スライダーを描画する CustomPainter。 +/// +/// - 縦トラック(白半透明) +/// - 中央基準線(0 EV を示すマーカー) +/// - 現在値を示すノブ(影付き白丸) +class ExposureSliderPainter extends CustomPainter { + final double currentValue; + final double minValue; + final double maxValue; + + const ExposureSliderPainter({ + required this.currentValue, + required this.minValue, + required this.maxValue, + }); + + @override + void paint(Canvas canvas, Size size) { + final trackPaint = Paint() + ..color = Colors.white.withValues(alpha: 0.3) + ..strokeWidth = 4 + ..strokeCap = StrokeCap.round; + + final centerLinePaint = Paint() + ..color = Colors.white54 + ..strokeWidth = 2; + + final knobPaint = Paint() + ..color = Colors.white + ..style = PaintingStyle.fill; + + final knobShadowPaint = Paint() + ..color = Colors.black26 + ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 4); + + // 縦トラック(中央線) + final trackX = size.width / 2; + canvas.drawLine( + Offset(trackX, 10), + Offset(trackX, size.height - 10), + trackPaint, + ); + + // 0 EV マーカー + canvas.drawLine( + Offset(trackX - 6, size.height / 2), + Offset(trackX + 6, size.height / 2), + centerLinePaint, + ); + + // ノブ位置を算出 + final range = maxValue - minValue; + if (range > 0) { + // minValue(下端) → 0.0、maxValue(上端) → 1.0 に正規化してY座標に変換 + final normalized = (currentValue - minValue) / range; + final knobY = (size.height - 20) * (1.0 - normalized) + 10; + canvas.drawCircle(Offset(trackX, knobY), 7, knobShadowPaint); + canvas.drawCircle(Offset(trackX, knobY), 6, knobPaint); + } + } + + @override + bool shouldRepaint(ExposureSliderPainter oldDelegate) { + return oldDelegate.currentValue != currentValue || + oldDelegate.minValue != minValue || + oldDelegate.maxValue != maxValue; + } +} diff --git a/lib/screens/camera_screen.dart b/lib/screens/camera_screen.dart index 6dd5280..58f3641 100644 --- a/lib/screens/camera_screen.dart +++ b/lib/screens/camera_screen.dart @@ -13,6 +13,7 @@ import 'package:image_picker/image_picker.dart'; // Gallery & Camera import '../services/image_compression_service.dart'; import '../theme/app_colors.dart'; import 'camera_analysis_mixin.dart'; +import 'camera_exposure_painter.dart'; enum CameraMode { @@ -475,8 +476,8 @@ class _CameraScreenState extends ConsumerState with SingleTickerPr height: 180, width: 48, // Wider for easier tapping child: CustomPaint( - key: ValueKey(_currentExposureOffset), // Force repaint on value change - painter: _ExposureSliderPainter( + key: ValueKey(_currentExposureOffset), + painter: ExposureSliderPainter( currentValue: _currentExposureOffset, minValue: _minExposure, maxValue: _maxExposure, @@ -656,63 +657,3 @@ class _CameraScreenState extends ConsumerState with SingleTickerPr } -// Custom Painter for Exposure Slider -class _ExposureSliderPainter extends CustomPainter { - final double currentValue; - final double minValue; - final double maxValue; - - _ExposureSliderPainter({ - required this.currentValue, - required this.minValue, - required this.maxValue, - }); - - @override - void paint(Canvas canvas, Size size) { - final trackPaint = Paint() - ..color = Colors.white.withValues(alpha: 0.3) - ..strokeWidth = 4 - ..strokeCap = StrokeCap.round; - - final centerLinePaint = Paint() - ..color = Colors.white54 - ..strokeWidth = 2; - - final knobPaint = Paint() - ..color = Colors.white - ..style = PaintingStyle.fill; final knobShadowPaint = Paint() - ..color = Colors.black26 - ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 4); // Draw vertical track (centered) - final trackX = size.width / 2; - canvas.drawLine( - Offset(trackX, 10), - Offset(trackX, size.height - 10), - trackPaint, - ); // Draw center marker - canvas.drawLine( - Offset(trackX - 6, size.height / 2), - Offset(trackX + 6, size.height / 2), - centerLinePaint, - ); // Calculate knob position - final range = maxValue - minValue; - if (range > 0) { - // Normalize currentValue to 0.0-1.0 range - // minValue (e.g., -4.0) -> 0.0 (bottom) - // 0.0 (center) -> 0.5 (middle) - // maxValue (e.g., +4.0) -> 1.0 (top) - final normalized = (currentValue - minValue) / range; - // Map to Y coordinate: 0.0 (normalized) -> bottom, 1.0 (normalized) -> top - final knobY = (size.height - 20) * (1.0 - normalized) + 10; - // Draw knob shadow - canvas.drawCircle(Offset(trackX, knobY), 7, knobShadowPaint); - // Draw knob - canvas.drawCircle(Offset(trackX, knobY), 6, knobPaint); - } - } @override - bool shouldRepaint(_ExposureSliderPainter oldDelegate) { - return oldDelegate.currentValue != currentValue || - oldDelegate.minValue != minValue || - oldDelegate.maxValue != maxValue; - } -} diff --git a/pubspec.yaml b/pubspec.yaml index 18ee739..ae9e142 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.41+48 +version: 1.0.42+49 environment: sdk: ^3.10.1