Files
Amphlify/Amphlify/UI/Player/SkipButton.swift
T

83 lines
2.8 KiB
Swift

import SwiftUI
struct SkipButton: View {
let backward: Bool
let action: () -> Void
@Environment(\.accessibilityReduceMotion) private var reduceMotion
@State private var skips = 0
var body: some View {
Button {
if !reduceMotion {
skips += 1
}
action()
} label: {
SkipGlyph(skips: skips).scaleEffect(x: backward ? -1 : 1)
}
.accessibilityLabel(backward ? "Previous" : "Next")
}
}
private struct SkipGlyph: View {
let skips: Int
private static let step = 0.455
private nonisolated static let base = UnitPoint(x: 0.078, y: 0.5)
private nonisolated static let tip = UnitPoint(x: 0.45, y: 0.5)
var body: some View {
Image(systemName: "forward.fill")
.hidden()
.overlay {
GeometryReader { proxy in
let shift = proxy.size.width * Self.step
Color.clear.keyframeAnimator(initialValue: 0.0, trigger: skips) { _, time in
Self.triangles(at: time, shift: shift)
} keyframes: { _ in
LinearKeyframe(0.75, duration: 0.75)
MoveKeyframe(0)
}
}
}
.transaction { $0.animation = nil }
.accessibilityHidden(true)
}
private nonisolated static func triangles(at time: Double, shift: CGFloat) -> some View {
let travel = springCurve(time, omega: 12.8, damping: 0.68)
let fade = min(max(time - 0.035, 0) / 0.09, 1)
return ZStack(alignment: .topLeading) {
triangle
.scaleEffect(springCurve(time - 0.035, omega: 12.4, damping: 0.68), anchor: base)
.opacity(time > 0 ? 0.6 + 0.4 * min(time / 0.26, 1) : 1)
.offset(x: max(travel - 1, 0) * shift)
triangle
.offset(x: travel * shift)
triangle
.scaleEffect(1 - fade, anchor: tip)
.opacity(1 - 0.5 * fade)
.offset(x: shift)
}
}
private nonisolated static var triangle: some View {
Image(systemName: "forward.fill").mask { JunctionMask() }
}
}
private nonisolated struct JunctionMask: Shape {
func path(in rect: CGRect) -> Path {
let junction = rect.width * 0.53
let taper = rect.width * 0.04
return Path { path in
path.move(to: CGPoint(x: rect.minX, y: rect.minY))
path.addLine(to: CGPoint(x: junction - taper, y: rect.minY))
path.addLine(to: CGPoint(x: junction, y: rect.midY))
path.addLine(to: CGPoint(x: junction - taper, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
path.closeSubpath()
}
}
}