Removed FancyScrollView

pull/474/head
GoShikhar 2024-04-29 01:56:10 +05:30
parent 545da8ea4d
commit ece2ec5091
42 changed files with 0 additions and 2216 deletions

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 77a80d348c62e93459753f8e704f474d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,119 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using UnityEditor;
using UnityEditor.AnimatedValues;
namespace UnityEngine.UI.Extensions
{
[CustomEditor(typeof(Scroller))]
[CanEditMultipleObjects]
public class ScrollerEditor : Editor
{
SerializedProperty viewport;
SerializedProperty scrollDirection;
SerializedProperty movementType;
SerializedProperty elasticity;
SerializedProperty scrollSensitivity;
SerializedProperty inertia;
SerializedProperty decelerationRate;
SerializedProperty snap;
SerializedProperty draggable;
SerializedProperty scrollbar;
AnimBool showElasticity;
AnimBool showInertiaRelatedValues;
void OnEnable()
{
viewport = serializedObject.FindProperty("viewport");
scrollDirection = serializedObject.FindProperty("scrollDirection");
movementType = serializedObject.FindProperty("movementType");
elasticity = serializedObject.FindProperty("elasticity");
scrollSensitivity = serializedObject.FindProperty("scrollSensitivity");
inertia = serializedObject.FindProperty("inertia");
decelerationRate = serializedObject.FindProperty("decelerationRate");
snap = serializedObject.FindProperty("snap");
draggable = serializedObject.FindProperty("draggable");
scrollbar = serializedObject.FindProperty("scrollbar");
showElasticity = new AnimBool(Repaint);
showInertiaRelatedValues = new AnimBool(Repaint);
SetAnimBools(true);
}
void OnDisable()
{
showElasticity.valueChanged.RemoveListener(Repaint);
showInertiaRelatedValues.valueChanged.RemoveListener(Repaint);
}
void SetAnimBools(bool instant)
{
SetAnimBool(showElasticity, !movementType.hasMultipleDifferentValues && movementType.enumValueIndex == (int)MovementType.Elastic, instant);
SetAnimBool(showInertiaRelatedValues, !inertia.hasMultipleDifferentValues && inertia.boolValue, instant);
}
void SetAnimBool(AnimBool a, bool value, bool instant)
{
if (instant)
{
a.value = value;
}
else
{
a.target = value;
}
}
public override void OnInspectorGUI()
{
SetAnimBools(false);
serializedObject.Update();
EditorGUILayout.PropertyField(viewport);
EditorGUILayout.PropertyField(scrollDirection);
EditorGUILayout.PropertyField(movementType);
DrawMovementTypeRelatedValue();
EditorGUILayout.PropertyField(scrollSensitivity);
EditorGUILayout.PropertyField(inertia);
DrawInertiaRelatedValues();
EditorGUILayout.PropertyField(draggable);
EditorGUILayout.PropertyField(scrollbar);
serializedObject.ApplyModifiedProperties();
}
void DrawMovementTypeRelatedValue()
{
using (var group = new EditorGUILayout.FadeGroupScope(showElasticity.faded))
{
if (!group.visible)
{
return;
}
using (new EditorGUI.IndentLevelScope())
{
EditorGUILayout.PropertyField(elasticity);
}
}
}
void DrawInertiaRelatedValues()
{
using (var group = new EditorGUILayout.FadeGroupScope(showInertiaRelatedValues.faded))
{
if (!group.visible)
{
return;
}
using (new EditorGUI.IndentLevelScope())
{
EditorGUILayout.PropertyField(decelerationRate);
EditorGUILayout.PropertyField(snap);
}
}
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e82bfdd42ec254849830933cbaf350fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d1ddf7c4a2094c8429c834b8c71bb812
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 6096cd9225d267f4da3b54947c75809c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,71 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyScrollView{TItemData, TContext}"/> のセルを実装するための抽象基底クラス.
/// <see cref="FancyCell{TItemData, TContext}.Context"/> が不要な場合は
/// 代わりに <see cref="FancyCell{TItemData}"/> を使用します.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="Context"/> の型.</typeparam>
public abstract class FancyCell<TItemData, TContext> : MonoBehaviour where TContext : class, new()
{
/// <summary>
/// このセルで表示しているデータのインデックス.
/// </summary>
public int Index { get; set; } = -1;
/// <summary>
/// このセルの可視状態.
/// </summary>
public virtual bool IsVisible => gameObject.activeSelf;
/// <summary>
/// <see cref="FancyScrollView{TItemData, TContext}.Context"/> の参照.
/// セルとスクロールビュー間で同じインスタンスが共有されます. 情報の受け渡しや状態の保持に使用します.
/// </summary>
protected TContext Context { get; private set; }
/// <summary>
/// <see cref="Context"/> をセットします.
/// </summary>
/// <param name="context">コンテキスト.</param>
public virtual void SetContext(TContext context) => Context = context;
/// <summary>
/// 初期化を行います.
/// </summary>
public virtual void Initialize() { }
/// <summary>
/// このセルの可視状態を設定します.
/// </summary>
/// <param name="visible">可視状態なら <c>true</c>, 非可視状態なら <c>false</c>.</param>
public virtual void SetVisible(bool visible) => gameObject.SetActive(visible);
/// <summary>
/// アイテムデータに基づいてこのセルの表示内容を更新します.
/// </summary>
/// <param name="itemData">アイテムデータ.</param>
public abstract void UpdateContent(TItemData itemData);
/// <summary>
/// <c>0.0f</c> ~ <c>1.0f</c> の値に基づいてこのセルのスクロール位置を更新します.
/// </summary>
/// <param name="position">ビューポート範囲の正規化されたスクロール位置.</param>
public abstract void UpdatePosition(float position);
}
/// <summary>
/// <see cref="FancyScrollView{TItemData}"/> のセルを実装するための抽象基底クラス.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <seealso cref="FancyCell{TItemData, TContext}"/>
public abstract class FancyCell<TItemData> : FancyCell<TItemData, NullContext>
{
/// <inheritdoc/>
public sealed override void SetContext(NullContext context) => base.SetContext(context);
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 6610dee308450ec40899aeedfd85e972
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,216 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// スクロールビューを実装するための抽象基底クラス.
/// 無限スクロールおよびスナップに対応しています.
/// <see cref="FancyScrollView{TItemData, TContext}.Context"/> が不要な場合は
/// 代わりに <see cref="FancyScrollView{TItemData}"/> を使用します.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="Context"/> の型.</typeparam>
public abstract class FancyScrollView<TItemData, TContext> : MonoBehaviour where TContext : class, new()
{
/// <summary>
/// セル同士の間隔.
/// </summary>
[SerializeField, Range(1e-2f, 1f)] protected float cellInterval = 0.2f;
/// <summary>
/// スクロール位置の基準.
/// </summary>
/// <remarks>
/// たとえば、 <c>0.5</c> を指定してスクロール位置が <c>0</c> の場合, 中央に最初のセルが配置されます.
/// </remarks>
[SerializeField, Range(0f, 1f)] protected float scrollOffset = 0.5f;
/// <summary>
/// セルを循環して配置させるどうか.
/// </summary>
/// <remarks>
/// <c>true</c> にすると最後のセルの後に最初のセル, 最初のセルの前に最後のセルが並ぶようになります.
/// 無限スクロールを実装する場合は <c>true</c> を指定します.
/// </remarks>
[SerializeField] protected bool loop = false;
/// <summary>
/// セルの親要素となる <c>Transform</c>.
/// </summary>
[SerializeField] protected Transform cellContainer = default;
readonly IList<FancyCell<TItemData, TContext>> pool = new List<FancyCell<TItemData, TContext>>();
/// <summary>
/// 初期化済みかどうか.
/// </summary>
protected bool initialized;
/// <summary>
/// 現在のスクロール位置.
/// </summary>
protected float currentPosition;
/// <summary>
/// セルの Prefab.
/// </summary>
protected abstract GameObject CellPrefab { get; }
/// <summary>
/// アイテム一覧のデータ.
/// </summary>
protected IList<TItemData> ItemsSource { get; set; } = new List<TItemData>();
/// <summary>
/// <typeparamref name="TContext"/> のインスタンス.
/// セルとスクロールビュー間で同じインスタンスが共有されます. 情報の受け渡しや状態の保持に使用します.
/// </summary>
protected TContext Context { get; } = new TContext();
/// <summary>
/// 初期化を行います.
/// </summary>
/// <remarks>
/// 最初にセルが生成される直前に呼び出されます.
/// </remarks>
protected virtual void Initialize() { }
/// <summary>
/// 渡されたアイテム一覧に基づいて表示内容を更新します.
/// </summary>
/// <param name="itemsSource">アイテム一覧.</param>
protected virtual void UpdateContents(IList<TItemData> itemsSource)
{
ItemsSource = itemsSource;
Refresh();
}
/// <summary>
/// セルのレイアウトを強制的に更新します.
/// </summary>
protected virtual void Relayout() => UpdatePosition(currentPosition, false);
/// <summary>
/// セルのレイアウトと表示内容を強制的に更新します.
/// </summary>
protected virtual void Refresh() => UpdatePosition(currentPosition, true);
/// <summary>
/// スクロール位置を更新します.
/// </summary>
/// <param name="position">スクロール位置.</param>
protected virtual void UpdatePosition(float position) => UpdatePosition(position, false);
void UpdatePosition(float position, bool forceRefresh)
{
if (!initialized)
{
Initialize();
initialized = true;
}
currentPosition = position;
var p = position - scrollOffset / cellInterval;
var firstIndex = Mathf.CeilToInt(p);
var firstPosition = (Mathf.Ceil(p) - p) * cellInterval;
if (firstPosition + pool.Count * cellInterval < 1f)
{
ResizePool(firstPosition);
}
UpdateCells(firstPosition, firstIndex, forceRefresh);
}
void ResizePool(float firstPosition)
{
Debug.Assert(CellPrefab != null);
Debug.Assert(cellContainer != null);
var addCount = Mathf.CeilToInt((1f - firstPosition) / cellInterval) - pool.Count;
for (var i = 0; i < addCount; i++)
{
var cell = Instantiate(CellPrefab, cellContainer).GetComponent<FancyCell<TItemData, TContext>>();
if (cell == null)
{
throw new MissingComponentException(string.Format(
"FancyCell<{0}, {1}> component not found in {2}.",
typeof(TItemData).FullName, typeof(TContext).FullName, CellPrefab.name));
}
cell.SetContext(Context);
cell.Initialize();
cell.SetVisible(false);
pool.Add(cell);
}
}
void UpdateCells(float firstPosition, int firstIndex, bool forceRefresh)
{
for (var i = 0; i < pool.Count; i++)
{
var index = firstIndex + i;
var position = firstPosition + i * cellInterval;
var cell = pool[CircularIndex(index, pool.Count)];
if (loop)
{
index = CircularIndex(index, ItemsSource.Count);
}
if (index < 0 || index >= ItemsSource.Count || position > 1f)
{
cell.SetVisible(false);
continue;
}
if (forceRefresh || cell.Index != index || !cell.IsVisible)
{
cell.Index = index;
cell.SetVisible(true);
cell.UpdateContent(ItemsSource[index]);
}
cell.UpdatePosition(position);
}
}
int CircularIndex(int i, int size) => size < 1 ? 0 : i < 0 ? size - 1 + (i + 1) % size : i % size;
#if UNITY_EDITOR
bool cachedLoop;
float cachedCellInterval, cachedScrollOffset;
void LateUpdate()
{
if (cachedLoop != loop ||
cachedCellInterval != cellInterval ||
cachedScrollOffset != scrollOffset)
{
cachedLoop = loop;
cachedCellInterval = cellInterval;
cachedScrollOffset = scrollOffset;
UpdatePosition(currentPosition);
}
}
#endif
}
/// <summary>
/// <see cref="FancyScrollView{TItemData}"/> のコンテキストクラス.
/// </summary>
public sealed class NullContext { }
/// <summary>
/// スクロールビューを実装するための抽象基底クラス.
/// 無限スクロールおよびスナップに対応しています.
/// </summary>
/// <typeparam name="TItemData"></typeparam>
/// <seealso cref="FancyScrollView{TItemData, TContext}"/>
public abstract class FancyScrollView<TItemData> : FancyScrollView<TItemData, NullContext> { }
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 0e38f4de7b1b5a5429228884014d12ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: c7768f2982b0142ab876d2bb4b597646
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,72 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System.Linq;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// 複数の <see cref="FancyCell{TItemData, TContext}"/> を持つセルグループ実装するための抽象基底クラス.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="FancyCell{TItemData, TContext}.Context"/> の型.</typeparam>
public abstract class FancyCellGroup<TItemData, TContext> : FancyCell<TItemData[], TContext>
where TContext : class, IFancyCellGroupContext, new()
{
/// <summary>
/// このグループで表示するセルの配列.
/// </summary>
protected virtual FancyCell<TItemData, TContext>[] Cells { get; private set; }
/// <summary>
/// このグループで表示するセルの配列をインスタンス化します.
/// </summary>
/// <returns>このグループで表示するセルの配列.</returns>
protected virtual FancyCell<TItemData, TContext>[] InstantiateCells()
{
return Enumerable.Range(0, Context.GetGroupCount())
.Select(_ => Instantiate(Context.CellTemplate, transform))
.Select(x => x.GetComponent<FancyCell<TItemData, TContext>>())
.ToArray();
}
/// <inheritdoc/>
public override void Initialize()
{
Cells = InstantiateCells();
Debug.Assert(Cells.Length == Context.GetGroupCount());
for (var i = 0; i < Cells.Length; i++)
{
Cells[i].SetContext(Context);
Cells[i].Initialize();
}
}
/// <inheritdoc/>
public override void UpdateContent(TItemData[] contents)
{
var firstCellIndex = Index * Context.GetGroupCount();
for (var i = 0; i < Cells.Length; i++)
{
Cells[i].Index = i + firstCellIndex;
Cells[i].SetVisible(i < contents.Length);
if (Cells[i].IsVisible)
{
Cells[i].UpdateContent(contents[i]);
}
}
}
/// <inheritdoc/>
public override void UpdatePosition(float position)
{
for (var i = 0; i < Cells.Length; i++)
{
Cells[i].UpdatePosition(position);
}
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 5d97e25c7748b8d44acd2298e509c8f1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,181 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.UI.Extensions.EasingCore;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// グリッドレイアウトのスクロールビューを実装するための抽象基底クラス.
/// 無限スクロールおよびスナップには対応していません.
/// <see cref="FancyScrollView{TItemData, TContext}.Context"/> が不要な場合は
/// 代わりに <see cref="FancyGridView{TItemData}"/> を使用します.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="FancyScrollView{TItemData, TContext}.Context"/> の型.</typeparam>
public abstract class FancyGridView<TItemData, TContext> : FancyScrollRect<TItemData[], TContext>
where TContext : class, IFancyGridViewContext, new()
{
/// <summary>
/// デフォルトのセルグループクラス.
/// </summary>
protected abstract class DefaultCellGroup : FancyCellGroup<TItemData, TContext> { }
/// <summary>
/// 最初にセルを配置する軸方向のセル同士の余白.
/// </summary>
[SerializeField] protected float startAxisSpacing = 0f;
/// <summary>
/// 最初にセルを配置する軸方向のセル数.
/// </summary>
[SerializeField] protected int startAxisCellCount = 4;
/// <summary>
/// セルのサイズ.
/// </summary>
[SerializeField] protected Vector2 cellSize = new Vector2(100f, 100f);
/// <summary>
/// セルのグループ Prefab.
/// </summary>
/// <remarks>
/// <see cref="FancyGridView{TItemData, TContext}"/> では,
/// <see cref="FancyScrollView{TItemData, TContext}.CellPrefab"/> を最初にセルを配置する軸方向のセルコンテナとして使用します.
/// </remarks>
protected sealed override GameObject CellPrefab => cellGroupTemplate;
/// <inheritdoc/>
protected override float CellSize => Scroller.ScrollDirection == ScrollDirection.Horizontal
? cellSize.x
: cellSize.y;
/// <summary>
/// アイテムの総数.
/// </summary>
public int DataCount { get; private set; }
GameObject cellGroupTemplate;
/// <inheritdoc/>
protected override void Initialize()
{
base.Initialize();
Debug.Assert(startAxisCellCount > 0);
Context.ScrollDirection = Scroller.ScrollDirection;
Context.GetGroupCount = () => startAxisCellCount;
Context.GetStartAxisSpacing = () => startAxisSpacing;
Context.GetCellSize = () => Scroller.ScrollDirection == ScrollDirection.Horizontal
? cellSize.y
: cellSize.x;
SetupCellTemplate();
}
/// <summary>
/// 最初にセルが生成される直前に呼び出されます.
/// <see cref="Setup{TGroup}(FancyCell{TItemData, TContext})"/> メソッドを使用してセルテンプレートのセットアップを行ってください.
/// </summary>
/// <example>
/// <code><![CDATA[
/// using UnityEngine;
/// using FancyScrollView;
///
/// public class MyGridView : FancyGridView<ItemData, Context>
/// {
/// class CellGroup : DefaultCellGroup { }
///
/// [SerializeField] Cell cellPrefab = default;
///
/// protected override void SetupCellTemplate() => Setup<CellGroup>(cellPrefab);
/// }
/// ]]></code>
/// </example>
protected abstract void SetupCellTemplate();
/// <summary>
/// セルテンプレートのセットアップを行います.
/// </summary>
/// <param name="cellTemplate">セルのテンプレート.</param>
/// <typeparam name="TGroup">セルグループの型.</typeparam>
protected virtual void Setup<TGroup>(FancyCell<TItemData, TContext> cellTemplate)
where TGroup : FancyCell<TItemData[], TContext>
{
Context.CellTemplate = cellTemplate.gameObject;
cellGroupTemplate = new GameObject("Group").AddComponent<TGroup>().gameObject;
cellGroupTemplate.transform.SetParent(cellContainer, false);
cellGroupTemplate.SetActive(false);
}
/// <summary>
/// 渡されたアイテム一覧に基づいて表示内容を更新します.
/// </summary>
/// <param name="items">アイテム一覧.</param>
public virtual void UpdateContents(IList<TItemData> items)
{
DataCount = items.Count;
var itemGroups = items
.Select((item, index) => (item, index))
.GroupBy(
x => x.index / startAxisCellCount,
x => x.item)
.Select(group => group.ToArray())
.ToArray();
UpdateContents(itemGroups);
}
/// <summary>
/// 指定したアイテムの位置までジャンプします.
/// </summary>
/// <param name="itemIndex">アイテムのインデックス.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
protected override void JumpTo(int itemIndex, float alignment = 0.5f)
{
var groupIndex = itemIndex / startAxisCellCount;
base.JumpTo(groupIndex, alignment);
}
/// <summary>
/// 指定したアイテムの位置まで移動します.
/// </summary>
/// <param name="itemIndex">アイテムのインデックス.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
protected override void ScrollTo(int itemIndex, float duration, float alignment = 0.5f, Action onComplete = null)
{
var groupIndex = itemIndex / startAxisCellCount;
base.ScrollTo(groupIndex, duration, alignment, onComplete);
}
/// <summary>
/// 指定したアイテムの位置まで移動します.
/// </summary>
/// <param name="itemIndex">アイテムのインデックス.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="easing">移動に使用するイージング.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
protected override void ScrollTo(int itemIndex, float duration, Ease easing, float alignment = 0.5f, Action onComplete = null)
{
var groupIndex = itemIndex / startAxisCellCount;
base.ScrollTo(groupIndex, duration, easing, alignment, onComplete);
}
}
/// <summary>
/// グリッドレイアウトのスクロールビューを実装するための抽象基底クラス.
/// 無限スクロールおよびスナップには対応していません.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <seealso cref="FancyGridView{TItemData, TContext}"/>
public abstract class FancyGridView<TItemData> : FancyGridView<TItemData, FancyGridViewContext> { }
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e806c8d18b1ff45bb87e9a5b87ec85e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,42 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyGridView{TItemData, TContext}"/> のセルを実装するための抽象基底クラス.
/// <see cref="FancyCell{TItemData, TContext}.Context"/> が不要な場合は
/// 代わりに <see cref="FancyGridViewCell{TItemData}"/> を使用します.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="FancyCell{TItemData, TContext}.Context"/> の型.</typeparam>
public abstract class FancyGridViewCell<TItemData, TContext> : FancyScrollRectCell<TItemData, TContext>
where TContext : class, IFancyGridViewContext, new()
{
/// <inheritdoc/>
protected override void UpdatePosition(float normalizedPosition, float localPosition)
{
var cellSize = Context.GetCellSize();
var spacing = Context.GetStartAxisSpacing();
var groupCount = Context.GetGroupCount();
var indexInGroup = Index % groupCount;
var positionInGroup = (cellSize + spacing) * (indexInGroup - (groupCount - 1) * 0.5f);
transform.localPosition = Context.ScrollDirection == ScrollDirection.Horizontal
? new Vector2(-localPosition, -positionInGroup)
: new Vector2(positionInGroup, localPosition);
}
}
/// <summary>
/// <see cref="FancyGridView{TItemData}"/> のセルを実装するための抽象基底クラス.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <seealso cref="FancyGridViewCell{TItemData, TContext}"/>
public abstract class FancyGridViewCell<TItemData> : FancyGridViewCell<TItemData, FancyGridViewContext>
{
/// <inheritdoc/>
public sealed override void SetContext(FancyGridViewContext context) => base.SetContext(context);
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ab8a59bbf5118824ab084e32342ad86b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,20 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyGridView{TItemData, TContext}"/> のコンテキスト基底クラス.
/// </summary>
public class FancyGridViewContext : IFancyGridViewContext
{
ScrollDirection IFancyScrollRectContext.ScrollDirection { get; set; }
Func<(float ScrollSize, float ReuseMargin)> IFancyScrollRectContext.CalculateScrollSize { get; set; }
GameObject IFancyCellGroupContext.CellTemplate { get; set; }
Func<int> IFancyCellGroupContext.GetGroupCount { get; set; }
Func<float> IFancyGridViewContext.GetStartAxisSpacing { get; set; }
Func<float> IFancyGridViewContext.GetCellSize { get; set; }
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 954a17398bfb54ee7baac3d7ab7e822c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,16 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyCellGroup{TItemData, TContext}"/> のコンテキストインターフェース.
/// </summary>
public interface IFancyCellGroupContext
{
GameObject CellTemplate { get; set; }
Func<int> GetGroupCount { get; set; }
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 1dc086f250206754aa8449e252d50388
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,16 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyGridView{TItemData, TContext}"/> のコンテキストインターフェース.
/// </summary>
public interface IFancyGridViewContext : IFancyScrollRectContext, IFancyCellGroupContext
{
Func<float> GetStartAxisSpacing { get; set; }
Func<float> GetCellSize { get; set ; }
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3cf2d53d9c81945c28f7c558a7c40ba3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: e2c5beedd885c4490a86bb4973f965bf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,301 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
using System.Collections.Generic;
using UnityEngine.UI.Extensions.EasingCore;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// ScrollRect スタイルのスクロールビューを実装するための抽象基底クラス.
/// 無限スクロールおよびスナップには対応していません.
/// <see cref="FancyScrollView{TItemData, TContext}.Context"/> が不要な場合は
/// 代わりに <see cref="FancyScrollRect{TItemData}"/> を使用します.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="FancyScrollView{TItemData, TContext}.Context"/> の型.</typeparam>
[RequireComponent(typeof(Scroller))]
public abstract class FancyScrollRect<TItemData, TContext> : FancyScrollView<TItemData, TContext>
where TContext : class, IFancyScrollRectContext, new()
{
/// <summary>
/// スクロール中にセルが再利用されるまでの余白のセル数.
/// </summary>
/// <remarks>
/// <c>0</c> を指定するとセルが完全に隠れた直後に再利用されます.
/// <c>1</c> 以上を指定すると, そのセル数だけ余分にスクロールしてから再利用されます.
/// </remarks>
[SerializeField] protected float reuseCellMarginCount = 0f;
/// <summary>
/// コンテンツ先頭の余白.
/// </summary>
[SerializeField] protected float paddingHead = 0f;
/// <summary>
/// コンテンツ末尾の余白.
/// </summary>
[SerializeField] protected float paddingTail = 0f;
/// <summary>
/// スクロール軸方向のセル同士の余白.
/// </summary>
[SerializeField] protected float spacing = 0f;
/// <summary>
/// セルのサイズ.
/// </summary>
protected abstract float CellSize { get; }
/// <summary>
/// スクロール可能かどうか.
/// </summary>
/// <remarks>
/// アイテム数が十分少なくビューポート内に全てのセルが収まっている場合は <c>false</c>, それ以外は <c>true</c> になります.
/// </remarks>
protected virtual bool Scrollable => MaxScrollPosition > 0f;
Scroller cachedScroller;
/// <summary>
/// スクロール位置を制御する <see cref="FancyScrollView.Scroller"/> のインスタンス.
/// </summary>
/// <remarks>
/// <see cref="Scroller"/> のスクロール位置を変更する際は必ず <see cref="ToScrollerPosition(float)"/> を使用して変換した位置を使用してください.
/// </remarks>
protected Scroller Scroller => cachedScroller ?? (cachedScroller = GetComponent<Scroller>());
float ScrollLength => 1f / Mathf.Max(cellInterval, 1e-2f) - 1f;
float ViewportLength => ScrollLength - reuseCellMarginCount * 2f;
float PaddingHeadLength => (paddingHead - spacing * 0.5f) / (CellSize + spacing);
float MaxScrollPosition => ItemsSource.Count
- ScrollLength
+ reuseCellMarginCount * 2f
+ (paddingHead + paddingTail - spacing) / (CellSize + spacing);
/// <inheritdoc/>
protected override void Initialize()
{
base.Initialize();
Context.ScrollDirection = Scroller.ScrollDirection;
Context.CalculateScrollSize = () =>
{
var interval = CellSize + spacing;
var reuseMargin = interval * reuseCellMarginCount;
var scrollSize = Scroller.ViewportSize + interval + reuseMargin * 2f;
return (scrollSize, reuseMargin);
};
AdjustCellIntervalAndScrollOffset();
Scroller.OnValueChanged(OnScrollerValueChanged);
}
/// <summary>
/// <see cref="Scroller"/> のスクロール位置が変更された際の処理.
/// </summary>
/// <param name="p"><see cref="Scroller"/> のスクロール位置.</param>
void OnScrollerValueChanged(float p)
{
base.UpdatePosition(ToFancyScrollViewPosition(Scrollable ? p : 0f));
if (Scroller.Scrollbar)
{
if (p > ItemsSource.Count - 1)
{
ShrinkScrollbar(p - (ItemsSource.Count - 1));
}
else if (p < 0f)
{
ShrinkScrollbar(-p);
}
}
}
/// <summary>
/// スクロール範囲を超えてスクロールされた量に基づいて, スクロールバーのサイズを縮小します.
/// </summary>
/// <param name="offset">スクロール範囲を超えてスクロールされた量.</param>
void ShrinkScrollbar(float offset)
{
var scale = 1f - ToFancyScrollViewPosition(offset) / (ViewportLength - PaddingHeadLength);
UpdateScrollbarSize((ViewportLength - PaddingHeadLength) * scale);
}
/// <inheritdoc/>
protected override void Refresh()
{
AdjustCellIntervalAndScrollOffset();
RefreshScroller();
base.Refresh();
}
/// <inheritdoc/>
protected override void Relayout()
{
AdjustCellIntervalAndScrollOffset();
RefreshScroller();
base.Relayout();
}
/// <summary>
/// <see cref="Scroller"/> の各種状態を更新します.
/// </summary>
protected void RefreshScroller()
{
Scroller.Draggable = Scrollable;
Scroller.ScrollSensitivity = ToScrollerPosition(ViewportLength - PaddingHeadLength);
Scroller.Position = ToScrollerPosition(currentPosition);
if (Scroller.Scrollbar)
{
Scroller.Scrollbar.gameObject.SetActive(Scrollable);
UpdateScrollbarSize(ViewportLength);
}
}
/// <inheritdoc/>
protected override void UpdateContents(IList<TItemData> items)
{
AdjustCellIntervalAndScrollOffset();
base.UpdateContents(items);
Scroller.SetTotalCount(items.Count);
RefreshScroller();
}
/// <summary>
/// スクロール位置を更新します.
/// </summary>
/// <param name="position">スクロール位置.</param>
protected new void UpdatePosition(float position)
{
Scroller.Position = ToScrollerPosition(position, 0.5f);
}
/// <summary>
/// 指定したアイテムの位置までジャンプします.
/// </summary>
/// <param name="itemIndex">アイテムのインデックス.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
protected virtual void JumpTo(int itemIndex, float alignment = 0.5f)
{
Scroller.Position = ToScrollerPosition(itemIndex, alignment);
}
/// <summary>
/// 指定したアイテムの位置まで移動します.
/// </summary>
/// <param name="index">アイテムのインデックス.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
protected virtual void ScrollTo(int index, float duration, float alignment = 0.5f, Action onComplete = null)
{
Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, onComplete);
}
/// <summary>
/// 指定したアイテムの位置まで移動します.
/// </summary>
/// <param name="index">アイテムのインデックス.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="easing">移動に使用するイージング.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
protected virtual void ScrollTo(int index, float duration, Ease easing, float alignment = 0.5f, Action onComplete = null)
{
Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, easing, onComplete);
}
/// <summary>
/// ビューポートとコンテンツの長さに基づいてスクロールバーのサイズを更新します.
/// </summary>
/// <param name="viewportLength">ビューポートのサイズ.</param>
protected void UpdateScrollbarSize(float viewportLength)
{
var contentLength = Mathf.Max(ItemsSource.Count + (paddingHead + paddingTail - spacing) / (CellSize + spacing), 1);
Scroller.Scrollbar.size = Scrollable ? Mathf.Clamp01(viewportLength / contentLength) : 1f;
}
/// <summary>
/// <see cref="Scroller"/> が扱うスクロール位置を <see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置に変換します.
/// </summary>
/// <param name="position"><see cref="Scroller"/> が扱うスクロール位置.</param>
/// <returns><see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置.</returns>
protected float ToFancyScrollViewPosition(float position)
{
return position / Mathf.Max(ItemsSource.Count - 1, 1) * MaxScrollPosition - PaddingHeadLength;
}
/// <summary>
/// <see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置を <see cref="Scroller"/> が扱うスクロール位置に変換します.
/// </summary>
/// <param name="position"><see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置.</param>
/// <returns><see cref="Scroller"/> が扱うスクロール位置.</returns>
protected float ToScrollerPosition(float position)
{
return (position + PaddingHeadLength) / MaxScrollPosition * Mathf.Max(ItemsSource.Count - 1, 1);
}
/// <summary>
/// <see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置を <see cref="Scroller"/> が扱うスクロール位置に変換します.
/// </summary>
/// <param name="position"><see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置.</param>
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
/// <returns><see cref="Scroller"/> が扱うスクロール位置.</returns>
protected float ToScrollerPosition(float position, float alignment = 0.5f)
{
var offset = alignment * (ScrollLength - (1f + reuseCellMarginCount * 2f))
+ (1f - alignment - 0.5f) * spacing / (CellSize + spacing);
return ToScrollerPosition(Mathf.Clamp(position - offset, 0f, MaxScrollPosition));
}
/// <summary>
/// 指定された設定を実現するための
/// <see cref="FancyScrollView{TItemData,TContext}.cellInterval"/> と
/// <see cref="FancyScrollView{TItemData,TContext}.scrollOffset"/> を計算して適用します.
/// </summary>
protected void AdjustCellIntervalAndScrollOffset()
{
var totalSize = Scroller.ViewportSize + (CellSize + spacing) * (1f + reuseCellMarginCount * 2f);
cellInterval = (CellSize + spacing) / totalSize;
scrollOffset = cellInterval * (1f + reuseCellMarginCount);
}
protected virtual void OnValidate()
{
AdjustCellIntervalAndScrollOffset();
if (loop)
{
loop = false;
Debug.LogError("Loop is currently not supported in FancyScrollRect.");
}
if (Scroller.SnapEnabled)
{
Scroller.SnapEnabled = false;
Debug.LogError("Snap is currently not supported in FancyScrollRect.");
}
if (Scroller.MovementType == MovementType.Unrestricted)
{
Scroller.MovementType = MovementType.Elastic;
Debug.LogError("MovementType.Unrestricted is currently not supported in FancyScrollRect.");
}
}
}
/// <summary>
/// ScrollRect スタイルのスクロールビューを実装するための抽象基底クラス.
/// 無限スクロールおよびスナップには対応していません.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <seealso cref="FancyScrollRect{TItemData, TContext}"/>
public abstract class FancyScrollRect<TItemData> : FancyScrollRect<TItemData, FancyScrollRectContext> { }
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 66c8eb84fdbde4a4a8273b98227a282d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,56 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyScrollRect{TItemData, TContext}"/> のセルを実装するための抽象基底クラス.
/// <see cref="FancyCell{TItemData, TContext}.Context"/> が不要な場合は
/// 代わりに <see cref="FancyScrollRectCell{TItemData}"/> を使用します.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <typeparam name="TContext"><see cref="FancyCell{TItemData, TContext}.Context"/> の型.</typeparam>
public abstract class FancyScrollRectCell<TItemData, TContext> : FancyCell<TItemData, TContext>
where TContext : class, IFancyScrollRectContext, new()
{
/// <inheritdoc/>
public override void UpdatePosition(float position)
{
var (scrollSize, reuseMargin) = Context.CalculateScrollSize();
var normalizedPosition = (Mathf.Lerp(0f, scrollSize, position) - reuseMargin) / (scrollSize - reuseMargin * 2f);
var start = 0.5f * scrollSize;
var end = -start;
UpdatePosition(normalizedPosition, Mathf.Lerp(start, end, position));
}
/// <summary>
/// このセルの位置を更新します.
/// </summary>
/// <param name="normalizedPosition">
/// ビューポートの範囲で正規化されたスクロール位置.
/// <see cref="FancyScrollRect{TItemData, TContext}.reuseCellMarginCount"/> の値に基づいて
/// <c>0.0</c> ~ <c>1.0</c> の範囲を超えた値が渡されることがあります.
/// </param>
/// <param name="localPosition">ローカル位置.</param>
protected virtual void UpdatePosition(float normalizedPosition, float localPosition)
{
transform.localPosition = Context.ScrollDirection == ScrollDirection.Horizontal
? new Vector2(-localPosition, 0)
: new Vector2(0, localPosition);
}
}
/// <summary>
/// <see cref="FancyScrollRect{TItemData}"/> のセルを実装するための抽象基底クラス.
/// </summary>
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
/// <seealso cref="FancyScrollRectCell{TItemData, TContext}"/>
public abstract class FancyScrollRectCell<TItemData> : FancyScrollRectCell<TItemData, FancyScrollRectContext>
{
/// <inheritdoc/>
public sealed override void SetContext(FancyScrollRectContext context) => base.SetContext(context);
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 09f137a55810740eab42e24ef242dcfa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,16 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyScrollRect{TItemData, TContext}"/> のコンテキスト基底クラス.
/// </summary>
public class FancyScrollRectContext : IFancyScrollRectContext
{
ScrollDirection IFancyScrollRectContext.ScrollDirection { get; set; }
Func<(float ScrollSize, float ReuseMargin)> IFancyScrollRectContext.CalculateScrollSize { get; set; }
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 91383bd46cee541a7a03e08cfaa47c16
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,16 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// <see cref="FancyScrollRect{TItemData, TContext}"/> のコンテキストインターフェース.
/// </summary>
public interface IFancyScrollRectContext
{
ScrollDirection ScrollDirection { get; set; }
Func<(float ScrollSize, float ReuseMargin)> CalculateScrollSize { get; set; }
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e425de6354b6946c7a3b9f2c807b60fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 691a9f5ee4aec4112a01b9a4332cac51
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,194 +0,0 @@
/*
* EasingCore (https://github.com/setchi/EasingCore)
* Copyright (c) 2020 setchi
* Licensed under MIT (https://github.com/setchi/EasingCore/blob/master/LICENSE)
*/
namespace UnityEngine.UI.Extensions.EasingCore
{
public enum Ease
{
Linear,
InBack,
InBounce,
InCirc,
InCubic,
InElastic,
InExpo,
InQuad,
InQuart,
InQuint,
InSine,
OutBack,
OutBounce,
OutCirc,
OutCubic,
OutElastic,
OutExpo,
OutQuad,
OutQuart,
OutQuint,
OutSine,
InOutBack,
InOutBounce,
InOutCirc,
InOutCubic,
InOutElastic,
InOutExpo,
InOutQuad,
InOutQuart,
InOutQuint,
InOutSine,
}
public delegate float EasingFunction(float t);
public static class Easing
{
/// <summary>
/// Gets the easing function
/// </summary>
/// <param name="type">Ease type</param>
/// <returns>Easing function</returns>
public static EasingFunction Get(Ease type)
{
switch (type)
{
case Ease.Linear: return linear;
case Ease.InBack: return inBack;
case Ease.InBounce: return inBounce;
case Ease.InCirc: return inCirc;
case Ease.InCubic: return inCubic;
case Ease.InElastic: return inElastic;
case Ease.InExpo: return inExpo;
case Ease.InQuad: return inQuad;
case Ease.InQuart: return inQuart;
case Ease.InQuint: return inQuint;
case Ease.InSine: return inSine;
case Ease.OutBack: return outBack;
case Ease.OutBounce: return outBounce;
case Ease.OutCirc: return outCirc;
case Ease.OutCubic: return outCubic;
case Ease.OutElastic: return outElastic;
case Ease.OutExpo: return outExpo;
case Ease.OutQuad: return outQuad;
case Ease.OutQuart: return outQuart;
case Ease.OutQuint: return outQuint;
case Ease.OutSine: return outSine;
case Ease.InOutBack: return inOutBack;
case Ease.InOutBounce: return inOutBounce;
case Ease.InOutCirc: return inOutCirc;
case Ease.InOutCubic: return inOutCubic;
case Ease.InOutElastic: return inOutElastic;
case Ease.InOutExpo: return inOutExpo;
case Ease.InOutQuad: return inOutQuad;
case Ease.InOutQuart: return inOutQuart;
case Ease.InOutQuint: return inOutQuint;
case Ease.InOutSine: return inOutSine;
default: return linear;
}
float linear(float t) => t;
float inBack(float t) => t * t * t - t * Mathf.Sin(t * Mathf.PI);
float outBack(float t) => 1f - inBack(1f - t);
float inOutBack(float t) =>
t < 0.5f
? 0.5f * inBack(2f * t)
: 0.5f * outBack(2f * t - 1f) + 0.5f;
float inBounce(float t) => 1f - outBounce(1f - t);
float outBounce(float t) =>
t < 4f / 11.0f ?
(121f * t * t) / 16.0f :
t < 8f / 11.0f ?
(363f / 40.0f * t * t) - (99f / 10.0f * t) + 17f / 5.0f :
t < 9f / 10.0f ?
(4356f / 361.0f * t * t) - (35442f / 1805.0f * t) + 16061f / 1805.0f :
(54f / 5.0f * t * t) - (513f / 25.0f * t) + 268f / 25.0f;
float inOutBounce(float t) =>
t < 0.5f
? 0.5f * inBounce(2f * t)
: 0.5f * outBounce(2f * t - 1f) + 0.5f;
float inCirc(float t) => 1f - Mathf.Sqrt(1f - (t * t));
float outCirc(float t) => Mathf.Sqrt((2f - t) * t);
float inOutCirc(float t) =>
t < 0.5f
? 0.5f * (1 - Mathf.Sqrt(1f - 4f * (t * t)))
: 0.5f * (Mathf.Sqrt(-((2f * t) - 3f) * ((2f * t) - 1f)) + 1f);
float inCubic(float t) => t * t * t;
float outCubic(float t) => inCubic(t - 1f) + 1f;
float inOutCubic(float t) =>
t < 0.5f
? 4f * t * t * t
: 0.5f * inCubic(2f * t - 2f) + 1f;
float inElastic(float t) => Mathf.Sin(13f * (Mathf.PI * 0.5f) * t) * Mathf.Pow(2f, 10f * (t - 1f));
float outElastic(float t) => Mathf.Sin(-13f * (Mathf.PI * 0.5f) * (t + 1)) * Mathf.Pow(2f, -10f * t) + 1f;
float inOutElastic(float t) =>
t < 0.5f
? 0.5f * Mathf.Sin(13f * (Mathf.PI * 0.5f) * (2f * t)) * Mathf.Pow(2f, 10f * ((2f * t) - 1f))
: 0.5f * (Mathf.Sin(-13f * (Mathf.PI * 0.5f) * ((2f * t - 1f) + 1f)) * Mathf.Pow(2f, -10f * (2f * t - 1f)) + 2f);
float inExpo(float t) => Mathf.Approximately(0.0f, t) ? t : Mathf.Pow(2f, 10f * (t - 1f));
float outExpo(float t) => Mathf.Approximately(1.0f, t) ? t : 1f - Mathf.Pow(2f, -10f * t);
float inOutExpo(float v) =>
Mathf.Approximately(0.0f, v) || Mathf.Approximately(1.0f, v)
? v
: v < 0.5f
? 0.5f * Mathf.Pow(2f, (20f * v) - 10f)
: -0.5f * Mathf.Pow(2f, (-20f * v) + 10f) + 1f;
float inQuad(float t) => t * t;
float outQuad(float t) => -t * (t - 2f);
float inOutQuad(float t) =>
t < 0.5f
? 2f * t * t
: -2f * t * t + 4f * t - 1f;
float inQuart(float t) => t * t * t * t;
float outQuart(float t)
{
var u = t - 1f;
return u * u * u * (1f - t) + 1f;
}
float inOutQuart(float t) =>
t < 0.5f
? 8f * inQuart(t)
: -8f * inQuart(t - 1f) + 1f;
float inQuint(float t) => t * t * t * t * t;
float outQuint(float t) => inQuint(t - 1f) + 1f;
float inOutQuint(float t) =>
t < 0.5f
? 16f * inQuint(t)
: 0.5f * inQuint(2f * t - 2f) + 1f;
float inSine(float t) => Mathf.Sin((t - 1f) * (Mathf.PI * 0.5f)) + 1f;
float outSine(float t) => Mathf.Sin(t * (Mathf.PI * 0.5f));
float inOutSine(float t) => 0.5f * (1f - Mathf.Cos(t * Mathf.PI));
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3b2a35f9ff1c8487582b74620e380486
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,13 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions
{
public enum MovementDirection
{
Left,
Right,
Up,
Down,
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 11769de5e972543c7a4132ceff94a5c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,12 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions
{
public enum MovementType
{
Unrestricted = ScrollRect.MovementType.Unrestricted,
Elastic = ScrollRect.MovementType.Elastic,
Clamped = ScrollRect.MovementType.Clamped
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 585de8f4f272547c89041f94f2ad92b8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,11 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions
{
public enum ScrollDirection
{
Vertical,
Horizontal,
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3392a30c085c642139c92e94ec0d6309
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,597 +0,0 @@
/// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView
using System;
using UnityEngine.EventSystems;
using UnityEngine.UI.Extensions.EasingCore;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// スクロール位置の制御を行うコンポーネント.
/// </summary>
public class Scroller : UIBehaviour, IPointerUpHandler, IPointerDownHandler, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollHandler
{
[SerializeField] RectTransform viewport = default;
/// <summary>
/// ビューポートのサイズ.
/// </summary>
public float ViewportSize => scrollDirection == ScrollDirection.Horizontal
? viewport.rect.size.x
: viewport.rect.size.y;
[SerializeField] ScrollDirection scrollDirection = ScrollDirection.Vertical;
/// <summary>
/// スクロール方向.
/// </summary>
public ScrollDirection ScrollDirection => scrollDirection;
[SerializeField] MovementType movementType = MovementType.Elastic;
/// <summary>
/// コンテンツがスクロール範囲を越えて移動するときに使用する挙動.
/// </summary>
public MovementType MovementType
{
get => movementType;
set => movementType = value;
}
[SerializeField] float elasticity = 0.1f;
/// <summary>
/// コンテンツがスクロール範囲を越えて移動するときに使用する弾力性の量.
/// </summary>
public float Elasticity
{
get => elasticity;
set => elasticity = value;
}
[SerializeField] float scrollSensitivity = 1f;
/// <summary>
/// <see cref="ViewportSize"/> の端から端まで Drag したときのスクロール位置の変化量.
/// </summary>
public float ScrollSensitivity
{
get => scrollSensitivity;
set => scrollSensitivity = value;
}
[SerializeField] bool inertia = true;
/// <summary>
/// 慣性を使用するかどうか. <c>true</c> を指定すると慣性が有効に, <c>false</c> を指定すると慣性が無効になります.
/// </summary>
public bool Inertia
{
get => inertia;
set => inertia = value;
}
[SerializeField] float decelerationRate = 0.03f;
/// <summary>
/// スクロールの減速率. <see cref="Inertia"/> が <c>true</c> の場合のみ有効です.
/// </summary>
public float DecelerationRate
{
get => decelerationRate;
set => decelerationRate = value;
}
[SerializeField] Snap snap = new Snap {
Enable = true,
VelocityThreshold = 0.5f,
Duration = 0.3f,
Easing = Ease.InOutCubic
};
/// <summary>
/// <c>true</c> ならスナップし, <c>false</c>ならスナップしません.
/// </summary>
/// <remarks>
/// スナップを有効にすると, 慣性でスクロールが止まる直前に最寄りのセルへ移動します.
/// </remarks>
public bool SnapEnabled
{
get => snap.Enable;
set => snap.Enable = value;
}
[SerializeField] bool draggable = true;
/// <summary>
/// Drag 入力を受付けるかどうか.
/// </summary>
public bool Draggable
{
get => draggable;
set => draggable = value;
}
[SerializeField] Scrollbar scrollbar = default;
/// <summary>
/// スクロールバーのオブジェクト.
/// </summary>
public Scrollbar Scrollbar => scrollbar;
/// <summary>
/// 現在のスクロール位置.
/// </summary>
/// <value></value>
public float Position
{
get => currentPosition;
set
{
autoScrollState.Reset();
velocity = 0f;
dragging = false;
UpdatePosition(value);
}
}
readonly AutoScrollState autoScrollState = new AutoScrollState();
Action<float> onValueChanged;
Action<int> onSelectionChanged;
Vector2 beginDragPointerPosition;
float scrollStartPosition;
float prevPosition;
float currentPosition;
int totalCount;
bool hold;
bool scrolling;
bool dragging;
float velocity;
[Serializable]
class Snap
{
public bool Enable;
public float VelocityThreshold;
public float Duration;
public Ease Easing;
}
static readonly EasingFunction DefaultEasingFunction = Easing.Get(Ease.OutCubic);
class AutoScrollState
{
public bool Enable;
public bool Elastic;
public float Duration;
public EasingFunction EasingFunction;
public float StartTime;
public float EndPosition;
public Action OnComplete;
public void Reset()
{
Enable = false;
Elastic = false;
Duration = 0f;
StartTime = 0f;
EasingFunction = DefaultEasingFunction;
EndPosition = 0f;
OnComplete = null;
}
public void Complete()
{
OnComplete?.Invoke();
Reset();
}
}
protected override void Start()
{
base.Start();
if (scrollbar)
{
scrollbar.onValueChanged.AddListener(x => UpdatePosition(x * (totalCount - 1f), false));
}
}
/// <summary>
/// スクロール位置が変化したときのコールバックを設定します.
/// </summary>
/// <param name="callback">スクロール位置が変化したときのコールバック.</param>
public void OnValueChanged(Action<float> callback) => onValueChanged = callback;
/// <summary>
/// 選択位置が変化したときのコールバックを設定します.
/// </summary>
/// <param name="callback">選択位置が変化したときのコールバック.</param>
public void OnSelectionChanged(Action<int> callback) => onSelectionChanged = callback;
/// <summary>
/// アイテムの総数を設定します.
/// </summary>
/// <remarks>
/// <paramref name="totalCount"/> を元に最大スクロール位置を計算します.
/// </remarks>
/// <param name="totalCount">アイテムの総数.</param>
public void SetTotalCount(int totalCount) => this.totalCount = totalCount;
/// <summary>
/// 指定した位置まで移動します.
/// </summary>
/// <param name="position">スクロール位置. <c>0f</c> ~ <c>totalCount - 1f</c> の範囲.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
public void ScrollTo(float position, float duration, Action onComplete = null) => ScrollTo(position, duration, Ease.OutCubic, onComplete);
/// <summary>
/// 指定した位置まで移動します.
/// </summary>
/// <param name="position">スクロール位置. <c>0f</c> ~ <c>totalCount - 1f</c> の範囲.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="easing">移動に使用するイージング.</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
public void ScrollTo(float position, float duration, Ease easing, Action onComplete = null) => ScrollTo(position, duration, Easing.Get(easing), onComplete);
/// <summary>
/// 指定した位置まで移動します.
/// </summary>
/// <param name="position">スクロール位置. <c>0f</c> ~ <c>totalCount - 1f</c> の範囲.</param>
/// <param name="duration">移動にかける秒数.</param>
/// <param name="easingFunction">移動に使用するイージング関数.</param>
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
public void ScrollTo(float position, float duration, EasingFunction easingFunction, Action onComplete = null)
{
if (duration <= 0f)
{
Position = CircularPosition(position, totalCount);
onComplete?.Invoke();
return;
}
autoScrollState.Reset();
autoScrollState.Enable = true;
autoScrollState.Duration = duration;
autoScrollState.EasingFunction = easingFunction ?? DefaultEasingFunction;
autoScrollState.StartTime = Time.unscaledTime;
autoScrollState.EndPosition = currentPosition + CalculateMovementAmount(currentPosition, position);
autoScrollState.OnComplete = onComplete;
velocity = 0f;
scrollStartPosition = currentPosition;
UpdateSelection(Mathf.RoundToInt(CircularPosition(autoScrollState.EndPosition, totalCount)));
}
/// <summary>
/// 指定したインデックスの位置までジャンプします.
/// </summary>
/// <param name="index">アイテムのインデックス.</param>
public void JumpTo(int index)
{
if (index < 0 || index > totalCount - 1)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
UpdateSelection(index);
Position = index;
}
/// <summary>
/// <paramref name="sourceIndex"/> から <paramref name="destIndex"/> に移動する際の移動方向を返します.
/// スクロール範囲が無制限に設定されている場合は, 最短距離の移動方向を返します.
/// </summary>
/// <param name="sourceIndex">移動元のインデックス.</param>
/// <param name="destIndex">移動先のインデックス.</param>
/// <returns></returns>
public MovementDirection GetMovementDirection(int sourceIndex, int destIndex)
{
var movementAmount = CalculateMovementAmount(sourceIndex, destIndex);
return scrollDirection == ScrollDirection.Horizontal
? movementAmount > 0
? MovementDirection.Left
: MovementDirection.Right
: movementAmount > 0
? MovementDirection.Up
: MovementDirection.Down;
}
/// <inheritdoc/>
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left)
{
return;
}
hold = true;
velocity = 0f;
autoScrollState.Reset();
}
/// <inheritdoc/>
void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left)
{
return;
}
if (hold && snap.Enable)
{
UpdateSelection(Mathf.RoundToInt(CircularPosition(currentPosition, totalCount)));
ScrollTo(Mathf.RoundToInt(currentPosition), snap.Duration, snap.Easing);
}
hold = false;
}
/// <inheritdoc/>
void IScrollHandler.OnScroll(PointerEventData eventData)
{
if (!draggable)
{
return;
}
var delta = eventData.scrollDelta;
// Down is positive for scroll events, while in UI system up is positive.
delta.y *= -1;
var scrollDelta = scrollDirection == ScrollDirection.Horizontal
? Mathf.Abs(delta.y) > Mathf.Abs(delta.x)
? delta.y
: delta.x
: Mathf.Abs(delta.x) > Mathf.Abs(delta.y)
? delta.x
: delta.y;
if (eventData.IsScrolling())
{
scrolling = true;
}
var position = currentPosition + scrollDelta / ViewportSize * scrollSensitivity;
if (movementType == MovementType.Clamped)
{
position += CalculateOffset(position);
}
if (autoScrollState.Enable)
{
autoScrollState.Reset();
}
UpdatePosition(position);
}
/// <inheritdoc/>
void IBeginDragHandler.OnBeginDrag(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left)
{
return;
}
hold = false;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
viewport,
eventData.position,
eventData.pressEventCamera,
out beginDragPointerPosition);
scrollStartPosition = currentPosition;
dragging = true;
autoScrollState.Reset();
}
/// <inheritdoc/>
void IDragHandler.OnDrag(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left || !dragging)
{
return;
}
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(
viewport,
eventData.position,
eventData.pressEventCamera,
out var dragPointerPosition))
{
return;
}
var pointerDelta = dragPointerPosition - beginDragPointerPosition;
var position = (scrollDirection == ScrollDirection.Horizontal ? -pointerDelta.x : pointerDelta.y)
/ ViewportSize
* scrollSensitivity
+ scrollStartPosition;
var offset = CalculateOffset(position);
position += offset;
if (movementType == MovementType.Elastic)
{
if (offset != 0f)
{
position -= RubberDelta(offset, scrollSensitivity);
}
}
UpdatePosition(position);
}
/// <inheritdoc/>
void IEndDragHandler.OnEndDrag(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left)
{
return;
}
dragging = false;
}
float CalculateOffset(float position)
{
if (movementType == MovementType.Unrestricted)
{
return 0f;
}
if (position < 0f)
{
return -position;
}
if (position > totalCount - 1)
{
return totalCount - 1 - position;
}
return 0f;
}
void UpdatePosition(float position, bool updateScrollbar = true)
{
onValueChanged?.Invoke(currentPosition = position);
if (scrollbar && updateScrollbar)
{
scrollbar.value = Mathf.Clamp01(position / Mathf.Max(totalCount - 1f, 1e-4f));
}
}
void UpdateSelection(int index) => onSelectionChanged?.Invoke(index);
float RubberDelta(float overStretching, float viewSize) =>
(1 - 1 / (Mathf.Abs(overStretching) * 0.55f / viewSize + 1)) * viewSize * Mathf.Sign(overStretching);
void Update()
{
var deltaTime = Time.unscaledDeltaTime;
var offset = CalculateOffset(currentPosition);
if (autoScrollState.Enable)
{
var position = 0f;
if (autoScrollState.Elastic)
{
position = Mathf.SmoothDamp(currentPosition, currentPosition + offset, ref velocity,
elasticity, Mathf.Infinity, deltaTime);
if (Mathf.Abs(velocity) < 0.01f)
{
position = Mathf.Clamp(Mathf.RoundToInt(position), 0, totalCount - 1);
velocity = 0f;
autoScrollState.Complete();
}
}
else
{
var alpha = Mathf.Clamp01((Time.unscaledTime - autoScrollState.StartTime) /
Mathf.Max(autoScrollState.Duration, float.Epsilon));
position = Mathf.LerpUnclamped(scrollStartPosition, autoScrollState.EndPosition,
autoScrollState.EasingFunction(alpha));
if (Mathf.Approximately(alpha, 1f))
{
autoScrollState.Complete();
}
}
UpdatePosition(position);
}
else if (!(dragging || scrolling) && (!Mathf.Approximately(offset, 0f) || !Mathf.Approximately(velocity, 0f)))
{
var position = currentPosition;
if (movementType == MovementType.Elastic && !Mathf.Approximately(offset, 0f))
{
autoScrollState.Reset();
autoScrollState.Enable = true;
autoScrollState.Elastic = true;
UpdateSelection(Mathf.Clamp(Mathf.RoundToInt(position), 0, totalCount - 1));
}
else if (inertia)
{
velocity *= Mathf.Pow(decelerationRate, deltaTime);
if (Mathf.Abs(velocity) < 0.001f)
{
velocity = 0f;
}
position += velocity * deltaTime;
if (snap.Enable && Mathf.Abs(velocity) < snap.VelocityThreshold)
{
ScrollTo(Mathf.RoundToInt(currentPosition), snap.Duration, snap.Easing);
}
}
else
{
velocity = 0f;
}
if (!Mathf.Approximately(velocity, 0f))
{
if (movementType == MovementType.Clamped)
{
offset = CalculateOffset(position);
position += offset;
if (Mathf.Approximately(position, 0f) || Mathf.Approximately(position, totalCount - 1f))
{
velocity = 0f;
UpdateSelection(Mathf.RoundToInt(position));
}
}
UpdatePosition(position);
}
}
if (!autoScrollState.Enable && (dragging || scrolling) && inertia)
{
var newVelocity = (currentPosition - prevPosition) / deltaTime;
velocity = Mathf.Lerp(velocity, newVelocity, deltaTime * 10f);
}
prevPosition = currentPosition;
scrolling = false;
}
float CalculateMovementAmount(float sourcePosition, float destPosition)
{
if (movementType != MovementType.Unrestricted)
{
return Mathf.Clamp(destPosition, 0, totalCount - 1) - sourcePosition;
}
var amount = CircularPosition(destPosition, totalCount) - CircularPosition(sourcePosition, totalCount);
if (Mathf.Abs(amount) > totalCount * 0.5f)
{
amount = Mathf.Sign(-amount) * (totalCount - Mathf.Abs(amount));
}
return amount;
}
float CircularPosition(float p, int size) => size < 1 ? 0 : p < 0 ? size - 1 + (p + 1) % size : p % size;
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 9f0e995f494626a4f878eedbded37c8d
timeCreated: 1487408581
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: