浏览代码

crosshair snapping

Berke 1 年之前
父节点
当前提交
c1cc1051b0
共有 1 个文件被更改,包括 34 次插入22 次删除
  1. 34 22
      src/charts/custom_line.rs

+ 34 - 22
src/charts/custom_line.rs

@@ -198,7 +198,8 @@ impl CustomLine {
                 max: self.x_max_time, 
                 crosshair_cache: &self.crosshair_cache, 
                 crosshair_position: self.crosshair_position, 
-                crosshair: self.crosshair
+                crosshair: self.crosshair,
+                timeframe: self.timeframe
             })
             .width(Length::FillPortion(10))
             .height(Length::Fixed(26.0));
@@ -593,32 +594,31 @@ impl canvas::Program<Message> for CustomLine {
         if self.crosshair {
             let crosshair = self.crosshair_cache.draw(renderer, bounds.size(), |frame| {
                 if let Some(cursor_position) = cursor.position_in(bounds) {
-                    let x = cursor_position.x;
-                    let y = cursor_position.y;
-
                     let line = Path::line(
-                        Point::new(x, 0.0), 
-                        Point::new(x, bounds.height as f32)
+                        Point::new(0.0, cursor_position.y), 
+                        Point::new(bounds.width as f32, cursor_position.y)
                     );
                     frame.stroke(&line, Stroke::default().with_color(Color::from_rgba8(200, 200, 200, 0.6)).with_width(1.0));
 
+                    let crosshair_ratio = cursor_position.x as f64 / bounds.width as f64;
+                    let crosshair_millis = earliest as f64 * 1000.0 + crosshair_ratio * (latest as f64 * 1000.0 - earliest as f64 * 1000.0);
+                    let crosshair_time = NaiveDateTime::from_timestamp((crosshair_millis / 1000.0) as i64, 0);
+
+                    let crosshair_timestamp = crosshair_time.timestamp();
+                    let rounded_timestamp = (crosshair_timestamp as f64 / (self.timeframe as f64 * 60.0)).round() as i64 * self.timeframe as i64 * 60;
+
+                    let snap_ratio = (rounded_timestamp as f64 - earliest as f64) / ((latest as f64) - (earliest as f64));
+                    let snap_x = snap_ratio * bounds.width as f64;
+
                     let line = Path::line(
-                        Point::new(0.0, y), 
-                        Point::new(bounds.width as f32, y)
+                        Point::new(snap_x as f32, 0.0), 
+                        Point::new(snap_x as f32, bounds.height as f32)
                     );
                     frame.stroke(&line, Stroke::default().with_color(Color::from_rgba8(200, 200, 200, 0.6)).with_width(1.0));
 
-                    let crosshair_ratio = cursor_position.x as f64 / bounds.width as f64;
-                    let crosshair_millis = earliest as f64 * 1000.0 + crosshair_ratio * (latest as f64 * 1000.0 - earliest as f64 * 1000.0);
-                    let crosshair_time = NaiveDateTime::from_timestamp((crosshair_millis / 1000.0) as i64, 0);
+                    if let Some((_, kline)) = self.klines_raw.iter()
+                        .find(|(time, _)| time.timestamp() == rounded_timestamp) {
 
-                    if let Some((_closest_time, kline)) = self.klines_raw.iter()
-                        .filter(|(time, _)| {
-                            let timestamp = time.timestamp();
-                            timestamp >= earliest && timestamp <= latest
-                        })
-                        .min_by_key(|(time, _)| ((time.timestamp() as f64 - crosshair_time.timestamp() as f64).abs() as i64)) {
-                        
                         let tooltip_text = format!(
                             "O: {} H: {} L: {} C: {}\nBuyV: {:.0} SellV: {:.0}",
                             kline.0, kline.1, kline.2, kline.3, kline.4, kline.5
@@ -653,7 +653,11 @@ impl canvas::Program<Message> for CustomLine {
             Interaction::Erasing => mouse::Interaction::Crosshair,
             Interaction::Panning { .. } => mouse::Interaction::Grabbing,
             Interaction::None if cursor.is_over(bounds) => {
-                mouse::Interaction::Crosshair
+                if self.crosshair {
+                    mouse::Interaction::Crosshair
+                } else {
+                    mouse::Interaction::default()
+                }
             }
             Interaction::None => mouse::Interaction::default(),
         }
@@ -723,6 +727,7 @@ pub struct AxisLabelXCanvas<'a> {
     crosshair: bool,
     min: i64,
     max: i64,
+    timeframe: i16,
 }
 impl canvas::Program<Message> for AxisLabelXCanvas<'_> {
     type State = Interaction;
@@ -789,11 +794,18 @@ impl canvas::Program<Message> for AxisLabelXCanvas<'_> {
                 let crosshair_millis = earliest_in_millis as f64 + crosshair_ratio * (latest_in_millis - earliest_in_millis) as f64;
                 let crosshair_time = NaiveDateTime::from_timestamp((crosshair_millis / 1000.0) as i64, 0);
 
+                let crosshair_timestamp = crosshair_time.timestamp();
+                let rounded_timestamp = (crosshair_timestamp as f64 / (self.timeframe as f64 * 60.0)).round() as i64 * self.timeframe as i64 * 60;
+                let rounded_time = NaiveDateTime::from_timestamp(rounded_timestamp, 0);
+
+                let snap_ratio = (rounded_timestamp as f64 * 1000.0 - earliest_in_millis as f64) / (latest_in_millis as f64 - earliest_in_millis as f64);
+                let snap_x = snap_ratio * bounds.width as f64;
+
                 let text_size = 12.0;
-                let text_content = crosshair_time.format("%H:%M").to_string();
+                let text_content = rounded_time.format("%H:%M").to_string();
                 let growth_amount = 6.0; 
-                let rectangle_position = Point::new(self.crosshair_position.x - 14.0 - growth_amount, bounds.height as f32 - 20.0);
-                let text_position = Point::new(self.crosshair_position.x - 14.0, bounds.height as f32 - 20.0);
+                let rectangle_position = Point::new(snap_x as f32 - 14.0 - growth_amount, bounds.height as f32 - 20.0);
+                let text_position = Point::new(snap_x as f32 - 14.0, bounds.height as f32 - 20.0);
 
                 let text_background = canvas::Path::rectangle(rectangle_position, Size::new(text_content.len() as f32 * text_size/2.0 + 2.0 * growth_amount + 1.0, text_size + text_size/2.0));
                 frame.fill(&text_background, Color::from_rgba8(200, 200, 200, 1.0));