/// Credit setchi (https://github.com/setchi) /// Sourced from - https://github.com/setchi/FancyScrollView using System.Collections.Generic; namespace UnityEngine.UI.Extensions { public class FancyScrollView : MonoBehaviour where TContext : class { [SerializeField, Range(float.Epsilon, 1f)] float cellInterval; [SerializeField, Range(0f, 1f)] float cellOffset; [SerializeField] bool loop; [SerializeField] GameObject cellBase; float currentPosition; readonly List> cells = new List>(); protected TContext context; protected List cellData = new List(); protected void Awake() { cellBase.SetActive(false); } /// /// コンテキストを設定します /// /// protected void SetContext(TContext context) { this.context = context; for (int i = 0; i < cells.Count; i++) { cells[i].SetContext(context); } } /// /// セルを生成して返します /// /// FancyScrollViewCell CreateCell() { var cellObject = Instantiate(cellBase); cellObject.SetActive(true); var cell = cellObject.GetComponent>(); var cellRectTransform = cell.transform as RectTransform; // 親要素の付け替えをおこなうとスケールやサイズが失われるため、変数に保持しておく var scale = cell.transform.localScale; var sizeDelta = Vector2.zero; var offsetMin = Vector2.zero; var offsetMax = Vector2.zero; if (cellRectTransform) { sizeDelta = cellRectTransform.sizeDelta; offsetMin = cellRectTransform.offsetMin; offsetMax = cellRectTransform.offsetMax; } cell.transform.SetParent(cellBase.transform.parent); cell.transform.localScale = scale; if (cellRectTransform) { cellRectTransform.sizeDelta = sizeDelta; cellRectTransform.offsetMin = offsetMin; cellRectTransform.offsetMax = offsetMax; } cell.SetContext(context); cell.SetVisible(false); return cell; } #if UNITY_EDITOR float prevCellInterval, prevCellOffset; bool prevLoop; void LateUpdate() { if (prevLoop != loop || prevCellOffset != cellOffset || prevCellInterval != cellInterval) { UpdatePosition(currentPosition); prevLoop = loop; prevCellOffset = cellOffset; prevCellInterval = cellInterval; } } #endif /// /// セルの内容を更新します /// /// /// void UpdateCellForIndex(FancyScrollViewCell cell, int dataIndex) { if (loop) { dataIndex = GetLoopIndex(dataIndex, cellData.Count); } else if (dataIndex < 0 || dataIndex > cellData.Count - 1) { // セルに対応するデータが存在しなければセルを表示しない cell.SetVisible(false); return; } cell.SetVisible(true); cell.DataIndex = dataIndex; cell.UpdateContent(cellData[dataIndex]); } /// /// 円環構造の index を取得します /// /// /// /// int GetLoopIndex(int index, int length) { if (index < 0) { index = (length - 1) + (index + 1) % length; } else if (index > length - 1) { index = index % length; } return index; } /// /// 表示内容を更新します /// protected void UpdateContents() { UpdatePosition(currentPosition); } /// /// スクロール位置を更新します /// /// protected void UpdatePosition(float position) { currentPosition = position; var visibleMinPosition = position - (cellOffset / cellInterval); var firstCellPosition = (Mathf.Ceil(visibleMinPosition) - visibleMinPosition) * cellInterval; var dataStartIndex = Mathf.CeilToInt(visibleMinPosition); var count = 0; var cellIndex = 0; for (float pos = firstCellPosition; pos <= 1f; pos += cellInterval, count++) { if (count >= cells.Count) { cells.Add(CreateCell()); } } count = 0; for (float pos = firstCellPosition; pos <= 1f; count++, pos += cellInterval) { var dataIndex = dataStartIndex + count; cellIndex = GetLoopIndex(dataIndex, cells.Count); if (cells[cellIndex].gameObject.activeSelf) { cells[cellIndex].UpdatePosition(pos); } UpdateCellForIndex(cells[cellIndex], dataIndex); } cellIndex = GetLoopIndex(dataStartIndex + count, cells.Count); for (; count < cells.Count; count++, cellIndex = GetLoopIndex(dataStartIndex + count, cells.Count)) { cells[cellIndex].SetVisible(false); } } } public sealed class FancyScrollViewNullContext { } public class FancyScrollView : FancyScrollView { } }