概要
アプリによくあるような、普段は見えないが、スクロール領域を動かしたらフェードインで表示され、スクロールが終わるとフェードアウトで消えていくようなスクロールバーを作った。
ステートパターンで非表示/フェードイン/表示/フェードアウトを切り替えている。
ステートが変わるたびにオブジェクトが生成されるのを避けるため、各ステートは初回で生成してしまっている。
フェード処理、遅延処理にはDoTweenを使用している。
パラメータ
- AutoFadeScrollbar.handle:表示・非表示となる操作用ハンドル画像。Sliding Areaは最初から非表示にしておく
- FadeInState.fadeDuration:フェードインにかかる時間
- ShowState.showDuration:表示し続ける時間
- FadeOutState.fadeDuration:フェードアウトにかかる時間
コード
以下を作成しScrollbarコンポーネントとともにアタッチする。
#region以下はステートの実装部。
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 動かしたときに表示され、自動で消えるスクロールバー
/// </summary>
[RequireComponent(typeof(Scrollbar))]
public class AutoFadeScrollbar : MonoBehaviour
{
[SerializeField] private Scrollbar scrollbar;
[SerializeField] private Image handle;
/// <summary>
/// 初期処理
/// </summary>
private void Awake()
{
var context = new Context(handle);
scrollbar.onValueChanged.AddListener(_ => context.OnScroll());
}
#region ハンドルコンテキストとステートのクラス定義
private enum HandleState
{
Hide,
FadeIn,
Show,
FadeOut
}
/// <summary>
/// コンテキストクラス
/// </summary>
private class Context
{
/// <summary>
/// ハンドル画像
/// </summary>
public readonly Image Image;
private IHandleState currentState;
/// <summary>
/// ステートリスト
/// 毎回newしたくないので状態は保持しておく
/// </summary>
private readonly IHandleState[] stateList = {
new HideState(),
new FadeInState(),
new ShowState(),
new FadeOutState()
};
/// <summary>
/// コンストラクタ
/// ハンドル画像の取得と初期Stateのセット
/// </summary>
/// <param name="image"></param>
public Context(Image image)
{
Image = image;
ChangeState(HandleState.Hide);
}
/// <summary>
/// ハンドル状態の更新
/// </summary>
/// <param name="newState"></param>
public void ChangeState(HandleState newState)
{
currentState = stateList[(int)newState];
currentState.Initialize(this);
}
/// <summary>
/// 動かしたとき各ステートの実行
/// </summary>
public void OnScroll()
{
currentState.OnScroll(this);
}
}
/// <summary>
/// ハンドルの状態を表すインターフェース
/// </summary>
private interface IHandleState
{
/// <summary>
/// ステート遷移時の初期化
/// </summary>
/// <param name="context"></param>
void Initialize(Context context);
/// <summary>
/// 動かしたとき
/// </summary>
/// <param name="context"></param>
void OnScroll(Context context);
}
/// <summary>
/// 非表示状態
/// </summary>
private class HideState : IHandleState
{
private readonly Color clearColor;
/// <summary>
/// コンストラクタ
/// 透明色のキャッシュ
/// </summary>
public HideState()
{
clearColor = Color.white;
clearColor.a = 0;
}
/// <summary>
/// 完全に非表示化
/// </summary>
/// <param name="context"></param>
public void Initialize(Context context)
{
context.Image.color = clearColor;
}
/// <summary>
/// 表示中に切り替え
/// </summary>
/// <param name="context"></param>
public void OnScroll(Context context)
{
context.ChangeState(HandleState.FadeIn);
}
}
/// <summary>
/// フェード表示中
/// </summary>
private class FadeInState : IHandleState
{
// フェード時間
private float fadeDuration = 0.2f;
/// <summary>
/// フェード表示の実行
/// </summary>
/// <param name="context"></param>
public void Initialize(Context context)
{
DOTween.ToAlpha(() => context.Image.color, color => context.Image.color = color, 1, fadeDuration)
.OnComplete(() => context.ChangeState(HandleState.Show));
}
public void OnScroll(Context context)
{
}
}
/// <summary>
/// 表示中
/// </summary>
private class ShowState : IHandleState
{
// 表示し続ける時間
private float showDuration = 3f;
private Tween _tween;
private readonly Color clearColor;
/// <summary>
/// コンストラクタ
/// 表示色のキャッシュ
/// </summary>
public ShowState()
{
clearColor = Color.white;
}
/// <summary>
/// 表示後、徐々にフェードアウトさせる
/// </summary>
/// <param name="context"></param>
public void Initialize(Context context)
{
context.Image.color = clearColor;
_tween = DOVirtual.DelayedCall(showDuration, () => context.ChangeState(HandleState.FadeOut));
}
/// <summary>
/// スクロールされると表示時間をリセット
/// </summary>
/// <param name="context"></param>
public void OnScroll(Context context)
{
_tween.Restart();
}
}
/// <summary>
/// フェードアウト中
/// </summary>
private class FadeOutState : IHandleState
{
private float fadeDuration = 0.4f;
/// <summary>
/// フェードアウトの実行
/// </summary>
/// <param name="context"></param>
public void Initialize(Context context)
{
DOTween.ToAlpha(() => context.Image.color, color => context.Image.color = color, 0, fadeDuration)
.OnComplete(() => context.ChangeState(HandleState.Hide));
}
/// <summary>
/// スクロールすると再表示
/// </summary>
/// <param name="context"></param>
public void OnScroll(Context context)
{
context.ChangeState(HandleState.FadeIn);
}
}
#endregion
}