/Sandwich

"here the source code for Unity web like content carousel"

using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems;

public class UIContentCarousel : MonoBehaviour, IEndDragHandler, IBeginDragHandler { [Header("Important Information")] [Tooltip("Ensure that a GridLayoutGroup is assigned to CONTENT. The correct layout group is crucial for the proper functioning of the carousel.")] [TextArea(3, 5)] public string layoutGroupWarning;

public enum LayoutType
{
    Vertical,
    Horizontal
}

[Header("Layout Settings")]
[Tooltip("Select the layout type for the content carousel.")]
public LayoutType layoutType = LayoutType.Vertical;

[Header("Content Viewport")]
[Tooltip("Specify the size of each page in the carousel.")]
public float pageSize = 600f;

[Tooltip("The threshold for swipe detection. Adjust for sensitivity.")]
public float swipeThreshold = 0.2f;

[Tooltip("The speed at which the content snaps to the target position.")]
public float snapSpeed = 8f;

[Header("Navigation Buttons")]
[Tooltip("Click to move to the next page.")]
public Button nextButton;

[Tooltip("Click to move to the previous page.")]
public Button prevButton;

[Header("Carousel Mode")]
[Tooltip("Enables automatic cycling through pages.")]
public bool carouselMode = false;

[Tooltip("Starts automatic page cycling when enabled.")]
public bool autoMove = false;

[Tooltip("Adjusts the time interval between automatic page transitions (in seconds).")]
public float autoMoveTimer = 5f;

[Header("Navigation Dots")]
[Tooltip("Prefab for navigation dots.")]
public GameObject dotPrefab;

[Tooltip("Container to hold the navigation dots.")]
public GameObject dotsContainer;

[Header("Dot Colors")]
[Tooltip("Sets the color of the dot representing the current page.")]
public Color activeDotColor = Color.yellow; // Yellow/Goldish

[Tooltip("Sets the color of the dots representing inactive pages.")]
public Color inactiveDotColor = Color.grey;

[Tooltip("Controls the speed of the color transition between dots (in seconds).")]
public float dotColorTransitionSpeed = 5f;

[Header("Dot Scaling")]
[Tooltip("The size of the active navigation dot.")]
public Vector2 activeDotSize = new Vector2(20f, 10f);

[Tooltip("The size of inactive navigation dots.")]
public Vector2 inactiveDotSize = new Vector2(10f, 10f);

[Tooltip("The speed at which the navigation dots scale.")]
public float dotScalingSpeed = 5f;

[Header("3D Rotation")]
[Tooltip("The maximum rotation angle for content items.")]
public float maxRotationAngle = 45f;

[Tooltip("The speed of the rotation effect.")]
public float rotationSpeed = 5f;

[Header("(Experimental Features)")]
[Header("Infinite Looping")]
[Tooltip("Enable or disable infinite looping for the carousel.")]
public bool infiniteLooping = true;

[Header("Checking...")]
[Tooltip("Indicates the total number of pages.")]
public int totalPages;

[Tooltip("Specifies the index of the currently displayed page.")]
public int currentIndex = 0;

[Tooltip("Manages the layout of page elements.")]
public GridLayoutGroup gridLayoutGroup;

//private variables//...
private ScrollRect scrollRect;
private RectTransform contentRectTransform;
private float targetPosition;
private bool isDragging = false;
private Vector2 dragStartPos;
private float lastDragTime;
private float autoMoveTimerCountdown;

private void Start()
{
    scrollRect = GetComponent<ScrollRect>();
    gridLayoutGroup = GetComponentInChildren<GridLayoutGroup>();

    if (gridLayoutGroup == null)
    {
        Debug.LogError("GridLayoutGroup not found in children. Make sure it is present.");
        return;
    }

    contentRectTransform = gridLayoutGroup.transform as RectTransform;

    if (contentRectTransform == null)
    {
        Debug.LogError("RectTransform not found on the GridLayoutGroup. Make sure it is present.");
        return;
    }

    // Set the GridLayoutGroup's start axis based on the layout type
    gridLayoutGroup.startAxis = (layoutType == LayoutType.Vertical) ? GridLayoutGroup.Axis.Vertical : GridLayoutGroup.Axis.Horizontal;

    CalculateTotalPages();
    SetSnapTarget(0);

    if (carouselMode)
    {
        InitializeNavigationDots();
    }

    // Setup navigation button click events
    if (nextButton != null)
    {
        nextButton.onClick.AddListener(MoveToNextPage);
    }

    if (prevButton != null)
    {
        prevButton.onClick.AddListener(MoveToPreviousPage);
    }
}

private void InitializeNavigationDots()
{
    for (int i = 0; i < totalPages; i++)
    {
        GameObject dot = Instantiate(dotPrefab, dotsContainer.transform);
        SetDotSize(dot, i == currentIndex ? activeDotSize : inactiveDotSize);
        SetDotColor(dot, i == currentIndex ? activeDotColor : inactiveDotColor);
        // You may need to position the dots based on your layout preferences
    }
}

private void SetDotColor(GameObject dot, Color color)
{
    Image dotImage = dot.GetComponent<Image>();
    if (dotImage != null)
    {
        dotImage.color = color;
    }
}

private void SetDotSize(GameObject dot, Vector2 size)
{
    RectTransform dotRect = dot.GetComponent<RectTransform>();
    if (dotRect != null)
    {
        dotRect.sizeDelta = size;
    }
}

private void CalculateTotalPages()
{
    int itemCount = gridLayoutGroup.transform.childCount;
    totalPages = Mathf.CeilToInt((float)itemCount / gridLayoutGroup.constraintCount);
}

private void SetSnapTarget(int page)
{
    if (infiniteLooping)
    {
        int totalVisiblePages = totalPages * 2; // Duplicate the pages to allow for looping
        int offsetPage = (page + totalVisiblePages) % totalPages;
        targetPosition = -pageSize * offsetPage;
    }
    else
    {
        targetPosition = -pageSize * page;
    }

    currentIndex = page;
}

public void OnBeginDrag(PointerEventData eventData)
{
    isDragging = true;
    dragStartPos = eventData.position;
    lastDragTime = Time.unscaledTime;
}

public void OnEndDrag(PointerEventData eventData)
{
    isDragging = false;

    float dragDistance = Mathf.Abs(eventData.position.x - dragStartPos.x);
    float dragSpeed = eventData.delta.x / (Time.unscaledTime - lastDragTime);

    if (autoMove)
    {
        autoMoveTimerCountdown = autoMoveTimer;
    }

    if (carouselMode)
    {
        // Enable swiping to the next or previous content based on drag distance and speed
        if (dragDistance > pageSize * swipeThreshold || Mathf.Abs(dragSpeed) > swipeThreshold)
        {
            int currentPage = Mathf.RoundToInt(contentRectTransform.anchoredPosition.x / -pageSize);

            if (dragSpeed > 0)
            {
                MoveToPreviousPage();
            }
            else
            {
                MoveToNextPage();
            }
        }
        else
        {
            // Snap back to the current page if swipe distance is not enough
            SetSnapTarget(currentIndex);
        }
    }
}

private void Update()
{
    if (autoMove)
    {
        autoMoveTimerCountdown -= Time.deltaTime;
        if (autoMoveTimerCountdown <= 0f)
        {
            MoveToNextPage();
            autoMoveTimerCountdown = autoMoveTimer;
        }
    }

    if (!isDragging)
    {
        contentRectTransform.anchoredPosition = Vector2.Lerp(
            contentRectTransform.anchoredPosition,
            new Vector2(targetPosition, contentRectTransform.anchoredPosition.y),
            Time.deltaTime * snapSpeed
        );

        // Smoothly update dot sizes based on the current index
        UpdateDotSizes();

        // Rotate content based on its position
        RotateContent();
    }
}

private void RotateContent()
{
    for (int i = 0; i < totalPages; i++)
    {
        GameObject content = gridLayoutGroup.transform.GetChild(i).gameObject;
        float rotationAngle = Mathf.Lerp(0f, maxRotationAngle, Mathf.Abs(currentIndex - i) / (float)totalPages);
        Quaternion targetRotation = Quaternion.Euler(0f, 0f, rotationAngle);

        content.transform.rotation = Quaternion.Slerp(content.transform.rotation, targetRotation, Time.deltaTime * rotationSpeed);
    }
}

private void MoveToPreviousPage()
{
    if (infiniteLooping)
    {
        int prevPage = (currentIndex - 1 + totalPages) % totalPages;
        SetSnapTarget(prevPage);
    }
    else
    {
        int prevPage = Mathf.Clamp(currentIndex - 1, 0, totalPages - 1);
        SetSnapTarget(prevPage);
    }
}

private void MoveToNextPage()
{
    if (infiniteLooping)
    {
        int nextPage = (currentIndex + 1) % totalPages;
        SetSnapTarget(nextPage);
    }
    else
    {
        int nextPage = Mathf.Clamp(currentIndex + 1, 0, totalPages - 1);
        SetSnapTarget(nextPage);
    }
}

private void UpdateDotSizes()
{
    for (int i = 0; i < dotsContainer.transform.childCount; i++)
    {
        GameObject dot = dotsContainer.transform.GetChild(i).gameObject;
        Vector2 targetSize = i == currentIndex ? activeDotSize : inactiveDotSize;
        RectTransform dotRect = dot.GetComponent<RectTransform>();

        if (dotRect != null)
        {
            // Smoothly interpolate between current size and target size
            dotRect.sizeDelta = Vector2.Lerp(dotRect.sizeDelta, targetSize, Time.deltaTime * dotScalingSpeed);
        }

        // Smoothly interpolate between current color and target color
        Image dotImage = dot.GetComponent<Image>();
        if (dotImage != null)
        {
            Color targetColor = i == currentIndex ? activeDotColor : inactiveDotColor;
            dotImage.color = Color.Lerp(dotImage.color, targetColor, Time.deltaTime * dotColorTransitionSpeed);
        }
    }
}

}