Created
October 21, 2019 12:34
-
-
Save chriseidhof/5429ec66ae5161d5998aa4ab960998c0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ContentView.swift | |
// KeyFrameAnimation | |
// | |
// Created by Chris Eidhof on 21.10.19. | |
// Copyright © 2019 objc.io. All rights reserved. | |
// | |
import SwiftUI | |
extension CGSize { | |
static func *(lhs: CGSize, rhs: CGFloat) -> CGSize { | |
return CGSize(width: lhs.width * rhs, height: lhs.height * rhs) | |
} | |
} | |
extension VectorArithmetic { | |
func scaled(by amount: Double) -> Self { | |
var result = self | |
result.scale(by: amount) | |
return result | |
} | |
} | |
extension CGSize: VectorArithmetic { | |
public static func -= (lhs: inout CGSize, rhs: CGSize) { | |
lhs = lhs - rhs | |
} | |
public static func - (lhs: CGSize, rhs: CGSize) -> CGSize { | |
CGSize(width: lhs.width - rhs.width, height: lhs.height - rhs.height) | |
} | |
public static func += (lhs: inout CGSize, rhs: CGSize) { | |
lhs = lhs + rhs | |
} | |
public mutating func scale(by rhs: Double) { | |
width *= CGFloat(rhs) | |
height *= CGFloat(rhs) | |
} | |
public var magnitudeSquared: Double { | |
Double((width*width) + (height*height)) | |
} | |
public static func + (lhs: CGSize, rhs: CGSize) -> CGSize { | |
return CGSize(width: lhs.width + rhs.width, height: lhs.height + rhs.height) | |
} | |
} | |
struct Keyframed<D: VectorArithmetic> { | |
var start: D | |
var keyframes: [(position: Double, value: D)] = [] | |
var end: D | |
func value(position: Double) -> D { | |
if position >= 1 { return end } | |
var lastValue = start | |
var lastPosition: Double = 0 | |
var frames = keyframes | |
frames.append((1, end)) | |
for frame in frames { | |
if position < frame.position { | |
let amount = (position - lastPosition) / (frame.position - lastPosition) | |
return lastValue + (frame.value - lastValue).scaled(by: amount) | |
} else { | |
lastValue = frame.value | |
lastPosition = frame.position | |
} | |
} | |
return end | |
} | |
} | |
struct KeyframeAnimation: AnimatableModifier { | |
var progress: Double = 0 | |
var values = Keyframed<CGSize>(start: .zero, keyframes: [(position: 0.5, value: CGSize(width: 100, height: 0))], end: CGSize(width: 100, height: 100)) | |
var animatableData: Double { | |
get { progress } | |
set { progress = newValue } | |
} | |
func body(content: Content) -> some View { | |
let offset = values.value(position: progress) | |
print(progress, offset) | |
return content.offset(offset) | |
} | |
} | |
struct ContentView: View { | |
@State var progress: Double = 0 | |
var body: some View { | |
Text("Hello World") | |
.modifier(KeyframeAnimation(progress: progress)) | |
.animation(Animation.default.speed(0.2)) | |
.onAppear { self.progress = 1 } | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment