Unity 按钮控制ScrowView的脚本实现



using System;
/**
*Copyright(C) 2019 by #COMPANY#
*All rights reserved.
*FileName:     #SCRIPTFULLNAME#
*Author:       #AUTHOR#
*Version:      #VERSION#
*UnityVersion:#UNITYVERSION#
*Date:         #DATE#
*Description:   
*History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;

public enum ESVMoveType
{
    Horizontal = 0,
    Vertical
}

public class SVMove
{
    /// <summary>
    /// the firstStart do not Use HeadCallBack
    /// </summary>
    public Action correctionHeadCallBack;
    public Action correctionTailCallBack;
    /// <summary>
    /// the time with once move
    /// </summary>
    public float duration = 1f;
    Vector2 disNormalizedVe2;
    ScrollRect sv;
    bool isTail;

    ESVMoveType svType;
    /// <summary>
    /// set SV with scrowNum and type
    /// </summary>
    /// <param name="sv"></param>
    /// <param name="scrowNum">it can control moveNum with once move</param>
    /// <param name="type"></param>
    public SVMove(Transform sv, int scrowNum = 1, ESVMoveType type = ESVMoveType.Vertical)
    {
        this.sv = sv.GetComponent<ScrollRect>();
        svType = type;
        GetDistance(scrowNum);
    }

    /// <summary>
    /// Get the Distance with ScrowNum and Orientation
    /// </summary>
    /// <param name="scrowNum"></param>
    /// <param name="org"></param>
    public void GetDistance(int scrowNum)
    {
        var grid = sv.content.GetComponent<GridLayoutGroup>();
        switch (svType)
        {
            // scorwNum*one move long/contentLong(i can't get it,so i have to calculation)-svLong-gridDistance
            case ESVMoveType.Horizontal:
                float childWidth = grid.cellSize.x;
                float maxContentWidth = (childWidth + grid.spacing.x) * grid.transform.childCount - grid.padding.top - sv.GetComponent<RectTransform>().rect.width;
                float disWidth = (childWidth + grid.spacing.x) * scrowNum / maxContentWidth;
                disNormalizedVe2 = disWidth * Vector2.right;
                break;
            case ESVMoveType.Vertical:
                float childHeight = grid.cellSize.y;
                float maxContentHeight = (childHeight + grid.spacing.y) * grid.transform.childCount - grid.padding.left - sv.GetComponent<RectTransform>().rect.height;
                float disHeight = (childHeight + grid.spacing.y) * scrowNum / maxContentHeight;
                disNormalizedVe2 = disHeight * Vector2.up;
                break;
        }
    }

    /// <summary>
    /// Move Active Response, it always use one btn to contro
    /// </summary>
    public void Move()
    {
        float svNormalizedLong = GetSVNormalizedLong();

        if (svNormalizedLong <= 0)
            isTail = false;
        if (svNormalizedLong >= 1)
            isTail = true;

        if (isTail)
            DoForwardMove();
        else
            DoBackwordMove();
    }

    /// <summary>
    /// Forward move
    /// </summary>
    public void DoForwardMove()
    {
        Vector2 willPos = sv.normalizedPosition - disNormalizedVe2;
        Vector2 correctPos = MovieRangeLimit(willPos);

        float correctDur = GetDuarationLimitWithDis(willPos, correctPos);
        DOTween.To(() => sv.normalizedPosition, x => sv.normalizedPosition = x,
            correctPos, correctDur).OnComplete(() =>
             {
                 Debug.Log(GetSVNormalizedLong());
                 //Correction the tail with End
                 if (GetSVNormalizedLong() <= 0.01)
                 {
                     if (correctionTailCallBack != null)
                         correctionTailCallBack();

                     isTail = false;
                 }
             });
    }

    /// <summary>
    /// Backword Move
    /// </summary>
    public void DoBackwordMove()
    {
        Vector2 willPos = sv.normalizedPosition + disNormalizedVe2;
        Vector2 correctPos = MovieRangeLimit(willPos);
        float correctDur = GetDuarationLimitWithDis(willPos, correctPos);

        DOTween.To(() => sv.normalizedPosition, x => sv.normalizedPosition = x,
            correctPos, correctDur).OnComplete(() =>
       {
           Debug.Log(GetSVNormalizedLong());
           //Correction the head  with Start
           if (GetSVNormalizedLong() >= 0.99f)
           {
               if (correctionHeadCallBack != null)
                   correctionHeadCallBack();

               isTail = true;
           }
       });
    }

    /// <summary>
    /// chang the Orientation if you want  Backword start 
    /// </summary>
    public void ChangeOrientation()
    {
        switch (svType)
        {
            case ESVMoveType.Horizontal:
                disNormalizedVe2.x *= -1;
                break;
            case ESVMoveType.Vertical:
                disNormalizedVe2.y *= -1;
                break;
        }
    }
    /// <summary>
    /// get the SVNormalizedLong with the type
    /// </summary>
    /// <param name="norLong"></param>
    public float GetSVNormalizedLong()
    {
        switch (svType)
        {
            case ESVMoveType.Horizontal:
                return sv.normalizedPosition.x;

            case ESVMoveType.Vertical:
                return sv.normalizedPosition.y;
        }
        return 0;
    }

    //get WillPos limit with movie
    public Vector2 MovieRangeLimit(Vector2 willPos)
    {
        switch (svType)
        {
            case ESVMoveType.Horizontal:
                if (willPos.x > 1)
                    willPos.x = 1;
                else if (willPos.x < 0)
                    willPos.x = 0;
                break;
            case ESVMoveType.Vertical:
                if (willPos.y > 1)
                    willPos.y = 1;
                else if (willPos.y < 0)
                    willPos.y = 0;
                break;
        }
        return willPos;
    }

    //limitd the Duaration with WillPos and CorrectPos,it can let movie become more smooth
    public float GetDuarationLimitWithDis(Vector2 willPos, Vector2 correctPos)
    {
        //if they are equal don't change
        if (willPos == correctPos)
            return duration;

        //get the perfect duaration,DuarationPos/beforePos,but you should to know when duaration was less than zero we will add it -1,
        //at the first time i want use Mathf.Abs(), but they can offset the negative 
        switch (svType)
        {
            case ESVMoveType.Horizontal:
                return (willPos.x - correctPos.x) / (willPos.x<0?willPos.x-1: willPos.x) * duration;
            case ESVMoveType.Vertical:
                return (willPos.y - correctPos.y) / (willPos.y < 0 ? willPos.y - 1 : willPos.y) * duration;
        }
        return duration;
    }
}

GIF.gif

GIF.gif

这是单个按钮控制的 如果想两个按钮控制就要分别调用DoFofwordMove DoBackwordMove
脚本构造传入 ScrowView 和滚动幅度 还有横向移动还是竖向移动就可以了

然后我把旧的改了一下差值算法 现在的这个就是新的 这样传入的num就是要滑动的长度
然后我又优化了下差值 不会那么就是有回弹效果 ,然后最后几个移动距离如果短的话时间也会跟着一起缩短 现在看得自然了点


GIF.gif

然后是又添加上如果动态改变数量的话 每次移动速率不会变

using System;
/**
*Copyright(C) 2019 by #COMPANY#
*All rights reserved.
*FileName:     #SCRIPTFULLNAME#
*Author:       #AUTHOR#
*Version:      #VERSION#
*UnityVersion:#UNITYVERSION#
*Date:         #DATE#
*Description:   
*History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;

public enum ESVMoveType
{
    Horizontal = 0,
    Vertical
}

public class SVMove
{
    /// <summary>
    /// the firstStart do not Use
    /// </summary>
    public Action correctionHeadCallBack;
    public Action correctionTailCallBack;
    public float duration = 1f;
    Vector2 disNormalizedVe2;
    ScrollRect sv;
    GridLayoutGroup grid;
    bool isTail;
    float scorwPercentage;
    int childcount;

    ESVMoveType svType;
    /// <summary>
    /// Set the SV with Btn and Org 
    /// </summary>
    /// <param name="sv">ScrowView</param>
    /// <param name="btn">ControBtn</param>
    /// <param name="org">Orientation</param>
    public SVMove(Transform sv, int scrowNum = 1, ESVMoveType type = ESVMoveType.Vertical)
    {
        this.sv = sv.GetComponent<ScrollRect>();
        grid = this.sv.content.GetComponent<GridLayoutGroup>();
        svType = type;
        childcount = grid.transform.childCount;
        scorwPercentage = (float)scrowNum / childcount;
        GetDistance(childcount);
    }

    /// <summary>
    /// Move Active Response
    /// </summary>
    public void Move()
    {
        float svNormalizedLong = GetSVNormalizedLong();

        if (svNormalizedLong <= 0)
            isTail = false;
        if (svNormalizedLong >= 1)
            isTail = true;

        if (isTail)
            DoForwardMove();
        else
            DoBackwordMove();
    }

    /// <summary>
    /// Forward move
    /// </summary>
    public void DoForwardMove()
    {
        ChangeDisWithChildcount();
        Vector2 willPos = sv.normalizedPosition - disNormalizedVe2;
        Vector2 correctPos = MovieRangeLimit(willPos);
        float correctDur = GetDuarationLimitWithDis(willPos, correctPos);

        DOTween.To(() => sv.normalizedPosition, x => sv.normalizedPosition = x,
            correctPos, correctDur).OnComplete(() =>
            {
                //Correction the tail with End
                if (GetSVNormalizedLong() <= 0.01)
                {
                    if (correctionTailCallBack != null)
                        correctionTailCallBack();

                    isTail = false;
                }
            });
    }

    /// <summary>
    /// Backword Move
    /// </summary>
    public void DoBackwordMove()
    {
        ChangeDisWithChildcount();
        Vector2 willPos = sv.normalizedPosition + disNormalizedVe2;
        Vector2 correctPos = MovieRangeLimit(willPos);
        float correctDur = GetDuarationLimitWithDis(willPos, correctPos);

        DOTween.To(() => sv.normalizedPosition, x => sv.normalizedPosition = x,
            correctPos, correctDur).OnComplete(() =>
            {
                //Correction the head  with Start
                if (GetSVNormalizedLong() >= 0.99f)
                {
                    if (correctionHeadCallBack != null)
                        correctionHeadCallBack();

                    isTail = true;
                }
            });
    }
    /// <summary>
    /// if childcount change the moveDistance will be reCalculation
    /// </summary>
    void ChangeDisWithChildcount()
    {
        if (childcount != grid.transform.childCount)
        {
            childcount = grid.transform.childCount;
            GetDistance(childcount);
        }
    }
    /// <summary>
    /// Get the Distance with ScrowNum and Orientation
    /// </summary>
    /// <param name="scrowNum"></param>
    /// <param name="org"></param>
    void GetDistance(int childcount)
    {
        switch (svType)
        {
            case ESVMoveType.Horizontal:
                float childWidth = grid.cellSize.x;
                float maxContentWidth = (childWidth + grid.spacing.x) * childcount - grid.padding.top - sv.GetComponent<RectTransform>().rect.width;
                float disWidth = (childWidth + grid.spacing.x) * scorwPercentage * childcount / maxContentWidth;
                disNormalizedVe2 = disWidth * Vector2.right;
                break;
            case ESVMoveType.Vertical:
                float childHeight = grid.cellSize.y;
                float maxContentHeight = (childHeight + grid.spacing.y) * childcount - grid.padding.left - sv.GetComponent<RectTransform>().rect.height;
                float disHeight = (childHeight + grid.spacing.y) * scorwPercentage * childcount / maxContentHeight;
                disNormalizedVe2 = disHeight * Vector2.up;
                break;
        }
    }
    /// <summary>
    /// chang the Orientation if you want  Backword start 
    /// </summary>
    public void ChangeOrientation()
    {
        switch (svType)
        {
            case ESVMoveType.Horizontal:
                disNormalizedVe2.x *= -1;
                break;
            case ESVMoveType.Vertical:
                disNormalizedVe2.y *= -1;
                break;
        }
    }
    /// <summary>
    /// get the SVNormalizedLong with the type
    /// </summary>
    /// <param name="norLong"></param>
    float GetSVNormalizedLong()
    {
        switch (svType)
        {
            case ESVMoveType.Horizontal:
                return sv.normalizedPosition.x;

            case ESVMoveType.Vertical:
                return sv.normalizedPosition.y;
        }
        return 0;
    }

    //get WillPos limit with movie
    Vector2 MovieRangeLimit(Vector2 willPos)
    {
        switch (svType)
        {
            case ESVMoveType.Horizontal:
                if (willPos.x > 1)
                    willPos.x = 1;
                else if (willPos.x < 0)
                    willPos.x = 0;
                break;
            case ESVMoveType.Vertical:
                if (willPos.y > 1)
                    willPos.y = 1;
                else if (willPos.y < 0)
                    willPos.y = 0;
                break;
        }
        return willPos;
    }

    //limitd the Duaration with WillPos and CorrectPos,it can let movie become more smooth
    float GetDuarationLimitWithDis(Vector2 willPos, Vector2 correctPos)
    {
        if (willPos == correctPos)
            return duration;

        switch (svType)
        {
            case ESVMoveType.Horizontal:
                return (willPos.x - correctPos.x) / (willPos.x < 0 ? willPos.x - 1 : willPos.x) * duration;
            case ESVMoveType.Vertical:
                return (willPos.y - correctPos.y) / (willPos.y < 0 ? willPos.y - 1 : willPos.y) * duration;
        }
        return duration;
    }
}

然后是添加了动态删除添加移动速率不变


using System;
/**
*Copyright(C) 2019 by #COMPANY#
*All rights reserved.
*FileName:     #SCRIPTFULLNAME#
*Author:       #AUTHOR#
*Version:      #VERSION#
*UnityVersion:#UNITYVERSION#
*Date:         #DATE#
*Description:   
*History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;

public enum ESVMoveType
{
    Horizontal = 0,
    Vertical
}

public class SVMove
{
    /// <summary>
    /// the firstStart do not Use
    /// </summary>
    public Action correctionHeadCallBack;
    public Action correctionTailCallBack;
    public float duration = 1f;
    Vector2 disNormalizedVe2;
    ScrollRect sv;
    GridLayoutGroup grid;
    bool isTail;
    float scorwPercentage;
    int childcount;

    ESVMoveType svType;
    /// <summary>
    /// Set the SV with Btn and Org 
    /// </summary>
    /// <param name="sv">ScrowView</param>
    /// <param name="btn">ControBtn</param>
    /// <param name="org">Orientation</param>
    public SVMove(Transform sv, int scrowNum = 1, ESVMoveType type = ESVMoveType.Vertical)
    {
        this.sv = sv.GetComponent<ScrollRect>();
        grid = this.sv.content.GetComponent<GridLayoutGroup>();
        svType = type;
        childcount = grid.transform.childCount;
        scorwPercentage = (float)scrowNum / childcount;
        GetDistance(childcount);
    }

    /// <summary>
    /// Move Active Response
    /// </summary>
    public void Move()
    {
        float svNormalizedLong = GetSVNormalizedLong();

        if (svNormalizedLong <= 0)
            isTail = false;
        if (svNormalizedLong >= 1)
            isTail = true;

        if (isTail)
            DoForwardMove();
        else
            DoBackwordMove();
    }

    /// <summary>
    /// Forward move
    /// </summary>
    public void DoForwardMove()
    {
        ChangeDisWithChildcount();
        Vector2 willPos = sv.normalizedPosition - disNormalizedVe2;
        Vector2 correctPos = MovieRangeLimit(willPos);
        float correctDur = GetDuarationLimitWithDis(willPos, correctPos);

        DOTween.To(() => sv.normalizedPosition, x => sv.normalizedPosition = x,
            correctPos, correctDur).OnComplete(() =>
            {
                //Correction the tail with End
                if (GetSVNormalizedLong() <= 0.01)
                {
                    if (correctionTailCallBack != null)
                        correctionTailCallBack();

                    isTail = false;
                }
            });
    }

    /// <summary>
    /// Backword Move
    /// </summary>
    public void DoBackwordMove()
    {
        ChangeDisWithChildcount();
        Vector2 willPos = sv.normalizedPosition + disNormalizedVe2;
        Vector2 correctPos = MovieRangeLimit(willPos);
        float correctDur = GetDuarationLimitWithDis(willPos, correctPos);

        DOTween.To(() => sv.normalizedPosition, x => sv.normalizedPosition = x,
            correctPos, correctDur).OnComplete(() =>
            {
                //Correction the head  with Start
                if (GetSVNormalizedLong() >= 0.99f)
                {
                    if (correctionHeadCallBack != null)
                        correctionHeadCallBack();

                    isTail = true;
                }
            });
    }
    /// <summary>
    /// if childcount change the moveDistance will be reCalculation
    /// </summary>
    void ChangeDisWithChildcount()
    {
        if (childcount != grid.transform.childCount)
        {
            childcount = grid.transform.childCount;
            GetDistance(childcount);
        }
    }
    /// <summary>
    /// Get the Distance with ScrowNum and Orientation
    /// </summary>
    /// <param name="scrowNum"></param>
    /// <param name="org"></param>
    void GetDistance(int childcount)
    {
        switch (svType)
        {
            case ESVMoveType.Horizontal:
                float childWidth = grid.cellSize.x;
                float maxContentWidth = (childWidth + grid.spacing.x) * childcount - grid.padding.top - sv.GetComponent<RectTransform>().rect.width;
                float disWidth = (childWidth + grid.spacing.x) * scorwPercentage * childcount / maxContentWidth;
                disNormalizedVe2 = disWidth * Vector2.right;
                break;
            case ESVMoveType.Vertical:
                float childHeight = grid.cellSize.y;
                float maxContentHeight = (childHeight + grid.spacing.y) * childcount - grid.padding.left - sv.GetComponent<RectTransform>().rect.height;
                float disHeight = (childHeight + grid.spacing.y) * scorwPercentage * childcount / maxContentHeight;
                disNormalizedVe2 = disHeight * Vector2.up;
                break;
        }
    }
    /// <summary>
    /// chang the Orientation if you want  Backword start 
    /// </summary>
    public void ChangeOrientation()
    {
        switch (svType)
        {
            case ESVMoveType.Horizontal:
                disNormalizedVe2.x *= -1;
                break;
            case ESVMoveType.Vertical:
                disNormalizedVe2.y *= -1;
                break;
        }
    }
    /// <summary>
    /// get the SVNormalizedLong with the type
    /// </summary>
    /// <param name="norLong"></param>
    float GetSVNormalizedLong()
    {
        switch (svType)
        {
            case ESVMoveType.Horizontal:
                return sv.normalizedPosition.x;

            case ESVMoveType.Vertical:
                return sv.normalizedPosition.y;
        }
        return 0;
    }

    //get WillPos limit with movie
    Vector2 MovieRangeLimit(Vector2 willPos)
    {
        switch (svType)
        {
            case ESVMoveType.Horizontal:
                if (willPos.x > 1)
                    willPos.x = 1;
                else if (willPos.x < 0)
                    willPos.x = 0;
                break;
            case ESVMoveType.Vertical:
                if (willPos.y > 1)
                    willPos.y = 1;
                else if (willPos.y < 0)
                    willPos.y = 0;
                break;
        }
        return willPos;
    }

    //limitd the Duaration with WillPos and CorrectPos,it can let movie become more smooth
    float GetDuarationLimitWithDis(Vector2 willPos, Vector2 correctPos)
    {
        if (willPos == correctPos)
            return duration;

        switch (svType)
        {
            case ESVMoveType.Horizontal:
                return (willPos.x - correctPos.x) / (willPos.x < 0 ? willPos.x - 1 : willPos.x) * duration;
            case ESVMoveType.Vertical:
                return (willPos.y - correctPos.y) / (willPos.y < 0 ? willPos.y - 1 : willPos.y) * duration;
        }
        return duration;
    }
}

然后是源码Demo地址
https://github.com/1004019267/ScorwViewMoveAndDragUI

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 29,106评论 1 45
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,812评论 1 32
  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,594评论 0 21
  • 做自己喜歡的事會被專重嗎[?] 在開學後的第一堂公民課,班主任叫每個同學介紹自己一遍,除姓名以外,還要説自己喜歡做...
    咕噜咕噜喝酒了阅读 249评论 0 2
  • 在古老的山村里,流传着一个关于月下石缘的民间故事。这个故事充满了神奇与浪漫,被一代又一代人传颂至今。 故事发生在很...
    Abc159357阅读 271评论 0 5

友情链接更多精彩内容