{"id":14961275,"url":"https://github.com/jtrivedi/wave","last_synced_at":"2025-05-14T13:05:59.295Z","repository":{"id":38307418,"uuid":"498385616","full_name":"jtrivedi/Wave","owner":"jtrivedi","description":"Wave is a spring-based animation engine for iOS and macOS that makes it easy to create fluid, interruptible animations that feel great.","archived":false,"fork":false,"pushed_at":"2025-02-17T19:48:42.000Z","size":8263,"stargazers_count":2118,"open_issues_count":11,"forks_count":62,"subscribers_count":15,"default_branch":"main","last_synced_at":"2025-04-12T20:43:27.360Z","etag":null,"topics":["animation","appkit","gestures","interaction-design","ios","motion","swift","swiftui","ui","uikit"],"latest_commit_sha":null,"homepage":"https://jtrivedi.github.io/Wave/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jtrivedi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-31T15:08:10.000Z","updated_at":"2025-04-11T09:50:36.000Z","dependencies_parsed_at":"2025-02-28T22:11:35.428Z","dependency_job_id":"fd126e2d-cc56-418a-b294-2b14d0e01baa","html_url":"https://github.com/jtrivedi/Wave","commit_stats":{"total_commits":45,"total_committers":1,"mean_commits":45.0,"dds":0.0,"last_synced_commit":"27b7e1d2d42b8a4f56d9e84213910b0441c8b810"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtrivedi%2FWave","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtrivedi%2FWave/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtrivedi%2FWave/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtrivedi%2FWave/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jtrivedi","download_url":"https://codeload.github.com/jtrivedi/Wave/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254149952,"owners_count":22022851,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animation","appkit","gestures","interaction-design","ios","motion","swift","swiftui","ui","uikit"],"created_at":"2024-09-24T13:24:21.044Z","updated_at":"2025-05-14T13:05:59.269Z","avatar_url":"https://github.com/jtrivedi.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg width=\"400\" src=\"./Assets/Logo.png\"\u003e\n\u003c/p\u003e\n\n## Wave\n\nWave is a spring-based animation engine for iOS, iPadOS, and macOS. It makes it easy to create fluid, interactive, and interruptible animations that feel great.\n\nWave has no external dependencies, and can be easily dropped into existing UIKit, SwiftUI, or AppKit based projects and apps.\n\nThe core feature of Wave is that all animations are _re-targetable_, meaning that you can change an animation’s destination value in-flight, and the animation will gracefully _redirect_ to that new value.\n\n- [Understanding Retargeting](#features)\n- [Installation](#installation)\n- [Documentation](#documentation)\n- [Getting Started](#getting-started)\n    - [Block-Based Animation](#block-based-animation)\n    - [Property-Based Animation](#property-based-animation)\n- [Example Code](#example-code)\n\n#### Understanding Retargeting\n\nConsider these demos of the iOS Picture-in-Picture feature. The screen on the left is created with standard UIKit animations, and the one on the right is created with Wave.\n\nThough both are “interruptible”, the Wave-based implementation handles the interruption much better, and fluidly _arcs_ to its new destination. The UIKit animation feels stiff and jerky in comparison.\n\nAt its core, [retargeting](https://developer.apple.com/videos/play/wwdc2018/803/) is the process of preserving an animation’s velocity even as its target changes, which Wave does automatically.\n\n![Demo](./Assets/Retargeting.gif)\n\n\n### Installation\n\nAdd Wave to your app's `Package.swift` file, or selecting `File -\u003e Add Packages` in Xcode:\n\n```swift\n.package(url: \"https://github.com/jtrivedi/Wave\")\n```\n\nIf you clone the repo, you can run the sample app, which contains a few interactive demos to understand what Wave provides.\n\nNote: To enable high frame-rate animations on ProMotion devices (i.e. 120 fps animation), you'll need to add a key/value pair in your `Info.plist`. Set the key `CADisableMinimumFrameDuration` to `true`. Without this entry, animations will be capped at 60 fps.\n\n### Documentation\n\nThere’s a full Wave [documentation site](https://jtrivedi.github.io/Wave/) available for full API and usage documentation.\n\n### Getting Started\n\nThere are two ways you can interact with Wave, depending on your needs: the block-based and property-based animations:\n\n#### Block-Based Animation\n\nThe easiest way to get started is by using Wave’s block-based APIs that resemble the `UIView.animateWithDuration()` APIs.\n\nThis API lets you animate several common UIView and CALayer properties, like `frame`, `center`, `scale`, `backgroundColor`, and more.\n\nFor these supported properties, Wave will create, manage, and execute the required spring animations under-the-hood.\n\nFor example, animating the above PiP view to its final destination is extremely simple:\n\n```swift\nif panGestureRecognizer.state == .ended {\n\n    // Create a spring with some bounciness. `response` affects the animation's duration.\n    let animatedSpring = Spring(dampingRatio: 0.68, response: 0.80)\n\n    // Get the gesture's lift-off velocity, and pass it into the Wave animation.\n    let gestureVelocity = panGestureRecognizer.velocity(in: view)\n\n    Wave.animate(withSpring: animatedSpring, gestureVelocity: gestureVelocity) {\n        // Update animatable properties on the view's `animator` property, _not_ the view itself.\n        pipView.animator.center = pipViewDestination     // Some target CGPoint that you calculate.\n        pipView.animator.scale = CGPoint(x: 1.1, y: 1.1)\n    }\n}\n```\n\nNote that at _any_ time, you can _retarget_ the view’s `center` property to somewhere else, and it’ll gracefully animate.\n\n##### Supported Animatable Properties\n\nThe block-based API currently supports animating the following properties. For other properties, you can use the property-based animation API below.\n\n* `frame`\n* `bounds`\n* `center`\n* `origin`\n* `alpha`\n* `backgroundColor`\n* `cornerRadius`\n* `scale`\n* `translation`\n* `shadowColor/radius/offset/opacity`\n* `borderColor/borderWidth`\n\nUpcoming properties:\n\n* `rotation`\n\n#### Property-Based Animation\n\nWhile the block-based API is often most convenient, you may want to animate something that the block-based API doesn’t yet support (e.x. rotation). Or, you may want the flexibility of getting the intermediate spring values and driving an animation yourself (e.x. a progress value).\n\nFor example, to draw the orange path of the PiP demo, we need to know the value of every `CGPoint` from the view’s initial center, to its destination center:\n\n```swift\n// When the gesture ends, create a `CGPoint` animator from the PiP view's initial center, to its target.\n// The `valueChanged` callback provides the intermediate locations of the callback, allowing us to draw the path.\n\nlet positionAnimator = SpringAnimator\u003cCGPoint\u003e(spring: animatedSpring)\npositionAnimator.value = pipView.center       // The presentation value\npositionAnimator.target = pipViewDestination  // The target value\npositionAnimator.velocity = gestureVelocity\n\npositionAnimator.valueChanged = { [weak self] location in\n    self?.drawPathPoint(at: location)\n}\n\npositionAnimator.start()\n```\n\n\n\n##### Completion Blocks\n\nBoth the block-based and property-based APIs support completion blocks. If an animation completes fully, the completion block’s `finished` flag will be true.\n\nHowever, if an animation’s target was changed in-flight (“retargeted”), `finished` will be false, while `retargeted` will be true.\n\n```swift\nWave.animate(withSpring: Spring.defaultAnimated) {\n    myView.animator.backgroundColor = .systemBlue\n} completion: { finished, retargeted in\n    print(finished, retargeted)\n}\n```\n\n### Example Code\n\nExploring the provided sample app is a great way to get started with Wave.\n\nSimply open the `Wave-Sample` Xcode project and hit “Run”. The full source code for the Picture-in-Picture demo is available there, too!\n\n### Acknowledgements\n\nSpecial thanks to [Ben Oztalay](https://github.com/boztalay) for helping architect the underlying physics of Wave!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjtrivedi%2Fwave","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjtrivedi%2Fwave","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjtrivedi%2Fwave/lists"}