找回密码
 立即注册
查看: 196|回复: 2

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

[复制链接]
发表于 2022-11-5 07:13 | 显示全部楼层 |阅读模式
今天策划提了个这么个小需求,如视频所示:


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
{
    [SerializeField] private ScrollRect parentScrollView;
    [SerializeField] 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);
            }
        }
    }
}

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

如果喜欢请点赞 + 关注哦

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2022-11-5 07:13 | 显示全部楼层
答主,那种顶层比较特殊(塔顶)的下面做成无限循环的列表(塔身)而且还能固定卡位(底部不截断ui)那种思路和这个一样吗[蹲]
发表于 2022-11-5 07:23 | 显示全部楼层
抱歉,没有懂你说的是什么效果,但循环列表基本都是用的scrollview,如果数据比较多的情况下除了用scrollview还得对其做个优化(content下的item能复用的那种),要不内存太大,节点太多
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-15 15:37 , Processed in 0.115915 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表