Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/timdonnelly/Advance

Physics-based animations for iOS, tvOS, and macOS.
https://github.com/timdonnelly/Advance

animation gestures interaction ios motion physics springs swift ui

Last synced: 3 months ago
JSON representation

Physics-based animations for iOS, tvOS, and macOS.

Awesome Lists containing this project

README

        

[![Build Status](https://travis-ci.org/timdonnelly/Advance.svg?branch=master)](https://travis-ci.org/timdonnelly/Advance)
![Swift 5.0](https://img.shields.io/badge/Swift-5.0-orange.svg)
![GitHub release](https://img.shields.io/github/release/timdonnelly/Advance.svg)
[![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-orange.svg)](#swift-package-manager)
[![CocoaPods compatible](https://img.shields.io/cocoapods/v/Advance.svg)](#cocoapods)

# Advance

An animation library for iOS, tvOS, and macOS that uses physics-based animations (including springs) to power interactions that move and respond realistically.

```swift
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

// Springs animate changes to a value
let spring = Spring(initialValue: view.center)

// The `onChange` closure will be called every time the spring updates
spring.onChange = { [view] newCenter in
view.center = newCenter
}

/// The view's center will realistically animate to the new target value.
spring.target = CGPoint(x: 300, y: 200)
```

## Installation

There are several ways to integrate Advance into your project.

* **Manually:** add `Advance.xcodeproj` to your project, then add `Advance-{iOS|macOS|tvOS}.framework` as an "Embedded Binary" to your application target (under General in target settings). From there, add `import Advance` to your code and you're good to go.

* **Carthage:** add `github "timdonnelly/Advance"` to your `Cartfile`.

* **CocoaPods:** add `pod 'Advance'` to your `Podfile`.

* **Swift Package Manager:** add a dependency to your `Project.swift`: `.package(url: "http://github.com/timdonnelly/Advance", from: "3.0.0")`

##### Requirements
* iOS 10+, tvOS 10+, or macOS 10.12+
* Swift 5.0 (Xcode 10.2 or higher)

## Usage

**API documentation is [available here](http://timdonnelly.github.io/Advance/).**

Advance animations are applied on every frame (using `CADisplayLink` on iOS/tvOS, and `CVDisplayLink` on macOS), allowing for fine-grained control at any time.

### `Spring`

`Spring` instances animate changes to a value over time, using spring physics.

```swift
let spring = Spring(initialValue: 0.0)
spring.onChange = { [view] newAlpha in
view.alpha = newAlpha
}

// Off it goes!
spring.target = 0.5
```

#### Configuring a spring

```swift

/// Spring values can be adjusted at any time.
spring.tension = 30.0 /// The strength of the spring
spring.damping = 2.0 /// The resistance (drag) that the spring encounters
spring.threshold = 0.1 /// The maximum delta between the current value and the spring's target (for each component) for which the simulation can enter a converged state.

/// Update the simulation state at any time.
spring.velocity = 6.5
spring.value = 0.2

/// Sets the spring's target and the current simulation value, and removes all velocity. This causes the spring to converge at the given value.
spring.reset(to: 0.5)

```

### `Animator`

`Animator` allows for more flexibility in the types of animation that can be performed, but gives up some convenience
in order to do so. Specifically, animators allow for *any* type of animation or simulation to be performed for a single
value.

```swift
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

/// Animators coordinate animations to drive changes to a value.
let sizeAnimator = Animator(initialValue: view.bounds.size)

sizeAnimator.onChange = { [view] newSize in
view.bounds.size = newSize
}

/// A simple timed animation
sizeAnimator.animate(to: CGSize(width: 123, height: 456), duration: 0.25, timingFunction: .easeInOut)

/// Some time in the future (before the previous timed animation was complete)...

/// Spring physics will move the view's size to the new value, maintaining the velocity from the timed animation.
sizeAnimator.simulate(using: SpringFunction(target: CGSize(width: 300, height: 300)))

/// Some time in the future (before the previous spring animation was complete)...

/// The value will keep the same velocity that it had from the preceeding spring
/// animation, and a decay function will slowly bring movement to a stop.
sizeAnimator.simulate(using: DecayFunction(drag: 2.0))

```

Animators support two fundamentally different types of animations: timed and simulated.

#### Timed animations

Timed animations are, well, timed: they have a fixed duration, and they animate to a final value in a predictable manner.

```swift
animator.animate(to: CGSize(width: 123, height: 456), duration: 0.25, timingFunction: .easeInOut)
```

`TimingFunction` described the pacing of a timed animation.

`TimingFunction` comes with a standard set of functions:

```swift
TimingFunction.linear // No easing
TimingFunction.easeIn
TimingFunction.easeOut
TimingFunction.easeInOut
TimingFunction.swiftOut // Similar to Material Design's default curve
```

Custom timing functions can be expressed as unit beziers ([described here](https://www.w3.org/TR/css-easing-1/#cubic-bzier-timing-function)).

```swift
let customTimingFunction = TimingFunction(x1: 0.1, y1: 0.2, x2: 0.6, y2: 0.0)
```

#### Simulated animations

Simulated animations use a *simulation function* to power a physics-based transition. Simulation functions are types conforming to the `SimulationFunction` protocol.

Simulated animations may be started using two different methods:

```swift
// Begins animating with the custom simulation function, maintaining the previous velocity of the animator.
animator.simulate(using: MyCustomFunction())

// or...

// Begins animating with the custom simulation function, imparting the specified velocity into the simulation.
animator.simulate(using: DecayFunction(), initialVelocity: dragGestureRecognizer.velocity(in: view))
```

### Animating Custom Types

Values conforming to the `VectorConvertible` protocol can be animated by Advance. Conforming types can be converted to and from a `Vector` implementation.
```swift
public protocol VectorConvertible: Equatable, Interpolatable {
associatedtype VectorType: SIMD where VectorType.Scalar == Double
init(vector: VectorType)
var vector: VectorType { get }
}
```

The library adds conformance for many common types through extensions.

## Contributing

If you encounter any issues or surprises, please open an issue.

For suggestions or new features, please consider opening a PR with a functional implementation. Issues may be used if you aren't sure how to implement the change, but working code is typically easier to evaluate.

## License
This project is released under the [BSD 2-clause license](https://github.com/timdonnelly/Advance/blob/master/LICENSE).