feat: 动画系统第五阶段(未验收)
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace AibisDream.FrameAnimation.Editor
|
||||
{
|
||||
internal sealed class FrameAnimationPreviewElement : VisualElement
|
||||
{
|
||||
private static Texture2D checkerTexture;
|
||||
private readonly Image image;
|
||||
private readonly Label emptyLabel;
|
||||
private Sprite sprite;
|
||||
private FrameAnimationPreviewZoomMode zoomMode = FrameAnimationPreviewZoomMode.Fit;
|
||||
private float manualZoom = 1f;
|
||||
private Vector2 pan;
|
||||
private Vector2 dragStart;
|
||||
private Vector2 panStart;
|
||||
private bool dragging;
|
||||
|
||||
public FrameAnimationPreviewElement()
|
||||
{
|
||||
AddToClassList("fa-preview");
|
||||
focusable = true;
|
||||
image = new Image { scaleMode = ScaleMode.ScaleToFit, pickingMode = PickingMode.Ignore };
|
||||
image.AddToClassList("fa-preview__image");
|
||||
emptyLabel = new Label("Empty Frame") { pickingMode = PickingMode.Ignore };
|
||||
emptyLabel.AddToClassList("fa-preview__empty");
|
||||
Add(image);
|
||||
Add(emptyLabel);
|
||||
RegisterCallback<GeometryChangedEvent>(_ => UpdateImageLayout());
|
||||
RegisterCallback<PointerDownEvent>(OnPointerDown);
|
||||
RegisterCallback<PointerMoveEvent>(OnPointerMove);
|
||||
RegisterCallback<PointerUpEvent>(OnPointerUp);
|
||||
SetBackground(FrameAnimationPreviewBackground.Checkerboard);
|
||||
}
|
||||
|
||||
public void SetDisplay(Sprite value, bool isEmpty, string emptyText = "Empty Frame")
|
||||
{
|
||||
sprite = value;
|
||||
image.sprite = value;
|
||||
image.style.display = value != null ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
emptyLabel.text = string.IsNullOrEmpty(emptyText) ? "Empty Frame" : emptyText;
|
||||
emptyLabel.style.display = value == null || isEmpty ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
UpdateImageLayout();
|
||||
}
|
||||
|
||||
public void SetBackground(FrameAnimationPreviewBackground background)
|
||||
{
|
||||
switch (background)
|
||||
{
|
||||
case FrameAnimationPreviewBackground.Dark:
|
||||
style.backgroundImage = StyleKeyword.None;
|
||||
style.backgroundColor = new StyleColor(new Color(0.08f, 0.08f, 0.08f));
|
||||
break;
|
||||
case FrameAnimationPreviewBackground.Light:
|
||||
style.backgroundImage = StyleKeyword.None;
|
||||
style.backgroundColor = new StyleColor(new Color(0.72f, 0.72f, 0.72f));
|
||||
break;
|
||||
default:
|
||||
style.backgroundImage = new StyleBackground(GetCheckerTexture());
|
||||
style.backgroundColor = StyleKeyword.Null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetZoom(FrameAnimationPreviewZoomMode mode, float value)
|
||||
{
|
||||
zoomMode = mode;
|
||||
manualZoom = Mathf.Clamp(value, 0.25f, 8f);
|
||||
if (mode == FrameAnimationPreviewZoomMode.Fit)
|
||||
{
|
||||
pan = Vector2.zero;
|
||||
}
|
||||
UpdateImageLayout();
|
||||
}
|
||||
|
||||
private void UpdateImageLayout()
|
||||
{
|
||||
if (sprite == null || resolvedStyle.width <= 0f || resolvedStyle.height <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var sourceSize = sprite.rect.size;
|
||||
if (sourceSize.x <= 0f || sourceSize.y <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var scale = zoomMode == FrameAnimationPreviewZoomMode.Fit
|
||||
? Mathf.Min(resolvedStyle.width / sourceSize.x, resolvedStyle.height / sourceSize.y)
|
||||
: GetScale();
|
||||
var size = sourceSize * Mathf.Max(0.0001f, scale);
|
||||
image.style.width = size.x;
|
||||
image.style.height = size.y;
|
||||
image.style.left = (resolvedStyle.width - size.x) * 0.5f + pan.x;
|
||||
image.style.top = (resolvedStyle.height - size.y) * 0.5f + pan.y;
|
||||
}
|
||||
|
||||
private float GetScale()
|
||||
{
|
||||
return zoomMode switch
|
||||
{
|
||||
FrameAnimationPreviewZoomMode.One => 1f,
|
||||
FrameAnimationPreviewZoomMode.Two => 2f,
|
||||
FrameAnimationPreviewZoomMode.Three => 3f,
|
||||
FrameAnimationPreviewZoomMode.Four => 4f,
|
||||
FrameAnimationPreviewZoomMode.Eight => 8f,
|
||||
FrameAnimationPreviewZoomMode.Manual => manualZoom,
|
||||
_ => 1f
|
||||
};
|
||||
}
|
||||
|
||||
private void OnPointerDown(PointerDownEvent evt)
|
||||
{
|
||||
if (zoomMode == FrameAnimationPreviewZoomMode.Fit || evt.button != 0) return;
|
||||
dragging = true;
|
||||
dragStart = new Vector2(evt.position.x, evt.position.y);
|
||||
panStart = pan;
|
||||
this.CapturePointer(evt.pointerId);
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
private void OnPointerMove(PointerMoveEvent evt)
|
||||
{
|
||||
if (!dragging || !this.HasPointerCapture(evt.pointerId)) return;
|
||||
var position = new Vector2(evt.position.x, evt.position.y);
|
||||
pan = panStart + position - dragStart;
|
||||
UpdateImageLayout();
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
private void OnPointerUp(PointerUpEvent evt)
|
||||
{
|
||||
if (!dragging || !this.HasPointerCapture(evt.pointerId)) return;
|
||||
dragging = false;
|
||||
this.ReleasePointer(evt.pointerId);
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
private static Texture2D GetCheckerTexture()
|
||||
{
|
||||
if (checkerTexture != null) return checkerTexture;
|
||||
const int textureSize = 128;
|
||||
const int cellSize = 8;
|
||||
checkerTexture = new Texture2D(textureSize, textureSize, TextureFormat.RGBA32, false)
|
||||
{
|
||||
name = "Frame Animation Preview Checker",
|
||||
hideFlags = HideFlags.HideAndDontSave,
|
||||
wrapMode = TextureWrapMode.Repeat,
|
||||
filterMode = FilterMode.Point
|
||||
};
|
||||
var light = new Color32(106, 106, 106, 255);
|
||||
var dark = new Color32(78, 78, 78, 255);
|
||||
var pixels = new Color32[textureSize * textureSize];
|
||||
for (var y = 0; y < textureSize; y++)
|
||||
for (var x = 0; x < textureSize; x++)
|
||||
{
|
||||
pixels[y * textureSize + x] = ((x / cellSize) + (y / cellSize)) % 2 == 0 ? light : dark;
|
||||
}
|
||||
checkerTexture.SetPixels32(pixels);
|
||||
checkerTexture.Apply(false, true);
|
||||
return checkerTexture;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user