增加Serie的barPercentStack参数,可配置百分比堆叠柱状图

This commit is contained in:
monitor1394
2019-10-18 06:48:24 +08:00
parent 8465f0cb06
commit 7c7f21fd27
11 changed files with 32332 additions and 9498 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,8 @@ namespace XCharts
SerializedProperty m_SampleDist = prop.FindPropertyRelative("m_SampleDist");
SerializedProperty m_SampleType = prop.FindPropertyRelative("m_SampleType");
SerializedProperty m_SampleAverage = prop.FindPropertyRelative("m_SampleAverage");
SerializedProperty m_BarType = prop.FindPropertyRelative("m_BarType");
SerializedProperty m_BarPercentStack = prop.FindPropertyRelative("m_BarPercentStack");
SerializedProperty m_BarWidth = prop.FindPropertyRelative("m_BarWidth");
SerializedProperty m_BarGap = prop.FindPropertyRelative("m_BarGap");
SerializedProperty m_AreaStyle = prop.FindPropertyRelative("m_AreaStyle");
@@ -114,6 +116,10 @@ namespace XCharts
}
if (serieType == SerieType.Bar)
{
EditorGUI.PropertyField(drawRect, m_BarType);
drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(drawRect, m_BarPercentStack);
drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(drawRect, m_BarWidth);
drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(drawRect, m_BarGap);
@@ -350,7 +356,7 @@ namespace XCharts
}
if (serieType == SerieType.Bar)
{
height += 2 * EditorGUIUtility.singleLineHeight + 1 * EditorGUIUtility.standardVerticalSpacing;
height += 4 * EditorGUIUtility.singleLineHeight + 3 * EditorGUIUtility.standardVerticalSpacing;
}
if (m_DataFoldout[index])
{

View File

@@ -1,5 +1,4 @@
using System.Net.Mime;
using System;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
@@ -435,12 +434,14 @@ namespace XCharts
/// <param name="maxValue"></param>
/// <param name="dataZoom"></param>
/// <returns></returns>
public string GetLabelName(float coordinateWidth, int index, float minValue, float maxValue, DataZoom dataZoom)
public string GetLabelName(float coordinateWidth, int index, float minValue, float maxValue,
DataZoom dataZoom, bool forcePercent)
{
int split = GetSplitNumber(coordinateWidth, dataZoom);
if (m_Type == AxisType.Value)
{
float value = 0;
if (forcePercent) maxValue = 100;
if (m_Interval > 0)
{
if (index == split - 1) value = maxValue;
@@ -450,7 +451,8 @@ namespace XCharts
{
value = (minValue + (maxValue - minValue) * index / (split - 1));
}
return m_AxisLabel.GetFormatterContent(value);
if (forcePercent) return string.Format("{0}%", (int)value);
else return m_AxisLabel.GetFormatterContent(value);
}
var showData = GetDataList(dataZoom);
int dataCount = showData.Count;
@@ -521,13 +523,13 @@ namespace XCharts
/// 更新刻度标签文字
/// </summary>
/// <param name="dataZoom"></param>
public void UpdateLabelText(float coordinateWidth, DataZoom dataZoom)
public void UpdateLabelText(float coordinateWidth, DataZoom dataZoom, bool forcePercent)
{
for (int i = 0; i < axisLabelTextList.Count; i++)
{
if (axisLabelTextList[i] != null)
{
axisLabelTextList[i].text = GetLabelName(coordinateWidth, i, minValue, maxValue, dataZoom);
axisLabelTextList[i].text = GetLabelName(coordinateWidth, i, minValue, maxValue, dataZoom, forcePercent);
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Text;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine;
@@ -119,6 +120,22 @@ namespace XCharts
DashDotDot
}
public enum BarType
{
/// <summary>
/// 普通柱形图
/// </summary>
Normal,
/// <summary>
/// 斑马柱形图
/// </summary>
ZebraLine,
/// <summary>
/// 胶囊柱形图
/// </summary>
Capsule
}
/// <summary>
/// 采样类型
/// </summary>
@@ -163,23 +180,25 @@ namespace XCharts
[SerializeField] protected int m_MaxCache;
[SerializeField] private AreaStyle m_AreaStyle = AreaStyle.defaultAreaStyle;
[SerializeField] private SerieSymbol m_Symbol = new SerieSymbol();
[SerializeField] private LineType m_LineType = LineType.Normal;
[SerializeField] private float m_SampleDist = 0;
[SerializeField] private SampleType m_SampleType = SampleType.Average;
[SerializeField] private float m_SampleAverage = 0;
[SerializeField] private LineType m_LineType = LineType.Normal;
[SerializeField] private LineStyle m_LineStyle = new LineStyle();
[SerializeField] private BarType m_BarType = BarType.Normal;
[SerializeField] private bool m_BarPercentStack = false;
[SerializeField] private float m_BarWidth = 0.6f;
[SerializeField] private float m_BarGap = 0.3f; // 30%
[SerializeField] private float m_BarCategoryGap = 0.2f; // 20%
#region PieChart field
[SerializeField] private bool m_ClickOffset = true;
[SerializeField] private RoseType m_RoseType = RoseType.None;
[SerializeField] private float m_Space;
[SerializeField] private float[] m_Center = new float[2] { 0.5f, 0.5f };
[SerializeField] private float[] m_Radius = new float[2] { 0, 80 };
#endregion
[SerializeField] private SerieLabel m_Label = new SerieLabel();
[SerializeField] private Animation m_Animation = new Animation();
[SerializeField] private LineArrow m_LineArrow = new LineArrow();
@@ -266,7 +285,6 @@ namespace XCharts
/// The style of area.
/// 区域填充样式。
/// </summary>
/// <value></value>
public AreaStyle areaStyle { get { return m_AreaStyle; } set { m_AreaStyle = value; } }
/// <summary>
/// the symbol of serie data item.
@@ -277,13 +295,11 @@ namespace XCharts
/// The type of line chart.
/// 折线图样式类型。
/// </summary>
/// <value></value>
public LineType lineType { get { return m_LineType; } set { m_LineType = value; } }
/// <summary>
/// the min pixel dist of sample.
/// 采样的最小像素距离默认为0时不采样。当两个数据点间的水平距离小于改值时开启采样保证两点间的水平距离不小于改值。
/// </summary>
/// <value></value>
public float sampleDist { get { return m_SampleDist; } set { m_SampleDist = value < 0 ? 0 : value; } }
/// <summary>
/// the type of sample.
@@ -293,19 +309,24 @@ namespace XCharts
/// <summary>
/// 设定的采样平均值。当sampleType 为 Peak 时用于和过滤数据的平均值做对比是取最大值还是最小值。默认为0时会实时计算所有数据的平均值。
/// </summary>
/// <value></value>
public float sampleAverage { get { return m_SampleAverage; } set { m_SampleAverage = value; } }
/// <summary>
/// The style of line.
/// 线条样式。
/// </summary>
/// <value></value>
public LineStyle lineStyle { get { return m_LineStyle; } set { m_LineStyle = value; } }
/// <summary>
/// 柱形图类型。
/// </summary>
public BarType barType { get { return m_BarType; } set { m_BarType = value; } }
/// <summary>
/// 柱形图是否为百分比堆积。
/// </summary>
public bool barPercentStack { get { return m_BarPercentStack; } set { m_BarPercentStack = value; } }
/// <summary>
/// The width of the bar. Adaptive when default 0.
/// 柱条的宽度,不设时自适应。支持设置成相对于类目宽度的百分比。
/// </summary>
/// <value></value>
public float barWidth { get { return m_BarWidth; } set { m_BarWidth = value; } }
/// <summary>
/// The gap between bars between different series, is a percent value like '0.3f' , which means 30% of the bar width, can be set as a fixed value.
@@ -327,7 +348,6 @@ namespace XCharts
/// 同一系列的柱间距离默认为类目间距的20%,可设固定值。
/// 在同一坐标系上,此属性会被多个 'bar' 系列共享。此属性应设置于此坐标系中最后一个 'bar' 系列上才会生效,并且是对此坐标系中所有 'bar' 系列生效。
/// </summary>
/// <value></value>
public float barCategoryGap { get { return m_BarCategoryGap; } set { m_BarCategoryGap = value; } }
/// <summary>
/// Whether offset when mouse click pie chart item.
@@ -363,7 +383,6 @@ namespace XCharts
/// The start animation.
/// 起始动画。
/// </summary>
/// <value></value>
public Animation animation { get { return m_Animation; } set { m_Animation = value; } }
/// <summary>
/// The arrow of line.

View File

@@ -574,27 +574,38 @@ namespace XCharts
{
float min = int.MaxValue;
float max = int.MinValue;
var isPercentStack = IsPercentStack(SerieType.Bar);
if (!IsStack() || (isValueAxis && !yValue))
{
for (int i = 0; i < m_Series.Count; i++)
{
if (m_Series[i].axisIndex != axisIndex) continue;
var serie = m_Series[i];
if (serie.axisIndex != axisIndex) continue;
if (IsActive(i))
{
var showData = m_Series[i].GetDataList(dataZoom);
foreach (var data in showData)
if (isPercentStack && IsPercentStack(serie.name, SerieType.Bar))
{
if (yValue)
Debug.LogError("minmax:" + serie.name);
if (100 > max) max = 100;
if (0 < min) min = 0;
}
else
{
var showData = m_Series[i].GetDataList(dataZoom);
foreach (var data in showData)
{
if (data.data[1] > max) max = data.data[1];
if (data.data[1] < min) min = data.data[1];
if (yValue)
{
if (data.data[1] > max) max = data.data[1];
if (data.data[1] < min) min = data.data[1];
}
else
{
if (data.data[0] > max) max = data.data[0];
if (data.data[0] < min) min = data.data[0];
}
}
else
{
if (data.data[0] > max) max = data.data[0];
if (data.data[0] < min) min = data.data[0];
}
}
}
}
@@ -610,13 +621,24 @@ namespace XCharts
var serie = ss.Value[i];
if (serie.axisIndex != axisIndex || !IsActive(i)) continue;
var showData = serie.GetDataList(dataZoom);
for (int j = 0; j < showData.Count; j++)
if (IsPercentStack(serie.stack, SerieType.Bar))
{
if (!_serieTotalValueForMinMax.ContainsKey(j))
_serieTotalValueForMinMax[j] = 0;
_serieTotalValueForMinMax[j] = _serieTotalValueForMinMax[j] +
(yValue ? showData[j].data[1] : showData[i].data[0]);
for (int j = 0; j < showData.Count; j++)
{
_serieTotalValueForMinMax[j] = 100;
}
}
else
{
for (int j = 0; j < showData.Count; j++)
{
if (!_serieTotalValueForMinMax.ContainsKey(j))
_serieTotalValueForMinMax[j] = 0;
_serieTotalValueForMinMax[j] = _serieTotalValueForMinMax[j] +
(yValue ? showData[j].data[1] : showData[i].data[0]);
}
}
}
float tmax = int.MinValue;
float tmin = int.MaxValue;
@@ -661,6 +683,12 @@ namespace XCharts
return false;
}
/// <summary>
/// 是否堆叠
/// </summary>
/// <param name="stackName"></param>
/// <param name="type"></param>
/// <returns></returns>
public bool IsStack(string stackName, SerieType type)
{
if (string.IsNullOrEmpty(stackName)) return false;
@@ -676,6 +704,56 @@ namespace XCharts
return false;
}
/// <summary>
/// 是否时百分比堆叠
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public bool IsPercentStack(SerieType type)
{
int count = 0;
bool isPercentStack = false;
foreach (var serie in m_Series)
{
if (serie.show && serie.type == type)
{
if (!string.IsNullOrEmpty(serie.stack))
{
count++;
if (serie.barPercentStack) isPercentStack = true;
}
if (count >= 2 && isPercentStack) return true;
}
}
return false;
}
/// <summary>
/// 是否时百分比堆叠
/// </summary>
/// <param name="stackName"></param>
/// <param name="type"></param>
/// <returns></returns>
public bool IsPercentStack(string stackName, SerieType type)
{
if (string.IsNullOrEmpty(stackName)) return false;
int count = 0;
bool isPercentStack = false;
foreach (var serie in m_Series)
{
if (serie.show && serie.type == type)
{
if (stackName.Equals(serie.stack))
{
count++;
if (serie.barPercentStack) isPercentStack = true;
}
if (count >= 2 && isPercentStack) return true;
}
}
return false;
}
/// <summary>
/// 获得堆叠系列列表
/// </summary>

View File

@@ -223,10 +223,16 @@ namespace XCharts
var content = m_Formatter.Replace("{a}", serieName);
content = content.Replace("{b}", dataName);
content = content.Replace("{c}", ChartCached.FloatToStr(dataValue));
content = content.Replace("{c:f0}", ChartCached.IntToStr((int)Mathf.Round(dataValue)));
content = content.Replace("{c:f1}", ChartCached.FloatToStr(dataValue, 1));
content = content.Replace("{c:f2}", ChartCached.FloatToStr(dataValue, 2));
if (dataTotal > 0)
{
var percent = dataValue / dataTotal * 100;
content = content.Replace("{d}", ChartCached.FloatToStr(percent, 1));
content = content.Replace("{d:f0}", ChartCached.IntToStr((int)Mathf.Round(percent)));
content = content.Replace("{d:f1}", ChartCached.FloatToStr(percent, 1));
content = content.Replace("{d:f2}", ChartCached.FloatToStr(percent, 2));
}
content = content.Replace("\\n", "\n");
content = content.Replace("<br/>", "\n");

View File

@@ -291,6 +291,7 @@ namespace XCharts
for (int j = 0; j < serie.data.Count; j++)
{
var serieData = serie.data[j];
if(j>100) break;
//if (!serie.label.show && !serieData.showIcon) continue;
var textName = s_SerieLabelObjectName + "_" + i + "_" + j + "_" + serieData.name;
var color = Color.grey;

View File

@@ -482,7 +482,8 @@ namespace XCharts
txt.transform.localPosition = GetLabelYPosition(totalWidth + (yAxis.boundaryGap ? labelWidth / 2 : 0), i, yAxisIndex, yAxis);
txt.text = yAxis.GetLabelName(coordinateHeight, i, yAxis.minValue, yAxis.maxValue, m_DataZoom);
var isPercentStack = m_Series.IsPercentStack(SerieType.Bar);
txt.text = yAxis.GetLabelName(coordinateHeight, i, yAxis.minValue, yAxis.maxValue, m_DataZoom, isPercentStack);
txt.gameObject.SetActive(yAxis.show &&
(yAxis.axisLabel.interval == 0 || i % (yAxis.axisLabel.interval + 1) == 0));
yAxis.axisLabelTextList.Add(txt);
@@ -576,7 +577,8 @@ namespace XCharts
txt.transform.localPosition = GetLabelXPosition(totalWidth + (xAxis.boundaryGap ? labelWidth : labelWidth / 2),
i, xAxisIndex, xAxis);
totalWidth += labelWidth;
txt.text = xAxis.GetLabelName(coordinateWidth, i, xAxis.minValue, xAxis.maxValue, m_DataZoom);
var isPercentStack = m_Series.IsPercentStack(SerieType.Bar);
txt.text = xAxis.GetLabelName(coordinateWidth, i, xAxis.minValue, xAxis.maxValue, m_DataZoom, isPercentStack);
txt.gameObject.SetActive(xAxis.show &&
(xAxis.axisLabel.interval == 0 || i % (xAxis.axisLabel.interval + 1) == 0));
xAxis.axisLabelTextList.Add(txt);
@@ -759,6 +761,7 @@ namespace XCharts
if (axis.IsCategory() || !axis.show) return;
int tempMinValue = 0;
int tempMaxValue = 0;
if (IsValue())
{
if (axis is XAxis)
@@ -799,7 +802,8 @@ namespace XCharts
}
}
float coordinateWidth = axis is XAxis ? this.coordinateWidth : coordinateHeight;
axis.UpdateLabelText(coordinateWidth, m_DataZoom);
var isPercentStack = m_Series.IsPercentStack(SerieType.Bar);
axis.UpdateLabelText(coordinateWidth, m_DataZoom, isPercentStack);
RefreshChart();
}
}
@@ -1347,10 +1351,12 @@ namespace XCharts
protected override void OnRefreshLabel()
{
var anyPercentStack = m_Series.IsPercentStack(SerieType.Bar);
for (int i = 0; i < m_Series.Count; i++)
{
var serie = m_Series.GetSerie(i);
var total = serie.yTotal;
var isPercentStack = m_Series.IsPercentStack(serie.stack, SerieType.Bar);
for (int j = 0; j < serie.data.Count; j++)
{
if (j >= serie.dataPoints.Count) break;
@@ -1368,7 +1374,16 @@ namespace XCharts
serieData.data.Count - 1;
}
value = serieData.data[dimension];
var content = serie.label.GetFormatterContent(serie.name, serieData.name, value, total);
var content = "";
if (anyPercentStack && isPercentStack)
{
var tempTotal = GetSameStackTotalValue(serie.stack,j);
content = serie.label.GetFormatterContent(serie.name, serieData.name, value, tempTotal);
}
else
{
content = serie.label.GetFormatterContent(serie.name, serieData.name, value, total);
}
serieData.SetLabelActive(value != 0);
serieData.SetLabelPosition(serie.label.offset);
if (serieData.SetLabelText(content)) RefreshChart();

View File

@@ -35,6 +35,7 @@ namespace XCharts
seriesHig.Add(0);
}
}
var isPercentStack = m_Series.IsPercentStack(serie.stack, SerieType.Bar);
for (int i = serie.minShow; i < maxCount; i++)
{
if (i >= seriesHig.Count)
@@ -45,9 +46,22 @@ namespace XCharts
float pX = seriesHig[i] + coordinateX + xAxis.zeroXOffset + yAxis.axisLine.width;
float pY = coordinateY + +i * categoryWidth;
if (!yAxis.boundaryGap) pY -= categoryWidth / 2;
float barHig = (xAxis.minValue > 0 ? value - xAxis.minValue : value)
/ (xAxis.maxValue - xAxis.minValue) * coordinateWidth;
seriesHig[i] += barHig;
var barHig = 0f;
var valueTotal = 0f;
if (isPercentStack)
{
valueTotal = GetSameStackTotalValue(serie.stack, i);
barHig = value / valueTotal * coordinateWidth;
seriesHig[i] += barHig;
}
else
{
valueTotal = xAxis.maxValue - xAxis.minValue;
barHig = (xAxis.minValue > 0 ? value - xAxis.minValue : value)
/ valueTotal * coordinateWidth;
seriesHig[i] += barHig;
}
float currHig = CheckAnimation(serie, i, barHig);
@@ -119,6 +133,7 @@ namespace XCharts
seriesHig.Add(0);
}
}
var isPercentStack = m_Series.IsPercentStack(serie.stack, SerieType.Bar);
for (int i = serie.minShow; i < maxCount; i++)
{
if (i >= seriesHig.Count)
@@ -130,9 +145,22 @@ namespace XCharts
float zeroY = coordinateY + yAxis.zeroYOffset;
if (!xAxis.boundaryGap) pX -= categoryWidth / 2;
float pY = seriesHig[i] + zeroY + xAxis.axisLine.width;
float barHig = (yAxis.minValue > 0 ? value - yAxis.minValue : value)
/ (yAxis.maxValue - yAxis.minValue) * coordinateHeight;
seriesHig[i] += barHig;
var barHig = 0f;
var valueTotal = 0f;
if (isPercentStack)
{
valueTotal = GetSameStackTotalValue(serie.stack, i);
barHig = value / valueTotal * coordinateHeight;
seriesHig[i] += barHig;
}
else
{
valueTotal = yAxis.maxValue - yAxis.minValue;
barHig = (yAxis.minValue > 0 ? value - yAxis.minValue : value)
/ valueTotal * coordinateHeight;
seriesHig[i] += barHig;
}
float currHig = CheckAnimation(serie, i, barHig);
@@ -175,6 +203,23 @@ namespace XCharts
return gap;
}
private float GetSameStackTotalValue(string stack, int dataIndex)
{
if (string.IsNullOrEmpty(stack)) return 0;
float total = 0;
foreach (var serie in m_Series.list)
{
if (serie.type == SerieType.Bar)
{
if (stack.Equals(serie.stack))
{
total += serie.data[dataIndex].data[1];
}
}
}
return total;
}
private HashSet<string> barStackSet = new HashSet<string>();
private float GetBarTotalWidth(float categoryWidth, float gap)

View File

@@ -350,6 +350,11 @@
* `DashDot`:点划线。
* `DashDotDot`:双点划线。
* `lineStyle`:线条样式 [LineStyle](#LineStyle)。
* `barType`:柱状图类型。以下几种类型:
* `Normal`:普通柱状图。
* `ZebraLine`:斑马柱状图。
* `Capsule`:胶囊柱状图。
* `barPercentStack`:是否百分比堆叠柱状图,相同 `stack``serie` 只要有一个 `barPercentStack``true`,则就显示成百分比堆叠柱状图。
* `barWidth`:柱条的宽度,不设时自适应。支持设置成相对于类目宽度的百分比。
* `barGap`:不同系列的柱间距离。为百分比(如 `'0.3f'`,表示柱子宽度的 `30%`)。如果想要两个系列的柱子重叠,可以设置 `barGap``'-1f'`。这在用柱子做背景的时候有用。在同一坐标系上,此属性会被多个 `'bar'` 系列共享。此属性应设置于此坐标系中最后一个 `'bar'` 系列上才会生效,并且是对此坐标系中所有 `'bar'` 系列生效。
* `barCategoryGap`同一系列的柱间距离默认为类目间距的20%,可设固定值。在同一坐标系上,此属性会被多个 `'bar'` 系列共享。此属性应设置于此坐标系中最后一个 `'bar'` 系列上才会生效,并且是对此坐标系中所有 `'bar'` 系列生效。
@@ -555,7 +560,7 @@
* `Center`:在中心位置(折线图,柱状图,饼图)。
* `Top`:顶部(柱状图)。
* `Bottom`:底部(柱状图)。
* `formatter`:标签内容字符串模版格式器。支持用 `\n` 换行。模板变量有:`{a}`:系列名;`{b}`:数据名;`{c}`:数据值;`{d}`:百分比。示例:`{b}:{c}`
* `formatter`:标签内容字符串模版格式器。支持用 `\n` 换行。模板变量有:`{a}`:系列名;`{b}`:数据名;`{c}`:数据值;`{d}`:百分比。示例:`{b}:{c:f1}`
* `offset`:距离图形元素的偏移。
* `color`:自定义文字颜色,默认和系列的颜色一致。
* `backgroundColor`:标签的背景色,默认无颜色。

View File

@@ -28,6 +28,7 @@ QQ交流群XCharts交流群202030963
## 更新日志
* 2019.10.16)增加`Serie``barPercentStack`参数,可配置`百分比堆叠柱状图`
* 2019.10.16)增加`Demo`首页`LineChart`的代码动态控制效果
* 2019.10.15)移除`Pie`组件,相关参数放到`Settings`中配置
* 2019.10.15)增加`Demo`首页,展示代码动态控制效果