diff --git a/Documentation~/zh/changelog.md b/Documentation~/zh/changelog.md index 4fd234fa..5cde791c 100644 --- a/Documentation~/zh/changelog.md +++ b/Documentation~/zh/changelog.md @@ -79,6 +79,7 @@ slug: /changelog ## master +* (2025.02.23) 增加`Bar`的`realtimeSort`支持实时排序 * (2025.02.19) 增加`Tooltip`的`itemFormatter`对`\n`换行的支持 * (2025.02.18) 优化`Tooltip`的对齐方式 * (2025.02.09) 修复`SaveAsImage`保存图片时不支持透明度的问题 (#337) diff --git a/Editor/Series/BarEditor.cs b/Editor/Series/BarEditor.cs index f28b1bbd..d1ec01b1 100644 --- a/Editor/Series/BarEditor.cs +++ b/Editor/Series/BarEditor.cs @@ -22,6 +22,11 @@ namespace XCharts.Editor PropertyField("m_BarWidth"); PropertyField("m_BarGap"); PropertyField("m_BarMaxWidth"); + PropertyField("m_RealtimeSort"); + if(serie.useSortData) + { + PropertyField("m_DataSortType"); + } if (serie.IsUseCoord()) { PropertyField("m_RoundCap"); diff --git a/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs b/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs index ce8aa36e..a96a7b5c 100644 --- a/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs +++ b/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs @@ -53,7 +53,7 @@ namespace XCharts.Runtime if (axis.context.labelObjectList.Count <= 0) InitAngleAxis(axis); else - axis.UpdateLabelText(runtimeWidth, null, false); + UpdateLabelText(axis, runtimeWidth, null, false); } private void InitAngleAxis(AngleAxis axis) diff --git a/Runtime/Component/Axis/Axis.cs b/Runtime/Component/Axis/Axis.cs index 83fc972a..0d048a1c 100644 --- a/Runtime/Component/Axis/Axis.cs +++ b/Runtime/Component/Axis/Axis.cs @@ -836,22 +836,6 @@ namespace XCharts.Runtime return IsCategory() ? GetDataList(dataZoom).Count : 0; } - /// - /// 更新刻度标签文字 - /// - /// - internal void UpdateLabelText(float coordinateWidth, DataZoom dataZoom, bool forcePercent) - { - for (int i = 0; i < context.labelObjectList.Count; i++) - { - if (context.labelObjectList[i] != null) - { - var text = AxisHelper.GetLabelName(this, coordinateWidth, i, context.destMinValue, context.destMaxValue, dataZoom, forcePercent); - context.labelObjectList[i].SetText(text); - } - } - } - internal Vector3 GetLabelObjectPosition(int index) { if (context.labelObjectList != null && index < context.labelObjectList.Count) diff --git a/Runtime/Component/Axis/AxisContext.cs b/Runtime/Component/Axis/AxisContext.cs index 6090feab..11e4e36f 100644 --- a/Runtime/Component/Axis/AxisContext.cs +++ b/Runtime/Component/Axis/AxisContext.cs @@ -69,6 +69,7 @@ namespace XCharts.Runtime public List runtimeData { get { return m_RuntimeData; } } public List labelValueList { get { return m_LabelValueList; } } public List labelObjectList { get { return m_AxisLabelList; } } + public List sortedDataIndices { get { return m_SortedDataIndices; } } public int dataZoomStartIndex; /// /// 添加过的历史数据总数 @@ -86,6 +87,7 @@ namespace XCharts.Runtime private List m_AxisLabelList = new List(); private List m_LabelValueList = new List(); private List m_RuntimeData = new List(); + private List m_SortedDataIndices = new List(); internal void Clear() { diff --git a/Runtime/Component/Axis/AxisHandler.cs b/Runtime/Component/Axis/AxisHandler.cs index 23e69d47..239102fc 100644 --- a/Runtime/Component/Axis/AxisHandler.cs +++ b/Runtime/Component/Axis/AxisHandler.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using XCharts.Runtime; @@ -149,6 +150,10 @@ namespace XCharts axis.context.minValue = 0; axis.context.maxValue = axis.data.Count > 0 ? axis.data.Count - 1 : SeriesHelper.GetMaxSerieDataCount(chart.series) - 1; axis.context.minMaxRange = axis.context.maxValue; + if (chart.HasRealtimeSortSerie()) + { + UpdateAxisLabelText(axis); + } return; } @@ -236,7 +241,59 @@ namespace XCharts var isPercentStack = SeriesHelper.IsPercentStack(chart.series); var dataZoom = chart.GetDataZoomOfAxis(axis); - axis.UpdateLabelText(runtimeWidth, dataZoom, isPercentStack); + UpdateLabelText(axis, runtimeWidth, dataZoom, isPercentStack); + } + + internal void UpdateLabelText(Axis axis, float coordinateWidth, DataZoom dataZoom, bool forcePercent) + { + var context = axis.context; + var destMaxValue = context.destMaxValue; + var destMinValue = context.destMinValue; + var isCategory = axis.IsCategory(); + var serie = chart.GetSerie(0); + if (isCategory && serie != null && serie.useSortData) + { + var showData = serie.GetDataList(dataZoom); + var isChanged = CheckSortedDataChanged(axis, showData); + if (isChanged) + { + for (int i = 0; i < context.labelObjectList.Count; i++) + { + if (context.labelObjectList[i] != null) + { + var index = i < showData.Count ? showData[i].index : i; + var text = AxisHelper.GetLabelName(axis, coordinateWidth, index, destMinValue, destMaxValue, dataZoom, forcePercent); + context.labelObjectList[i].SetText(text); + } + } + axis.context.sortedDataIndices.Clear(); + for (int i = 0; i < showData.Count; i++) + { + axis.context.sortedDataIndices.Add(showData[i].index); + } + } + } + else + { + for (int i = 0; i < context.labelObjectList.Count; i++) + { + if (context.labelObjectList[i] != null) + { + var text = AxisHelper.GetLabelName(axis, coordinateWidth, i, destMinValue, destMaxValue, dataZoom, forcePercent); + context.labelObjectList[i].SetText(text); + } + } + } + } + + private bool CheckSortedDataChanged(Axis axis, List dataList) + { + if (dataList.Count != axis.context.sortedDataIndices.Count) return true; + for (int i = 0; i < dataList.Count; i++) + { + if (dataList[i].index != axis.context.sortedDataIndices[i]) return true; + } + return false; } internal void UpdateAxisTickValueList(Axis axis) @@ -558,8 +615,8 @@ namespace XCharts } if (axis.axisName.show) { - ChartLabel label = null; - var relativedDist = (relativedAxis == null ? 0 : relativedAxis.context.offset); + ChartLabel label; + var relativedDist = relativedAxis == null ? 0 : relativedAxis.context.offset; var zeroPos = new Vector3(axisStartX, axisStartY + relativedDist); var offset = axis.axisName.labelStyle.offset; var autoColor = axis.axisLine.GetColor(chart.theme.axis.lineColor); diff --git a/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs b/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs index 6feca7f5..036665e7 100644 --- a/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs +++ b/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs @@ -80,7 +80,7 @@ namespace XCharts.Runtime InitRadiusAxis(axis); else { - axis.UpdateLabelText(polar.context.radius, null, false); + UpdateLabelText(axis, polar.context.radius, null, false); } } diff --git a/Runtime/Component/Tooltip/TooltipHandler.cs b/Runtime/Component/Tooltip/TooltipHandler.cs index 449b7991..e9b63fab 100644 --- a/Runtime/Component/Tooltip/TooltipHandler.cs +++ b/Runtime/Component/Tooltip/TooltipHandler.cs @@ -641,6 +641,7 @@ namespace XCharts.Runtime private bool GetAxisCategory(int gridIndex, ref int dataIndex, ref string category, ref int timestamp, ref double axisRange) { + var needSort = chart.HasRealtimeSortSerie(); foreach (var component in chart.components) { if (component is Axis) @@ -653,7 +654,12 @@ namespace XCharts.Runtime dataIndex = double.IsNaN(axis.context.pointerValue) ? axis.context.dataZoomStartIndex : (int)axis.context.axisTooltipValue; - category = axis.GetData(dataIndex); + int axisIndex = dataIndex; + if (needSort && axisIndex < axis.context.sortedDataIndices.Count) + { + axisIndex = axis.context.sortedDataIndices[axisIndex]; + } + category = axis.GetData(axisIndex); return true; } else if (axis.IsTime()) diff --git a/Runtime/Internal/BaseChart.Serie.cs b/Runtime/Internal/BaseChart.Serie.cs index ce742f00..9e00ba58 100644 --- a/Runtime/Internal/BaseChart.Serie.cs +++ b/Runtime/Internal/BaseChart.Serie.cs @@ -101,6 +101,16 @@ namespace XCharts.Runtime return false; } + public bool HasRealtimeSortSerie() + { + foreach (var serie in m_Series) + { + if (serie.useSortData) + return true; + } + return false; + } + public T GetSerie() where T : Serie { foreach (var serie in m_Series) diff --git a/Runtime/Serie/Bar/Bar.cs b/Runtime/Serie/Bar/Bar.cs index 5ad093e3..aad83f37 100644 --- a/Runtime/Serie/Bar/Bar.cs +++ b/Runtime/Serie/Bar/Bar.cs @@ -1,3 +1,5 @@ +using UnityEngine; + namespace XCharts.Runtime { [System.Serializable] @@ -11,6 +13,8 @@ namespace XCharts.Runtime [SerieDataExtraField("m_Ignore")] public class Bar : Serie, INeedSerieContainer { + public override bool useSortData { get { return realtimeSort; } } + public int containerIndex { get; internal set; } public int containterInstanceId { get; internal set; } diff --git a/Runtime/Serie/Bar/BarHandler.cs b/Runtime/Serie/Bar/BarHandler.cs index e40b6298..ff26d233 100644 --- a/Runtime/Serie/Bar/BarHandler.cs +++ b/Runtime/Serie/Bar/BarHandler.cs @@ -161,6 +161,10 @@ namespace XCharts.Runtime m_SerieGrid = chart.GetChartComponent(axis.gridIndex); if (m_SerieGrid == null) return; + if(serie.useSortData) + { + SerieHelper.UpdateSerieRuntimeFilterData(serie); + } var dataZoom = chart.GetDataZoomOfAxis(axis); var showData = serie.GetDataList(dataZoom); diff --git a/Runtime/Serie/Serie.cs b/Runtime/Serie/Serie.cs index ed02b5be..8ae952c7 100644 --- a/Runtime/Serie/Serie.cs +++ b/Runtime/Serie/Serie.cs @@ -305,6 +305,7 @@ namespace XCharts.Runtime [SerializeField] private float m_Top; [SerializeField] private float m_Bottom; [SerializeField] private bool m_InsertDataToHead; + [SerializeField][Since("v3.14.0")] private bool m_RealtimeSort = false; [SerializeField] private LineStyle m_LineStyle = new LineStyle(); [SerializeField] private SerieSymbol m_Symbol = new SerieSymbol(); @@ -983,6 +984,15 @@ namespace XCharts.Runtime set { if (PropertyUtil.SetStruct(ref m_MinShowLabelValue, value)) { SetVerticesDirty(); } } } /// + /// Whether to enable realtime sorting, which is used for bar-racing effect. Currently only available in Bar. + /// ||是否开启实时排序,用来实现动态排序图效果。目前仅在Bar中生效。 + /// + public bool realtimeSort + { + get { return m_RealtimeSort; } + set { if (PropertyUtil.SetStruct(ref m_RealtimeSort, value)) SetVerticesDirty(); } + } + /// /// 系列中的数据内容数组。SerieData可以设置1到n维数据。 /// public List data { get { return m_Data; } }