当前位置:网站首页>Unity_折线图+UI动画
Unity_折线图+UI动画
2022-08-08 06:26:00 【GREAT1217】
效果
介绍
实现折线图的大体思路和数学画图一致:先画底部刻度(底部描述),再画点,再连线。
关键字
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] = desc;
}
LayoutRebuilder.ForceRebuildLayoutImmediate(_descContent);//使用自动布局组件要刷新UI,刷新位置
}
根据底部描述的位置、数据的值计算点的位置
/// <summary>
/// 画点
/// </summary>
private void DrawDot()
{
float height = _dotContent.rect.height;
_dots = new RectTransform[_datas.Length];
for (int i = 0; i < _datas.Length; i++)
{
RectTransform dot = ObjectPool.Instance.GetObject(_dotPrefab.name, _dotContent).GetComponent<RectTransform>();
dot.localPosition = new Vector3(_descs[i].transform.localPosition.x, height * (_datas[i].Rate - 0.5f), 0);//锚点在中心
dot.sizeDelta = Vector2.one * _lineWidth * 2;
dot.gameObject.SetActive(true);
_dots[i] = dot;
}
}
根据上一步的点生成线段,线段我使用的是Image,所以可以随意指定它的宽度,位置、旋转根据相邻的两点计算。这里为了实现动画,使用了DoTween和递归,在每生成完一条线后接着生成下一条。
/// <summary>
/// 画线
/// </summary>
private void DrawLines()
{
_lines = new Image[_datas.Length - 1];
DrawLine();
}
/// <summary>
/// 画线
/// </summary>
/// <param name="index"></param>
private void DrawLine(int index = 0)
{
if (index >= _lines.Length) return;
Vector2 curPos = _dots[index].localPosition;
Vector2 nextPos = _dots[index + 1].localPosition;
float length = Vector2.Distance(curPos, nextPos);
Vector3 dir = curPos - nextPos;
float angle = Vector3.Angle(Vector3.up, dir);
Vector2 center = (curPos + nextPos) / 2;
Image line = ObjectPool.Instance.GetObject(_linePrefab.name, _lineContent).GetComponent<Image>();
line.rectTransform.localEulerAngles = Vector3.forward * angle;
line.rectTransform.localPosition = center;
line.rectTransform.sizeDelta = new Vector2(_lineWidth, length);
line.gameObject.SetActive(true);
line.fillAmount = 0;
line.fillOrigin = dir.x > 0 ? 0 : 1;
line.DOFillAmount(1, _tweenTime / _lines.Length).OnComplete(() => DrawLine(index + 1));
_lines[index] = line;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
[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;
}
}
/// <summary>
/// 折线图
/// </summary>
public class UILineGraphManager : MonoBehaviour
{
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;
private void Awake()
{
ObjectPool.Instance.SetPrefab(_descPrefab.gameObject);
ObjectPool.Instance.SetPrefab(_dotPrefab.gameObject);
ObjectPool.Instance.SetPrefab(_linePrefab.gameObject);
}
/// <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();
}
RefeshLineGraph(data);
}
/// <summary>
/// 刷新折线图
/// </summary>
public void RefeshLineGraph(GraphData[] data)
{
_datas = data;
ClearTransform(_descContent);
ClearTransform(_dotContent);
ClearTransform(_lineContent);
DrawDesc();
DrawDot();
DrawLines();
}
/// <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] = desc;
}
LayoutRebuilder.ForceRebuildLayoutImmediate(_descContent);//使用自动布局组件要刷新UI,刷新位置
}
/// <summary>
/// 画点
/// </summary>
private void DrawDot()
{
float height = _dotContent.rect.height;
_dots = new RectTransform[_datas.Length];
for (int i = 0; i < _datas.Length; i++)
{
RectTransform dot = ObjectPool.Instance.GetObject(_dotPrefab.name, _dotContent).GetComponent<RectTransform>();
dot.localPosition = new Vector3(_descs[i].transform.localPosition.x, height * (_datas[i].Rate - 0.5f), 0);//锚点在中心
dot.sizeDelta = Vector2.one * _lineWidth * 2;
dot.gameObject.SetActive(true);
_dots[i] = dot;
}
}
/// <summary>
/// 画线
/// </summary>
private void DrawLines()
{
_lines = new Image[_datas.Length - 1];
DrawLine();
}
/// <summary>
/// 画线
/// </summary>
/// <param name="index"></param>
private void DrawLine(int index = 0)
{
if (index >= _lines.Length) return;
Vector2 curPos = _dots[index].localPosition;
Vector2 nextPos = _dots[index + 1].localPosition;
float length = Vector2.Distance(curPos, nextPos);
Vector3 dir = curPos - nextPos;
float angle = Vector3.Angle(Vector3.up, dir);
Vector2 center = (curPos + nextPos) / 2;
Image line = ObjectPool.Instance.GetObject(_linePrefab.name, _lineContent).GetComponent<Image>();
line.rectTransform.localEulerAngles = Vector3.forward * angle;
line.rectTransform.localPosition = center;
line.rectTransform.sizeDelta = new Vector2(_lineWidth, length);
line.gameObject.SetActive(true);
line.fillAmount = 0;
line.fillOrigin = dir.x > 0 ? 0 : 1;
line.DOFillAmount(1, _tweenTime / _lines.Length).OnComplete(() => DrawLine(index + 1));
_lines[index] = line;
}
/// <summary>
/// 入池
/// </summary>
/// <param name="parent"></param>
private void ClearTransform(Transform parent)
{
for (int i = 1; i < parent.childCount; i++)
{
ObjectPool.Instance.RecycleObj(parent.GetChild(i).gameObject, parent);
}
}
}
初始化时调用InitLineGraph(),刷新数据时调用RefeshLineGraph()。下面是示例:
using UnityEngine;
public class UIGraphManager : MonoBehaviour
{
public UILineGraphManager _line;
private GraphData[] _datas;
private void Start()
{
RefeshData();
_line.InitLineGraph(_datas);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
RefeshData();
_line.RefeshLineGraph(_datas);
}
}
/// <summary>
/// 刷新数据
/// </summary>
public void RefeshData()
{
_datas = new GraphData[8];
for (int i = 0; i < _datas.Length; i++)
{
_datas[i] = new GraphData("数据" + i, Random.Range(0, 100));
}
}
}
边栏推荐
- 【图形学】四元数(二)
- PyTorch向量变换基本操作
- 【EagleEye】2020-ECCV-EagleEye: Fast Sub-net Evaluation for Efficient Neural Network Pruning-论文详解
- P22 美颜相机——引入动态数组,优化重绘
- 基于深度学习的关系抽取
- RNN神经网络适用于什么,RNN神经网络基本原理
- 中序表达式转为后序表达式
- Double week leetcode 84th game
- Stack queue OJ question sharing and explanation
- 《Filter Pruning using Hierarchical Group Sparse》ICPR2020论文详解
猜你喜欢
CSDN21天学习挑战赛之顺序查找
NVIDIA CUDA Highly Parallel Processor Programming (8): Parallel Mode: Histogram Calculation
P17 五子棋的实现4 悔棋功能
P20 美颜相机的实现——基础铺垫2
C语言判断大小端存储问题
P23 传值和传引用
NVIDIA CUDA Highly Parallel Processor Programming (VII): Parallel Mode: Prefix and
jupyter notebook添加目录
Unity_条形图(柱状图)+ UI动画
线性回归---Fit A Line
随机推荐
acwing 63rd weekly match【2022.08.06】
NVIDIA CUDA 高度并行处理器编程(七):并行模式:前缀和
关系抽取论文阅读笔记
HDU 6029 个人分析
bp神经网络预测模型原理,神经网络模型怎么预测
人工神经网络的工作原理,神经网络的基本原理
Unity—ParticleSystem(粒子系统)与Animator(动画状态机)批量管理器
Accelerate CNNs from Three Dimensions: A Comprehensive Pruning Framework详解
acwing 第63场周赛【2022.08.06】
神经网络预测结果分析,神经网络怎么预测数据
Vivado安装—Xilinx design tool already exists for 2019.1,specify a different program program group entr
Unity3D objects up and down or so rotation (is not affected by axes object itself)
C语言实现冒泡排序及对冒泡排序的优化处理
文件IO实现图片的加密操作
Unity HDRP下VRTK传送、穿墙 时画面淡入淡出、视觉遮挡无法正确显示问题解决
通过使用fgets()统计文件的行号和使用fgets()/fputs()拷贝文件
TextCNN实现imdb数据集情感分类任务
神经网络二分类问题范例,神经网络解决分类问题
SCIERC语料格式解读
[总结] Unity Lightmap使用总结