Add snapshots

This commit is contained in:
Noah Martin 2023-11-30 15:27:35 -03:00
parent 897afcde74
commit 7574e15bb7
14 changed files with 229 additions and 62 deletions

View file

@ -0,0 +1,62 @@
//
// ProgressableAnimation.swift
//
//
// Created by Noah Martin on 11/30/23.
//
import Foundation
import SwiftUI
#if DEBUG
typealias DebugProgressableAnimation = ProgressableAnimation
#else
typealias DebugProgressableAnimation = Animatable
#endif
protocol ProgressableAnimation: Animatable {
var progress: CGFloat { get set }
}
extension ProgressableAnimation where AnimatableData == CGFloat {
var progress: CGFloat {
get { animatableData }
set { animatableData = newValue }
}
}
#if DEBUG
protocol PreviewableAnimation {
associatedtype Animation: ProgressableAnimation & ViewModifier
static var animation: Animation { get }
static var content: any View { get }
}
extension PreviewableAnimation {
static var content: any View {
RoundedRectangle(
cornerRadius: 8,
style: .continuous)
.fill(Color.blue)
.frame(width: 80, height: 80)
}
}
extension PreviewableAnimation {
static var previews: AnyView {
let c = self.content
let anyContent = AnyView(c)
let modifiers = [0, 0.25, 0.5, 0.75, 1].map { i in
var copy = self.animation
copy.progress = i
return copy
}
return AnyView(ForEach(Array(modifiers.enumerated()), id: \.offset) { i, modifier in
anyContent.modifier(modifier)
.previewDisplayName("\(String(describing: Animation.self))-\(i)")
})
}
}
#endif

View file

@ -26,3 +26,5 @@ internal struct Scaled<V: ViewModifier & Animatable>: ViewModifier, Animatable {
content.modifier(base.animation(nil))
}
}
extension Scaled: ProgressableAnimation where V.AnimatableData == CGFloat { }

View file

@ -19,7 +19,7 @@ public extension AnyTransition.MovingParts {
}
}
internal struct Anvil: ViewModifier, Animatable, AnimatableModifier {
internal struct Anvil: ViewModifier, ProgressableAnimation, AnimatableModifier {
var animatableData: CGFloat = 0
#if os(iOS)
@ -31,11 +31,6 @@ internal struct Anvil: ViewModifier, Animatable, AnimatableModifier {
self.animatableData = animatableData
}
var progress: CGFloat {
get { animatableData }
set { animatableData = newValue }
}
func body(content: Content) -> some View {
/// Fraction of the animation spent on the view falling down.
let fall: CGFloat = 0.1
@ -212,6 +207,21 @@ extension EdgeInsets {
}
#if os(iOS) && DEBUG
struct Anvil_Preview: PreviewableAnimation, PreviewProvider {
static var animation: Anvil {
Anvil(animatableData: 0)
}
static var content: any View {
RoundedRectangle(
cornerRadius: 8,
style: .continuous)
.fill(Color.blue)
.frame(width: 80, height: 80)
.preferredColorScheme(.dark)
}
}
@available(iOS 15.0, *)
struct Anvil_Previews: PreviewProvider {
struct Item: Identifiable {

View file

@ -30,7 +30,7 @@ public extension AnyTransition.MovingParts {
}
}
internal struct Blinds: ViewModifier, Animatable, AnimatableModifier, Hashable {
internal struct Blinds: ViewModifier, ProgressableAnimation, AnimatableModifier, Hashable {
var slatWidth: CGFloat
var style: AnyTransition.MovingParts.BlindsStyle
@ -39,11 +39,6 @@ internal struct Blinds: ViewModifier, Animatable, AnimatableModifier, Hashable {
var animatableData: CGFloat
private var progress: CGFloat {
get { animatableData }
set { animatableData = newValue }
}
func body(content: Content) -> some View {
content
.mask {
@ -108,6 +103,13 @@ private struct BlindsShape: Shape {
}
#if os(iOS) && DEBUG
struct Blinds_Preview: PreviewableAnimation & PreviewProvider {
static var animation: Blinds {
Blinds(slatWidth: 20, style: .venetian, isStaggered: false, animatableData: 0)
}
}
@available(iOS 15.0, *)
struct Blinds_Previews: PreviewProvider {
struct Preview: View {

View file

@ -22,7 +22,7 @@ public extension AnyTransition.MovingParts {
}
}
internal struct Blur: ViewModifier, Animatable, AnimatableModifier, Hashable {
internal struct Blur: ViewModifier, DebugProgressableAnimation, AnimatableModifier, Hashable {
var animatableData: CGFloat {
get { radius }
set { radius = newValue }
@ -37,6 +37,12 @@ internal struct Blur: ViewModifier, Animatable, AnimatableModifier, Hashable {
}
#if os(iOS) && DEBUG
struct Blur_Preview: PreviewableAnimation, PreviewProvider {
static var animation: Blur {
Blur(radius: 30)
}
}
@available(iOS 15.0, *)
struct Blur_Previews: PreviewProvider {
struct Preview: View {

View file

@ -21,7 +21,7 @@ public extension AnyTransition.MovingParts {
}
}
internal struct Boing: Animatable, GeometryEffect {
internal struct Boing: DebugProgressableAnimation, GeometryEffect {
var edge: Edge
var animatableData: CGFloat = 0
@ -107,6 +107,12 @@ internal struct Boing: Animatable, GeometryEffect {
}
#if os(iOS) && DEBUG
struct Boing_Preview: PreviewableAnimation, PreviewProvider {
static var animation: Scaled<Boing> {
Scaled(Boing(.top, animatableData: 0))
}
}
@available(iOS 15.0, *)
struct Bounce_Previews: PreviewProvider {
struct Item: Identifiable {

View file

@ -21,7 +21,8 @@ public extension AnyTransition.MovingParts {
}
}
internal struct Clock: ViewModifier, Animatable, AnimatableModifier {
internal struct Clock: ViewModifier, DebugProgressableAnimation, AnimatableModifier {
var origin: UnitPoint
var animatableData: AnimatablePair<CGFloat, CGFloat>
@ -32,7 +33,8 @@ internal struct Clock: ViewModifier, Animatable, AnimatableModifier {
}
var progress: CGFloat {
animatableData.first
get { animatableData.first }
set { animatableData.first = newValue }
}
var blurRadius: CGFloat {
@ -84,6 +86,12 @@ internal struct Clock: ViewModifier, Animatable, AnimatableModifier {
}
#if os(iOS) && DEBUG
struct Clock_Preview: PreviewableAnimation, PreviewProvider {
static var animation: Clock {
Clock(origin: .center, blurRadius: 0, progress: 0)
}
}
struct Clock_Previews: PreviewProvider {
struct Item: Identifiable {
var color: Color

View file

@ -20,14 +20,9 @@ public extension AnyTransition.MovingParts {
}
}
internal struct Snapshot: ViewModifier, Animatable, AnimatableModifier, Hashable {
internal struct Snapshot: ViewModifier, ProgressableAnimation, AnimatableModifier, Hashable {
var animatableData: CGFloat = 0
var progress: CGFloat {
get { animatableData }
set { animatableData = newValue }
}
func body(content: Content) -> some View {
content
.saturation(0.5 + 0.5 * clamp(progress))
@ -38,14 +33,9 @@ internal struct Snapshot: ViewModifier, Animatable, AnimatableModifier, Hashable
}
}
internal struct ExposureFade: ViewModifier, Animatable, AnimatableModifier, Hashable {
internal struct ExposureFade: ViewModifier, ProgressableAnimation, AnimatableModifier, Hashable {
var animatableData: CGFloat = 0
var progress: CGFloat {
get { animatableData }
set { animatableData = newValue }
}
func body(content: Content) -> some View {
content
.opacity(Double(1.0 - pow(2.0, -10.0 * progress)))
@ -55,6 +45,18 @@ internal struct ExposureFade: ViewModifier, Animatable, AnimatableModifier, Hash
}
#if os(iOS) && DEBUG
struct Snapshot_Preview: PreviewableAnimation, PreviewProvider {
static var animation: Snapshot {
Snapshot(animatableData: 0)
}
}
struct FilmExposure_Preview: PreviewableAnimation, PreviewProvider {
static var animation: ExposureFade {
ExposureFade(animatableData: 0)
}
}
@available(iOS 15.0, *)
struct ExoposureFade_Previews: PreviewProvider {
struct Preview: View {

View file

@ -21,16 +21,11 @@ public extension AnyTransition.MovingParts {
}
}
internal struct Flicker: ViewModifier, Animatable, AnimatableModifier, Hashable {
internal struct Flicker: ViewModifier, ProgressableAnimation, AnimatableModifier, Hashable {
var count: Int
var animatableData: CGFloat
private var progress: CGFloat {
get { animatableData }
set { animatableData = newValue }
}
private var isVisible: Bool {
(progress * CGFloat(count)).remainder(dividingBy: 1) >= 0
}
@ -43,6 +38,12 @@ internal struct Flicker: ViewModifier, Animatable, AnimatableModifier, Hashable
}
#if os(iOS) && DEBUG
struct Flicker_Preview: PreviewableAnimation, PreviewProvider {
static var animation: Flicker {
Flicker(count: 1, animatableData: 0)
}
}
@available(iOS 15.0, *)
struct Flicker_Previews: PreviewProvider {
struct Preview: View {

View file

@ -47,7 +47,7 @@ public extension AnyTransition.MovingParts {
}
}
internal struct Glare: ViewModifier, Animatable, AnimatableModifier {
internal struct Glare: ViewModifier, DebugProgressableAnimation, AnimatableModifier {
var animatableData: CGFloat = 0
var angle: Angle
@ -132,8 +132,37 @@ internal struct Glare: ViewModifier, Animatable, AnimatableModifier {
}
#if os(iOS) && DEBUG
@available(iOS 16.0, *)
struct Glare_Preview: PreviewableAnimation, PreviewProvider {
static var animation: Glare {
Glare(.degrees(45), color: .white, increasedBrightness: true, animatableData: 0)
}
static var content: any View {
Glare_Previews.makeRect(start: .indigo, end: .purple)
.frame(width: 100, height: 100)
.preferredColorScheme(.dark)
}
}
@available(iOS 16.0, *)
struct Glare_Previews: PreviewProvider {
static func makeRect(start: Color, end: Color) -> some View {
RoundedRectangle(cornerRadius: 18, style: .continuous)
.fill(LinearGradient(
colors: [start, end],
startPoint: .topLeading,
endPoint: .bottom
))
.compositingGroup()
.overlay {
Text("Hello\nWorld")
.foregroundStyle(.white.shadow(.inner(radius: 0.5)))
}
.font(.system(.largeTitle, design: .rounded).weight(.medium))
.multilineTextAlignment(.center)
}
struct Item: Identifiable {
var color1: Color
var color2: Color
@ -214,19 +243,7 @@ struct Glare_Previews: PreviewProvider {
ForEach(items.indices, id: \.self) { index in
let item = items[index]
RoundedRectangle(cornerRadius: 18, style: .continuous)
.fill(LinearGradient(
colors: [item.color1, item.color2],
startPoint: .topLeading,
endPoint: .bottom
))
.compositingGroup()
.overlay {
Text("Hello\nWorld")
.foregroundStyle(.white.shadow(.inner(radius: 0.5)))
}
.font(.system(.largeTitle, design: .rounded).weight(.medium))
.multilineTextAlignment(.center)
Glare_Previews.makeRect(start: item.color1, end: item.color2)
.transition(
.asymmetric(
insertion: .movingParts.glare(angle: angle),

View file

@ -15,7 +15,7 @@ public extension AnyTransition.MovingParts {
}
}
private struct Iris: ViewModifier, Animatable, AnimatableModifier {
struct Iris: ViewModifier, DebugProgressableAnimation, AnimatableModifier {
var origin: UnitPoint
var blurRadius: CGFloat
@ -58,6 +58,12 @@ private struct Iris: ViewModifier, Animatable, AnimatableModifier {
}
#if os(iOS) && DEBUG
struct Iris_Preview: PreviewableAnimation, PreviewProvider {
static var animation: Iris {
Iris(origin: .center, animatableData: 0)
}
}
@available(iOS 15.0, *)
struct Mask_Previews: PreviewProvider {
struct Preview: View {

View file

@ -16,18 +16,13 @@ public extension AnyTransition.MovingParts {
}
}
private struct Poof: ViewModifier, Animatable, AnimatableModifier {
struct Poof: ViewModifier, ProgressableAnimation, AnimatableModifier {
var animatableData: CGFloat = 0
internal init(animatableData: CGFloat) {
self.animatableData = animatableData
}
var progress: CGFloat {
get { animatableData }
set { animatableData = newValue }
}
func body(content: Content) -> some View {
let frame = (6 * progress).rounded()
@ -56,6 +51,25 @@ private struct Poof: ViewModifier, Animatable, AnimatableModifier {
}
#if os(iOS) && DEBUG
struct Proof_Preview: PreviewableAnimation, PreviewProvider {
static var animation: Poof {
Poof(animatableData: 0)
}
static var content: any View {
ZStack {
RoundedRectangle(cornerRadius: 32, style: .continuous)
.fill(Color.accentColor)
Text("Hello\nWorld!")
.foregroundColor(.white)
.multilineTextAlignment(.center)
.font(.system(.title, design: .rounded))
}
.frame(width: 300, height: 150)
}
}
@available(iOS 15.0, *)
struct Poof_Previews: PreviewProvider {
struct Preview: View {

View file

@ -51,7 +51,7 @@ public extension AnyTransition.MovingParts {
}
@available(iOS 15.0, *)
private struct Pop: AnimatableModifier, Animatable, ViewModifier {
struct Pop: AnimatableModifier, ProgressableAnimation, ViewModifier {
var animatableData: CGFloat = 0
var style: AnyShapeStyle
@ -63,11 +63,6 @@ private struct Pop: AnimatableModifier, Animatable, ViewModifier {
self.style = style
}
var progress: CGFloat {
get { animatableData }
set { animatableData = newValue }
}
func body(content: Content) -> some View {
let t = clamp(2 * (progress - 1/2.5))
@ -170,6 +165,19 @@ private struct Pop: AnimatableModifier, Animatable, ViewModifier {
}
#if os(iOS) && DEBUG
struct Pop_Preview: PreviewableAnimation, PreviewProvider {
static var animation: Pop {
Pop(style: AnyShapeStyle(.tint), animatableData: 0)
}
static var content: any View {
Image(systemName: "heart.fill")
.foregroundColor(.red)
.tint(.red)
.preferredColorScheme(.dark)
}
}
@available(iOS 15.0, *)
struct Pop_Previews: PreviewProvider {
struct Preview: View {

View file

@ -29,7 +29,7 @@ public extension AnyTransition.MovingParts {
}
}
internal struct Skid: Animatable, GeometryEffect {
internal struct Skid: DebugProgressableAnimation, GeometryEffect {
var direction: AnyTransition.MovingParts.SkidDirection
var animatableData: CGFloat = 0
@ -73,6 +73,29 @@ internal struct Skid: Animatable, GeometryEffect {
}
#if os(iOS) && DEBUG
struct Skid_Preview: PreviewableAnimation, PreviewProvider {
static var animation: Skid {
Skid(.leading)
}
static var content: some View {
RoundedRectangle(cornerRadius: 8, style: .continuous)
.fill(Color.orange)
.overlay {
Text("Jell-O\nWorld")
.blendMode(.difference)
.offset(x: 2, y: 2)
}
.compositingGroup()
.overlay {
Text("Jell-O\nWorld")
}
.font(.system(.headline, design: .rounded).weight(.black))
.multilineTextAlignment(.center)
.frame(width: 150, height: 150)
}
}
@available(iOS 15.0, *)
struct Skid_Previews: PreviewProvider {
struct Item: Identifiable {