RecursiveFrog 发表于 2022-11-5 07:13

Unity 一个特殊的滑动效果!!!

今天策划提了个这么个小需求,如视频所示:


https://www.zhihu.com/video/1571107063538589696

如果你看到这个需求会想到怎么做呢?

首先我们分析这个效果需要考虑哪些东西呢
1、判断位置(决定标题栏是否能卡到顶部位置不动)
2、丝滑程度,向上滑动的时候标题卡住,下面的滑动列表自己滚动,向下滑动的时候下面滑动列表要滑动到顶部,然后紧接着title栏及上方栏向下滑

那知道了需求怎么做呢?我首先想到的是用2个scrollview来做,底层的叫parentScrollView,顶层的叫childScrollView.
因为滑动本来就是scrollview的职能,如果自己写没必要。

那既然决定怎么弄了那接下来就要看怎么实现了?
根据上边我们分析的我们首先要判断位置来决定两个scrollview何时停止,何时滑动,所以需要我添加了两个接口

private bool IsReachTop(ScrollRect scrollView)
{
    return scrollView.content.localPosition.y <= 4; //数值4只是经验值,如果是0会导致滑动的时候不丝滑
}

private bool IsReachBottom(ScrollRect scrollView)
{
    var content = scrollView.content;
    return content.sizeDelta.y - content.anchoredPosition.y < scrollView.viewport.rect.height + 5; //数值5只是经验值,如果是0会导致滑动的时候不丝滑
}
现在知道了怎么判定位置,那怎么让title正好卡在顶部呢?
很简单,我我们根据屏幕高,上下边缘的高就能算出title卡在顶部那个时刻的高。那这个高就是我们子scrollview的高,然后根据子scrollview的高倒推出父scrollview Content的高。

private float GetDefaultHeight()
{
    var rootHeight = transform.GetComponent<RectTransform>().rect.height;
    var scrollViewHeight = rootHeight - 100 - 111; //scrollView高度设置为 界面高- 下边缘高-上边缘高
    return scrollViewHeight;
}

private void SetScrollViewHeight(float height)
{
    var rectTrans = childScrollView.transform.GetComponent<RectTransform>();
    rectTrans.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);

    var bgRect = childScrollView.transform.Find("BgRight3").GetComponent<RectTransform>();
    bgRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);
    parentScrollView.content.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height + 291);// 子scrollview 高 + rightTop高 + 子title高
}
那现在高度设置好了,位置能判定了,接下来就是如何滑动的事了,我们实现几个接口类 IDragHandler, IBeginDragHandler, IEndDragHandler,IScrollHandler
然后根据这几个接口控制父scrollview和子scrollview的滑动。不多解释,直接上代码,看了就明白。
public class MultiScrollViewProxy : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler,IScrollHandler
{
    private ScrollRect parentScrollView;
    private ScrollRect childScrollView;

    private bool isDraggingParent = false;
    private bool isDraggingChild = false;
    private Vector2 pointerStartLocalCursor = Vector2.zero;
    private bool isDragging = false;

    private void OnDisable()
    {
      isDragging = false;
      isDraggingParent = false;
      isDraggingChild = false;
    }

    private bool IsReachTop(ScrollRect scrollView)
    {
      return scrollView.content.localPosition.y <= 4;
    }

    private bool IsReachBottom(ScrollRect scrollView)
    {
      var content = scrollView.content;
      return content.sizeDelta.y - content.anchoredPosition.y < scrollView.viewport.rect.height + 5;
    }

      
    public void OnDrag(PointerEventData eventData)
    {
      if (eventData.button != PointerEventData.InputButton.Left)
            return;
      
      Vector2 localCursor;
      if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(parentScrollView.viewport, eventData.position, eventData.pressEventCamera, out localCursor))
            return;

      var deltaPos = localCursor - pointerStartLocalCursor;
      
      if (!isDraggingParent && !isDraggingChild)
      {
            if (deltaPos.y > 0)//向上滑动
            {
                if (IsReachBottom(parentScrollView))
                {
                  childScrollView.OnBeginDrag(eventData);
                  isDraggingChild = true;
                }
                else
                {
                  parentScrollView.OnBeginDrag(eventData);
                  isDraggingParent = true;
                }
            }
            else if (deltaPos.y < 0)//向下滑动
            {
                if (IsReachTop(childScrollView))
                {
                  parentScrollView.OnBeginDrag(eventData);
                  isDraggingParent = true;
                }
                else
                {
                  childScrollView.OnBeginDrag(eventData);
                  isDraggingChild = true;
                }
            }
      }

      if (isDraggingParent)
      {
            parentScrollView.OnDrag(eventData);
            if (deltaPos.y > 0)
            {
                if(IsReachBottom(parentScrollView))
                {
                  isDraggingParent = false;
                  isDraggingChild = true;
                  parentScrollView.OnEndDrag(eventData);
                  childScrollView.OnBeginDrag(eventData);
                }
            }
            
      }

      if (isDraggingChild)
      {
            childScrollView.OnDrag(eventData);
            if (deltaPos.y < 0)
            {
                if (IsReachTop(childScrollView))
                {
                  isDraggingChild = false;
                  isDraggingParent = true;
                  childScrollView.OnEndDrag(eventData);
                  parentScrollView.OnBeginDrag(eventData);
                }
            }
      }
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
      if (eventData.button != PointerEventData.InputButton.Left)
            return;
      
      pointerStartLocalCursor = Vector2.zero;
      RectTransformUtility.ScreenPointToLocalPointInRectangle(parentScrollView.viewport, eventData.position, eventData.pressEventCamera, out pointerStartLocalCursor);
      isDragging = true;
      
      
    }

    public void OnEndDrag(PointerEventData eventData)
    {
      if (eventData.button != PointerEventData.InputButton.Left)
            return;
      isDragging = false;
      isDraggingParent = false;
      isDraggingChild = false;
      parentScrollView.OnEndDrag(eventData);
      childScrollView.OnEndDrag(eventData);
    }

    public void OnScroll(PointerEventData eventData)
    {
      if (!this.enabled)
            return;
      Vector2 delta = eventData.scrollDelta;
      
      if (delta.y > 0)//向下滚动
      {
            if (IsReachTop(childScrollView))
            {
                parentScrollView.OnScroll(eventData);
            }
            else
            {
                childScrollView.OnScroll(eventData);
            }
      }
      else if (delta.y < 0)//向上滚动
      {
            if (IsReachBottom(parentScrollView))
            {
                childScrollView.OnScroll(eventData);
            }
            else
            {
                parentScrollView.OnScroll(eventData);
            }
      }
    }
}

到此,我就分享完了这个简单的效果,虽然简单,但是细节还是挺多的。

如果喜欢请点赞 + 关注哦

kirin77 发表于 2022-11-5 07:13

答主,那种顶层比较特殊(塔顶)的下面做成无限循环的列表(塔身)而且还能固定卡位(底部不截断ui)那种思路和这个一样吗[蹲]

NoiseFloor 发表于 2022-11-5 07:23

抱歉,没有懂你说的是什么效果,但循环列表基本都是用的scrollview,如果数据比较多的情况下除了用scrollview还得对其做个优化(content下的item能复用的那种),要不内存太大,节点太多
页: [1]
查看完整版本: Unity 一个特殊的滑动效果!!!