mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-07-08 18:16:08 +00:00
Axis crossing support added
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
@@ -109,6 +110,16 @@ namespace Radzen.Blazor
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool Inverted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the value on the perpendicular axis where this axis should cross.
|
||||
/// When set, the axis line and ticks are positioned at the corresponding location
|
||||
/// instead of the chart edge. For example, setting <c>CrossesAt="0"</c> on the category axis
|
||||
/// positions it where 0 is on the value axis.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public object? CrossesAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the label rotation angle in degrees. Set to <c>null</c> by default which means no rotation is applied. Has higher precedence than <see cref="LabelAutoRotation"/>.
|
||||
/// </summary>
|
||||
@@ -130,6 +141,7 @@ namespace Radzen.Blazor
|
||||
DidParameterChange(parameters, nameof(LabelRotation), LabelRotation) ||
|
||||
DidParameterChange(parameters, nameof(LabelAutoRotation), LabelAutoRotation) ||
|
||||
DidParameterChange(parameters, nameof(Inverted), Inverted) ||
|
||||
DidParameterChange(parameters, nameof(CrossesAt), CrossesAt) ||
|
||||
DidParameterChange(parameters, nameof(Step), Step);
|
||||
}
|
||||
|
||||
@@ -152,6 +164,12 @@ namespace Radzen.Blazor
|
||||
}
|
||||
}
|
||||
|
||||
internal double? GetCrossesAtValue()
|
||||
{
|
||||
if (CrossesAt == null) return null;
|
||||
return Convert.ToDouble(CrossesAt, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
internal abstract double Size { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,18 +26,18 @@
|
||||
|
||||
if (XAxis?.GridLines?.Visible == true && idx > start)
|
||||
{
|
||||
<Line class="rz-grid-line" X1="@x" Y1="@y1" X2="@x" Y2="@y2" Stroke="@(XAxis.GridLines.Stroke ?? "")" StrokeWidth="@(XAxis?.GridLines.StrokeWidth ?? 0)" LineType="@(XAxis?.GridLines.LineType ?? LineType.Solid)" />
|
||||
<Line class="rz-grid-line" X1="@x" Y1="@gridY1" X2="@x" Y2="@gridY2" Stroke="@(XAxis.GridLines.Stroke ?? "")" StrokeWidth="@(XAxis?.GridLines.StrokeWidth ?? 0)" LineType="@(XAxis?.GridLines.LineType ?? LineType.Solid)" />
|
||||
}
|
||||
}
|
||||
@if (!String.IsNullOrEmpty(XAxis?.Title?.Text))
|
||||
{
|
||||
if (isTopSide)
|
||||
{
|
||||
<CategoryAxisTitle X="@(Math.Abs(x2 - x1)/2)" Y="@(y1 - (Chart?.CategoryAxis?.Size ?? 0) + (XAxis?.Title?.Size ?? 0))" Text="@(XAxis?.Title?.Text ?? "")" />
|
||||
<CategoryAxisTitle X="@(Math.Abs(x2 - x1)/2)" Y="@(titleY - (Chart?.CategoryAxis?.Size ?? 0) + (XAxis?.Title?.Size ?? 0))" Text="@(XAxis?.Title?.Text ?? "")" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<CategoryAxisTitle X="@(Math.Abs(x2 - x1)/2)" Y="@(y1 + (Chart?.CategoryAxis?.Size ?? 0) - (XAxis?.Title?.Size ?? 0))" Text="@(XAxis?.Title?.Text ?? "")" />
|
||||
<CategoryAxisTitle X="@(Math.Abs(x2 - x1)/2)" Y="@(titleY + (Chart?.CategoryAxis?.Size ?? 0) - (XAxis?.Title?.Size ?? 0))" Text="@(XAxis?.Title?.Text ?? "")" />
|
||||
}
|
||||
}
|
||||
</g>
|
||||
@@ -50,6 +50,9 @@
|
||||
double x2;
|
||||
double y1;
|
||||
double y2;
|
||||
double gridY1;
|
||||
double gridY2;
|
||||
double titleY;
|
||||
|
||||
private AxisBase? XAxis { get; set; }
|
||||
private AxisBase? YAxis { get; set; }
|
||||
@@ -85,8 +88,19 @@
|
||||
y1 = Chart.ValueScale.Scale(valueTicks.Start);
|
||||
y2 = Chart.ValueScale.Scale(valueTicks.End);
|
||||
|
||||
gridY1 = y1;
|
||||
gridY2 = y2;
|
||||
titleY = y1;
|
||||
|
||||
isTopSide = y1 < y2;
|
||||
|
||||
var crossesAt = XAxis?.GetCrossesAtValue();
|
||||
if (crossesAt.HasValue)
|
||||
{
|
||||
var clampedValue = Math.Max(Math.Min(crossesAt.Value, valueTicks.End), valueTicks.Start);
|
||||
y1 = Chart.ValueScale.Scale(clampedValue);
|
||||
}
|
||||
|
||||
if (XAxis?.LabelRotation != null)
|
||||
{
|
||||
rotate = XAxis.LabelRotation;
|
||||
|
||||
@@ -25,18 +25,18 @@
|
||||
|
||||
if (YAxis?.GridLines?.Visible == true && idx > start)
|
||||
{
|
||||
<Line class="rz-grid-line" X1="@Math.Min(x1, x2)" Y1="@y" X2="@Math.Max(x1, x2)" Y2="@y" Stroke="@(YAxis.GridLines.Stroke)" StrokeWidth="@(YAxis.GridLines.StrokeWidth)" LineType="@(YAxis.GridLines.LineType)" />
|
||||
<Line class="rz-grid-line" X1="@Math.Min(gridX1, gridX2)" Y1="@y" X2="@Math.Max(gridX1, gridX2)" Y2="@y" Stroke="@(YAxis.GridLines.Stroke)" StrokeWidth="@(YAxis.GridLines.StrokeWidth)" LineType="@(YAxis.GridLines.LineType)" />
|
||||
}
|
||||
}
|
||||
@if (!String.IsNullOrEmpty(YAxis?.Title?.Text))
|
||||
{
|
||||
if (RenderAsRightSide)
|
||||
{
|
||||
<ValueAxisTitle X="@(axisX + (YAxis?.Size ?? 0) - (YAxis?.Title?.Size ?? 0))" Y="@(Math.Abs(y1 - y2)/2)" Text="@(YAxis?.Title?.Text ?? "")" RightSide="true" />
|
||||
<ValueAxisTitle X="@(titleX + (YAxis?.Size ?? 0) - (YAxis?.Title?.Size ?? 0))" Y="@(Math.Abs(y1 - y2)/2)" Text="@(YAxis?.Title?.Text ?? "")" RightSide="true" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<ValueAxisTitle X="@(axisX - (Chart?.ValueAxis?.Size ?? 0) + (YAxis?.Title?.Size ?? 0))" Y="@(Math.Abs(y1 - y2)/2)" Text="@(YAxis?.Title?.Text ?? "")" />
|
||||
<ValueAxisTitle X="@(titleX - (Chart?.ValueAxis?.Size ?? 0) + (YAxis?.Title?.Size ?? 0))" Y="@(Math.Abs(y1 - y2)/2)" Text="@(YAxis?.Title?.Text ?? "")" />
|
||||
}
|
||||
}
|
||||
</g>
|
||||
@@ -56,6 +56,9 @@
|
||||
double y1;
|
||||
double y2;
|
||||
double axisX;
|
||||
double gridX1;
|
||||
double gridX2;
|
||||
double titleX;
|
||||
ScaleBase? scale;
|
||||
bool isFlippedToRight;
|
||||
private AxisBase? XAxis { get; set; }
|
||||
@@ -105,6 +108,9 @@
|
||||
y1 = scale.Scale(start);
|
||||
y2 = scale.Scale(end);
|
||||
|
||||
gridX1 = x1;
|
||||
gridX2 = x2;
|
||||
|
||||
// Detect if the primary value axis has been flipped to the right side due to category inversion
|
||||
isFlippedToRight = string.IsNullOrEmpty(AxisName) && x1 > x2;
|
||||
|
||||
@@ -121,6 +127,16 @@
|
||||
else
|
||||
{
|
||||
axisX = x1;
|
||||
|
||||
var crossesAt = YAxis?.GetCrossesAtValue();
|
||||
if (crossesAt.HasValue)
|
||||
{
|
||||
var clampedValue = Math.Max(Math.Min(crossesAt.Value, categoryTicks.End), categoryTicks.Start);
|
||||
axisX = Chart.CategoryScale.Scale(clampedValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Title stays at the chart edge, not the crossing position
|
||||
titleX = IsRightSide ? axisX : (YAxis?.GetCrossesAtValue() != null ? x1 : axisX);
|
||||
}
|
||||
}
|
||||
|
||||
38
RadzenBlazorDemos/Pages/AxisCrossingChartConfig.razor
Normal file
38
RadzenBlazorDemos/Pages/AxisCrossingChartConfig.razor
Normal file
@@ -0,0 +1,38 @@
|
||||
<RadzenStack class="rz-p-0 rz-p-md-6 rz-p-lg-12">
|
||||
<RadzenRow>
|
||||
<RadzenColumn Size="12">
|
||||
<RadzenChart Style="height: 480px">
|
||||
<RadzenLineSeries Data="@data" CategoryProperty="X" ValueProperty="Y" Title="Cubic Spline"
|
||||
Stroke="var(--rz-series-1)" LineType="LineType.Solid" Smooth="true">
|
||||
<RadzenMarkers MarkerType="MarkerType.Diamond" Fill="var(--rz-series-2)" Stroke="var(--rz-series-2)" Size="8" />
|
||||
</RadzenLineSeries>
|
||||
<RadzenCategoryAxis CrossesAt="0" FormatString="{0}" />
|
||||
<RadzenValueAxis CrossesAt="0">
|
||||
<RadzenGridLines Visible="true" />
|
||||
</RadzenValueAxis>
|
||||
<RadzenChartTooltipOptions Visible="true" />
|
||||
<RadzenLegend Position="LegendPosition.Bottom" />
|
||||
</RadzenChart>
|
||||
</RadzenColumn>
|
||||
</RadzenRow>
|
||||
</RadzenStack>
|
||||
|
||||
@code {
|
||||
class DataPoint
|
||||
{
|
||||
public double X { get; set; }
|
||||
public double Y { get; set; }
|
||||
}
|
||||
|
||||
DataPoint[] data = new DataPoint[]
|
||||
{
|
||||
new DataPoint { X = -6, Y = 3 },
|
||||
new DataPoint { X = -3, Y = -5 },
|
||||
new DataPoint { X = -1, Y = -1 },
|
||||
new DataPoint { X = 0, Y = 0 },
|
||||
new DataPoint { X = 1, Y = 1 },
|
||||
new DataPoint { X = 3, Y = 3.5 },
|
||||
new DataPoint { X = 5, Y = 4 },
|
||||
new DataPoint { X = 7, Y = 3.8 },
|
||||
};
|
||||
}
|
||||
15
RadzenBlazorDemos/Pages/AxisCrossingChartPage.razor
Normal file
15
RadzenBlazorDemos/Pages/AxisCrossingChartPage.razor
Normal file
@@ -0,0 +1,15 @@
|
||||
@page "/axis-crossing-chart"
|
||||
|
||||
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
|
||||
Radzen Blazor Chart with axis crossing
|
||||
</RadzenText>
|
||||
<RadzenText TextStyle="TextStyle.Subtitle1" TagName="TagName.P" class="rz-pb-4">
|
||||
Set <code>CrossesAt</code> on <code>RadzenValueAxis</code> or <code>RadzenCategoryAxis</code> to position the axis
|
||||
at a specific value on the perpendicular axis. For example, setting <code>CrossesAt="0"</code> on both axes
|
||||
creates a standard mathematical coordinate system where axes intersect at the origin.
|
||||
</RadzenText>
|
||||
|
||||
<RadzenExample ComponentName="Chart" Example="AxisCrossingChartConfig"
|
||||
DocumentationLink="https://blazor.radzen.com/docs/guides/components/chart.html">
|
||||
<AxisCrossingChartConfig />
|
||||
</RadzenExample>
|
||||
@@ -2388,6 +2388,14 @@ namespace RadzenBlazorDemos
|
||||
New = true
|
||||
},
|
||||
new Example
|
||||
{
|
||||
Name = "Axis Crossing",
|
||||
Path = "axis-crossing-chart",
|
||||
Description = "Radzen Blazor Chart with axis crossing to position axes at specific values instead of chart edges.",
|
||||
Tags = new [] { "chart", "graph", "axis", "crossing", "origin", "intersect" },
|
||||
New = true
|
||||
},
|
||||
new Example
|
||||
{
|
||||
Toc = [ new () { Text = "Legend position", Anchor = "#legend-position" }, new () { Text = "Hide the legend", Anchor = "#hide-the-legend" } ],
|
||||
Name = "Legend",
|
||||
|
||||
Reference in New Issue
Block a user