From 7591599aeaaaa1ed3d7fc8b2c1f69635bfca63b5 Mon Sep 17 00:00:00 2001 From: John Limos <103324036+jvlimos@users.noreply.github.com> Date: Tue, 17 Mar 2026 13:14:21 +0800 Subject: [PATCH] Add zoom buttons functionality --- .../Zooming/Charts-Zooming-ZoomAndPan.razor | 87 +++++++++++-- .../Charts-Zooming-ZoomAndPan.razor.css | 3 - .../Zooming/Charts-Zooming-ZoomArea.razor | 117 +++++++++++++++--- .../Zooming/Charts-Zooming-ZoomArea.razor.css | 4 - .../BlazorDemo/wwwroot/css/dx-demo-pages.css | 19 +++ .../images/icons/arrow-reset-fluent.svg | 3 + .../wwwroot/images/icons/zoom-in-fluent.svg | 3 + .../wwwroot/images/icons/zoom-out-fluent.svg | 3 + 8 files changed, 209 insertions(+), 30 deletions(-) create mode 100644 demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/images/icons/arrow-reset-fluent.svg create mode 100644 demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/images/icons/zoom-in-fluent.svg create mode 100644 demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/images/icons/zoom-out-fluent.svg diff --git a/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomAndPan.razor b/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomAndPan.razor index 3733b1b3..1de40576 100644 --- a/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomAndPan.razor +++ b/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomAndPan.razor @@ -1,8 +1,24 @@ + + + + + + - - - - - + @code { IEnumerable UsdJpyData; DxChart chart; @inject ICurrencyExchangeDataProvider UsdJpyDataProvider + DateTime startDate = new DateTime(2020, 01, 01); DateTime endDate = new DateTime(2021, 01, 29); - async void ExportChart(ChartExportFormat format) { + DateTime currentStart = new DateTime(2020, 01, 01); + DateTime currentEnd = new DateTime(2021, 01, 29); + + const double ZoomFactor = 0.25; + + async void ExportChart(ChartExportFormat format) + { await chart?.ExportAsync("ZoomAndPanChart", format); } - protected override async Task OnInitializedAsync() { + + protected override async Task OnInitializedAsync() + { UsdJpyData = await UsdJpyDataProvider.GetDataAsync(); } - public void SetArgumentAxisVisualRange() { - chart.SetArgumentAxisVisualRange([startDate, endDate]); + + void OnVisualRangeChanged(ChartVisualRangeChangedEventArgs args) + { + if (args.IsArgumentAxis) + { + currentStart = (DateTime)args.CurrentRange[0]; + currentEnd = (DateTime)args.CurrentRange[1]; + } + } + + static void CalcDateRange( + DateTime start, DateTime end, DateTime min, DateTime max, bool zoomIn, + out DateTime newStart, out DateTime newEnd) + { + var totalTicks = (end - start).Ticks; + var delta = (long)(totalTicks * ZoomFactor / 2); + if (zoomIn) + { + newStart = start.AddTicks(delta); + newEnd = end.AddTicks(-delta); + } + else + { + newStart = start.AddTicks(-delta) < min ? min : start.AddTicks(-delta); + newEnd = end.AddTicks(delta) > max ? max : end.AddTicks(delta); + } + } + + void ZoomIn() + { + CalcDateRange(currentStart, currentEnd, startDate, endDate, true, out DateTime newStart, out DateTime newEnd); + if (newEnd > newStart) + { + currentStart = newStart; + currentEnd = newEnd; + chart.SetArgumentAxisVisualRange([(object)currentStart, (object)currentEnd]); + } + } + + void ZoomOut() + { + CalcDateRange(currentStart, currentEnd, startDate, endDate, false, out currentStart, out currentEnd); + chart.SetArgumentAxisVisualRange([(object)currentStart, (object)currentEnd]); + } + + void ResetZoom() + { + currentStart = startDate; + currentEnd = endDate; + chart.SetArgumentAxisVisualRange([(object)currentStart, (object)currentEnd]); } } diff --git a/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomAndPan.razor.css b/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomAndPan.razor.css index 5ddfe9a3..e69de29b 100644 --- a/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomAndPan.razor.css +++ b/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomAndPan.razor.css @@ -1,3 +0,0 @@ -.chart-reset-btn { - margin-top: 1rem; -} diff --git a/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomArea.razor b/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomArea.razor index a6adec9d..14ff6a8e 100644 --- a/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomArea.razor +++ b/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomArea.razor @@ -1,30 +1,47 @@ @inject IChartBirthLifeDataProvider ChartBirthLifeDataProvider + + + + + + + - + + HorizontalAlignment="HorizontalAlignment.Right" /> - + - + - + - + @@ -35,26 +52,98 @@ ValueAxisZoomAndPanMode="ChartAxisZoomAndPanMode.Both" AllowDragToZoom="true" AllowMouseWheel="true" - PanKey="ChartEventPanKey.Shift"/> + PanKey="ChartEventPanKey.Shift" /> - - - - @code { IEnumerable DataSource = Enumerable.Empty(); DxChart chart; - protected override void OnInitialized() { + double argMin, argMax, valMin, valMax; + double currentArgStart, currentArgEnd, currentValStart, currentValEnd; + + const double ZoomFactor = 0.25; + + protected override void OnInitialized() + { DataSource = ChartBirthLifeDataProvider.GenerateData(); + argMin = DataSource.Min(d => d.LifeExp); + argMax = DataSource.Max(d => d.LifeExp); + valMin = DataSource.Min(d => d.BirthRate); + valMax = DataSource.Max(d => d.BirthRate); + currentArgStart = argMin; + currentArgEnd = argMax; + currentValStart = valMin; + currentValEnd = valMax; } - public void ResetVisualRange() { - chart.ResetVisualRange(); + void OnVisualRangeChanged(ChartVisualRangeChangedEventArgs args) + { + if (args.IsArgumentAxis) + { + currentArgStart = Convert.ToDouble(args.CurrentRange[0]); + currentArgEnd = Convert.ToDouble(args.CurrentRange[1]); + } + else + { + currentValStart = Convert.ToDouble(args.CurrentRange[0]); + currentValEnd = Convert.ToDouble(args.CurrentRange[1]); + } + } + + static void CalcRange(double start, double end, double min, double max, bool zoomIn, out double newStart, out double newEnd) + { + var span = end - start; + var delta = span * ZoomFactor / 2; + if (zoomIn) + { + newStart = start + delta; + newEnd = end - delta; + } + else + { + newStart = Math.Max(start - delta, min); + newEnd = Math.Min(end + delta, max); + } } + void ApplyVisualRange() + { + chart.SetArgumentAxisVisualRange([(object)currentArgStart, (object)currentArgEnd]); + chart.SetValueAxisVisualRange([(object)currentValStart, (object)currentValEnd]); + } + + void ZoomIn() + { + CalcRange(currentArgStart, currentArgEnd, argMin, argMax, true, out double newArgStart, out double newArgEnd); + CalcRange(currentValStart, currentValEnd, valMin, valMax, true, out double newValStart, out double newValEnd); + + if (newArgEnd > newArgStart && newValEnd > newValStart) + { + currentArgStart = newArgStart; + currentArgEnd = newArgEnd; + currentValStart = newValStart; + currentValEnd = newValEnd; + ApplyVisualRange(); + } + } + + void ZoomOut() + { + CalcRange(currentArgStart, currentArgEnd, argMin, argMax, false, out currentArgStart, out currentArgEnd); + CalcRange(currentValStart, currentValEnd, valMin, valMax, false, out currentValStart, out currentValEnd); + ApplyVisualRange(); + } + + void ResetZoom() + { + chart.ResetVisualRange(); + currentArgStart = argMin; + currentArgEnd = argMax; + currentValStart = valMin; + currentValEnd = valMax; + } } diff --git a/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomArea.razor.css b/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomArea.razor.css index b60a0d5a..ff43c46f 100644 --- a/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomArea.razor.css +++ b/demo/BlazorDemo.ServerSide/BlazorDemo/Pages/Charts/Zooming/Charts-Zooming-ZoomArea.razor.css @@ -1,7 +1,3 @@ .chart-tooltip { margin: 0.75rem; } - -.chart-reset-btn { - margin-top: 1rem; -} diff --git a/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/css/dx-demo-pages.css b/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/css/dx-demo-pages.css index 87990247..d731f268 100644 --- a/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/css/dx-demo-pages.css +++ b/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/css/dx-demo-pages.css @@ -785,6 +785,10 @@ body.modal-open .main { /*Chart*/ +.chart-toolbar { + margin-bottom: 1em; +} + .chart-icon { width: 1rem; height: 1rem; @@ -832,6 +836,21 @@ body.modal-open .main { mask-image: url("../images/icons/export-pdf.svg"); } +.chart-icon-arrow-reset { + -webkit-mask-image: url("../images/icons/arrow-reset-fluent.svg"); + mask-image: url("../images/icons/arrow-reset-fluent.svg"); +} + +.chart-icon-zoom-in { + -webkit-mask-image: url("../images/icons/zoom-in-fluent.svg"); + mask-image: url("../images/icons/zoom-in-fluent.svg"); +} + +.chart-icon-zoom-out { + -webkit-mask-image: url("../images/icons/zoom-out-fluent.svg"); + mask-image: url("../images/icons/zoom-out-fluent.svg"); +} + @media (max-width: 899.98px) { .component-container .chart-container .dxbl-chart-root { diff --git a/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/images/icons/arrow-reset-fluent.svg b/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/images/icons/arrow-reset-fluent.svg new file mode 100644 index 00000000..e9cafb51 --- /dev/null +++ b/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/images/icons/arrow-reset-fluent.svg @@ -0,0 +1,3 @@ + + + diff --git a/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/images/icons/zoom-in-fluent.svg b/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/images/icons/zoom-in-fluent.svg new file mode 100644 index 00000000..b95c8309 --- /dev/null +++ b/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/images/icons/zoom-in-fluent.svg @@ -0,0 +1,3 @@ + + + diff --git a/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/images/icons/zoom-out-fluent.svg b/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/images/icons/zoom-out-fluent.svg new file mode 100644 index 00000000..c0dedfdc --- /dev/null +++ b/demo/BlazorDemo.ServerSide/BlazorDemo/wwwroot/images/icons/zoom-out-fluent.svg @@ -0,0 +1,3 @@ + + +