效果
介绍
实现折线图的大体思路和数学画图一致:先画底部刻度(底部描述),再画点,再连线。
关键字
1. 对象池
2. 迭代
3. DoTween 动画
分析及代码
定义绘图数据的结构GraphData,包含数据的描述信息_desc,数据的值_value。在需要绘图时传递一组GraphData的类型的数据给此模块就可以实现折线图的绘制。
[System.Serializable]
public struct GraphData
{
public string _desc;
[Range(0, 100)]
public float _value;
public float Rate
{
get
{
return _value / 100;
}
}
public GraphData(string desc, float value)
{
_desc = desc;
_value = value;
}
}
下图是Unity的布局,为了方便管理,在LineGraphPanel中分别设置了LeftSide(左侧刻度),DescContent(底部描述),DotContent(点),LineContent(线)四个空对象存放相应的子元素。
public GraphData[] _datas;//数据
public float _lineWidth = 3;//线宽
public float _dotRadius = 2;//点半径
public Transform _leftSide;//左侧描述
public RectTransform _descContent;//描述Content
public RectTransform _dotContent;//点Content
public RectTransform _lineContent;//线Content
public Text _descPrefab;//描述Prefab
public RectTransform _dotPrefab;//点Prefab
public Image _linePrefab;//线Prefab
public float _tweenTime = 1f;//动画时间
//描述、点、线 管理
private Text[] _descs;
private RectTransform[] _dots;
private Image[] _lines;
因为背景、左侧刻度是不会改变的,所以初始化左侧刻度的方法只需要在Start方法中调用一次就可以(背景我直接用的一张10条横线的图片)。
/// <summary>
/// 初始化折线图
/// </summary>
public void InitLineGraph(GraphData[] data)
{
//leftSide
for (int i = 0; i < _leftSide.childCount; i++)
{
_leftSide.GetChild(i).GetComponent<Text>().text = (100 - i * 10).ToString();
}
}
底部描述使用了Unity的自动布局组件,因为在下一步需要绘制时是需要用到这里的位置信息,所以需要在结尾处刷新UI的布局。其次,为了防止动态的刷新数据过于频繁,使用到了对象池的优化(链接)。
/// <summary>
/// 底部描述
/// </summary>
private void DrawDesc()
{
_descs = new Text[_datas.Length];
for (int i = 0; i < _datas.Length; i++)
{
Text desc = ObjectPool.Instance.GetObject(_descPrefab.name, _descContent).GetComponent<Text>();
desc.text = _datas[i]._desc;
desc.transform.SetAsLastSibling();//使用对象池和自动布局组件会调乱顺序,要重置
desc.gameObject.SetActive(true);
_descs[i]