有时我们要实现一些比较特殊的滑动条效果,如下所示:
就是Slider去掉了Handle,然后Fill在最低的时候要限制在某个范围以上。颜色的变化忽略。
这样的实现目前我知道的总共有两种
- 更改UI的布局格式
- 写个脚本限制最低范围并重新映射范围,这种需要将滑动值的监听放在该脚本上
我自己实现的是第二种,先一个个看下
更改UI的布局格式
Slider控制的是Fill这个物体的Anchor的区域是否完全覆盖FillArea,所以当在Slider的value为0的时候,把anchor调节成如图所示,后面调节slider,当Value为0时,就不会fill完全消失了。这样做的话,在value为1的时候,图中的圆圈的上半部分是超出FillArea的。
这个的原理是,
在value不为0时,fill的RectTransform信息是这样的
Top代表了anchor所围成的矩形的顶部与这个UI物体本身的面积矩形的顶部的差值,是不会变的,其他的Left Right都是一样的。
而在保持这种不变的情况下,调节Anchors的区域就变相调节了Fill的大小,但是不管怎样这个距离的差值都在的。
但是在value为0的时候,fill的recttransform的信息是
但因为slider调节的是anchor的区域,所以这时的RectTransform情况并不用于参考,参考的还是上上图的情况。
重新映射范围
简单来说是,在检测到slider的value达到最低值时,限制为最低值然后将限制的最低值和1之间的区域映射为0-1的区域,并传给代理。缺点是原本对slider的值监听需要改为对此脚本的值监听。
按照映射的需求,在限定最低值时认为是0,在限定最高值是认为是1,这中间是线性变化的过程。那么现在是将最低值到1的这个过程看成是0-1的变化范围了。
所以原理是,将代码中限制的最低值比如0.15到默认最高值1之间的范围映射到(0-1),那么滑动条在这里面的值x就要以这个最低值为零点,先算出相对的偏移(x - 0.15),然后这个偏移值从(1 - 0.15)的范围映射到(1-0)的范围,即乘以
(1 - 0) / (1 - 0.15)这个比值。
乘以
public class SettingsSlideMinControl : MonoBehaviour
{
[SerializeField]
private Slider _slider;
[SerializeField, Range(0,1)]
private float minValue;
private UnityAction<float> onReachMin;
private UnityAction<float> onOutofMin;
private UnityAction<float> onDealedSlide;
private void Awake()
{
}
// Start is called before the first frame update
void Start()
{
_slider.onValueChanged.AddListener(OnSlide);
}
void OnReachMin(float value)
{
onReachMin?.Invoke(value);
}
void OnOutOfMin(float value)
{
onOutofMin?.Invoke(value);
}
public void SetOnReachMin(UnityAction<float> onReachMin)
{
this.onReachMin += onReachMin;
}
public void RemoveOnReachMin(UnityAction<float> onReachMin)
{
this.onReachMin -= onReachMin;
}
public void SetOnOutOfMin(UnityAction<float> onOutofMin)
{
this.onOutofMin += onOutofMin;
}
public void RemoveOnOutOfMin(UnityAction<float> onOutofMin)
{
this.onOutofMin -= onOutofMin;
}
public void AddOnSlideListener(UnityAction<float> onDealedSlide)
{
this.onDealedSlide += onDealedSlide;
}
public void RemoveOnSlide(UnityAction<float> onDealedSlide)
{
this.onDealedSlide -= onDealedSlide;
}
public void SetSlideValueWithoutNotify(float value)
{
float mappedValue = ValueMap(value);
_slider.SetValueWithoutNotify(mappedValue);
}
/// <summary>
/// 将0-1的值映射到缩短后的值
/// </summary>
/// <param name="originValue"></param>
/// <returns></returns>
public float ValueMap(float originValue)
{
float mappedValue = minValue + originValue * (1 - minValue) / (1 - 0);
return mappedValue;
}
/// <summary>
/// 将缩短后的值映射为0-1的值
/// </summary>
/// <param name="originValue"></param>
/// <returns></returns>
public float ValueReversemap(float originValue)
{
float mappedValue = (originValue - minValue) * (1 - 0) / (1 - minValue) ;
return mappedValue;
}
private bool isCurrentReachMin;
// Update is called once per frame
void OnSlide(float value)
{
if (value < minValue)
{
// _slider.SetValueWithoutNotify(minValue);
_slider.value = minValue;
OnReachMin(minValue);
isCurrentReachMin = true;
onDealedSlide(0);
// print(" dealedValue " + 0);
}
else
{
if (isCurrentReachMin)
{
isCurrentReachMin = false;
OnOutOfMin(value);
}
//这里因为是将调整范围后的slider的值映射为0-1范围,所以用ValueReversemap
float dealedValue = ValueReversemap(value);
// print(" dealedValue " + dealedValue);
onDealedSlide?.Invoke(dealedValue);
}
}
}