diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..767df98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.mode1 +*.mode1v3 +*.mode2v3 +*.perspective +*.perspectivev3 +*.pbxuser +*.xcworkspace +*.framework/ +xcuserdata +examples/iOS/Pods/ +src/iOS/Pods/ +src/Android/build/ +examples/Android/build/ +**/.idea +**/.vscode diff --git a/README.md b/README.md new file mode 100644 index 0000000..f1aa1c2 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# 动效曲线 + +> [en-US](./README_en.md) + +动效曲线SDK 由一系列的动效曲线函数构成,为了解决开发动效的成本高、效果和设计的预期不一致的问题,每个动效曲线函数可应用于任意一个可动效的属性上,通过改变动效过程中的速率和方向给用户带来更好的体验,目前包括线性曲线、加速曲线、减速曲线、余弦曲线、过度曲线、预期曲线、标准曲线(三次贝塞尔曲线)、弹跳曲线、弹性曲线等,目前支持 iOS 和 Android; + +[语雀知识库地址](https://www.yuque.com/youku-gaia/gaia-motion-curve) + +`iOS`和`Android`均用同一套动效曲线的算法实现,保证双端的动效的一致性,iOS 上层封装使用的是`Core Animation`,Android 则是使用的`插值器`。 + +## iOS + +#### 安装 + +```ruby +pod 'GaiaMotionCurve', '0.1.0' +``` + +#### 头文件 + +```objc +#import +``` + +#### 例子 + +```objc + +#import + +NSMutableArray *animationModels = [[NSMutableArray alloc] init]; +GMCModel *model1 =[GMCModel modelWithKeyPath:@"opacity" + duration:0.2 + delay:0 + curveType:MCXCurveTypeStandard + fromValue:[NSValue gmc_valueWithCGFloat:0] + toValue:[NSValue gmc_valueWithCGFloat:0.9]]; +[animationModels addObject:model1]; +[_tipsImageView.layer gmc_animateWithAnimationModels:animationModels completion:^(BOOL finished) {}]; + +``` + +#### [完整iOS Demo 工程](./examples/iOS) + + +## Android + +#### 安装 + +引入编译的aar文件 + +> 目录:src/Android/build/outputs + +#### 包名 + +```java + +import com.gaia.MotionCurve.*; + +``` + +#### 例子 + +```java + +TranslateAnimation animation = new TranslateAnimation(0, displaySize.x - maxTextWidth - 2 * margin, 0, 0); +animation.setFillAfter(true); +animation.setDuration(ANIMATION_DURATION); +animation.setInterpolator(new MotionCurveXStandardInterpolator()); +view.startAnimation(animation); + +``` + +#### [完整Android Demo 工程](./examples/Android) + +## 曲线分类 + +* [线性曲线](./docs/zh-CN/linear.md) +* [加速曲线](./docs/zh-CN/accelerate.md) +* [减速曲线](./docs/zh-CN/decelerate.md) +* [标准曲线](./docs/zh-CN/standard.md) +* [预期曲线](./docs/zh-CN/anticipate.md) +* [过度曲线](./docs/zh-CN/overshoot.md) +* [弹性曲线](./docs/zh-CN/spring.md) +* [弹跳曲线](./docs/zh-CN/bounce.md) +* [余弦曲线](./docs/zh-CN/cosine.md) + +# 行为准则 + +请参考[Alibaba Open Source Code of Conduct](https://github.com/AlibabaDR/community/blob/master/CODE_OF_CONDUCT.md) ([中文版](https://github.com/AlibabaDR/community/blob/master/CODE_OF_CONDUCT_zh.md)). + + +# 开源协议 + +gaia-motion-curve is licensed under the Apache License, Version 2.0. See LICENSE for the full license text. + diff --git a/README_en.md b/README_en.md new file mode 100644 index 0000000..c60ab83 --- /dev/null +++ b/README_en.md @@ -0,0 +1,90 @@ +# Motion Curve + +The Motion Curve SDK consists of a series of dynamic curve functions. In order to solve the problems of high cost of developing dynamic effects and inconsistent effects and design expectations, each dynamic curve function can be applied to any dynamic attribute. Bring a better experience to users by changing the speed and direction in the animation process, currently including linear curve, acceleration curve, deceleration curve, cosine curve, transition curve, expected curve, standard curve (cubic Bezier curve), bounce Curves, elastic curves, etc., currently support iOS and Android; + +[YuQue](https://www.yuque.com/youku-gaia/gaia-motion-curve) + +Both `iOS` and `Android` are implemented with the same set of motion curve algorithms to ensure the consistency of motion effects at both ends. The upper layer package of iOS uses `Core Animation`, and Android uses `Interpolator`. + +## iOS + +#### Install + +```ruby +pod 'GaiaMotionCurve', '0.1.0' +``` + +#### Header + +```objc +#import +``` + +#### Example + +```objc +#import + +NSMutableArray *animationModels = [[NSMutableArray alloc] init]; +GMCModel *model1 =[GMCModel modelWithKeyPath:@"opacity" + duration:0.2 + delay:0 + curveType:MCXCurveTypeStandard + fromValue:[NSValue gmc_valueWithCGFloat:0] + toValue:[NSValue gmc_valueWithCGFloat:0.9]]; +[animationModels addObject:model1]; +[_tipsImageView.layer gmc_animateWithAnimationModels:animationModels completion:^(BOOL finished) {}]; + +``` + +#### [Full iOS Demo Project](./examples/iOS) + + +## Android + +#### Install + +import compiled aar file + +> Directory:src/Android/build/outputs + +#### Package + +```java +import com.gaia.MotionCurve.*; +``` + +#### Example + +```java +TranslateAnimation animation = new TranslateAnimation(0, displaySize.x - maxTextWidth - 2 * margin, 0, 0); +animation.setFillAfter(true); +animation.setDuration(ANIMATION_DURATION); +animation.setInterpolator(new MotionCurveXStandardInterpolator()); +view.startAnimation(animation); + +``` + +#### [Full Android Demo Project](./examples/Android) + +## Motion Curve Categories + +* [Linear](./docs/en-US/linear.md) +* [Accelerate](./docs/en-US/accelerate.md) +* [Decelerate](./docs/en-US/decelerate.md) +* [Standard](./docs/en-US/standard.md) +* [Anticipate](./docs/en-US/anticipate.md) +* [Overshoot](./docs/en-US/overshoot.md) +* [Spring](./docs/en-US/spring.md) +* [Bounce](./docs/en-US/bounce.md) +* [Cosine](./docs/en-US/cosine.md) + +# Code of Conduct + +Please refer to [Alibaba Open Source Code of Conduct](https://github.com/AlibabaDR/community/blob/master/CODE_OF_CONDUCT.md). + + +# LICENCE + +gaia-motion-curve is licensed under the Apache License, Version 2.0. See LICENSE for the full license text. + diff --git a/docs/assets/accelerate.gif b/docs/assets/accelerate.gif new file mode 100644 index 0000000..b7f1c14 Binary files /dev/null and b/docs/assets/accelerate.gif differ diff --git a/docs/assets/anticipate.gif b/docs/assets/anticipate.gif new file mode 100644 index 0000000..70cc137 Binary files /dev/null and b/docs/assets/anticipate.gif differ diff --git a/docs/assets/bounce.gif b/docs/assets/bounce.gif new file mode 100644 index 0000000..27afb1d Binary files /dev/null and b/docs/assets/bounce.gif differ diff --git a/docs/assets/cosine.gif b/docs/assets/cosine.gif new file mode 100644 index 0000000..cccbc52 Binary files /dev/null and b/docs/assets/cosine.gif differ diff --git a/docs/assets/decelerate.gif b/docs/assets/decelerate.gif new file mode 100644 index 0000000..610850e Binary files /dev/null and b/docs/assets/decelerate.gif differ diff --git a/docs/assets/linear.gif b/docs/assets/linear.gif new file mode 100644 index 0000000..04d43a9 Binary files /dev/null and b/docs/assets/linear.gif differ diff --git a/docs/assets/overshoot.gif b/docs/assets/overshoot.gif new file mode 100644 index 0000000..f9a7fca Binary files /dev/null and b/docs/assets/overshoot.gif differ diff --git a/docs/assets/spring.gif b/docs/assets/spring.gif new file mode 100644 index 0000000..31aaaa1 Binary files /dev/null and b/docs/assets/spring.gif differ diff --git a/docs/assets/standard.gif b/docs/assets/standard.gif new file mode 100644 index 0000000..ca1cf16 Binary files /dev/null and b/docs/assets/standard.gif differ diff --git a/docs/en-US/accelerate.md b/docs/en-US/accelerate.md new file mode 100644 index 0000000..03e2581 --- /dev/null +++ b/docs/en-US/accelerate.md @@ -0,0 +1,93 @@ +# Accelerate + +## Demo + + + +## iOS + +Enumerate:GMCCurveTypeAccelerate + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeAccelerate + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +Interpolator:GaiaMotionCurveAccelerateInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveAccelerateInterpolator()); + animationView.startAnimation(animation); +``` + +## Customizable Parameters + +Configurable Parameters: + +``` +x1: x coordinate of control point x1 +y1: y coordinate of control point y1 +x2: x coordinate of control point x2 +y2: y coordinate of control point y2 +``` + +Default: + +``` +x1 = 0.4 +y1 = 0.0 +x2 = 1.0 +y2 = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeAccelerate + customArgs:@{@"x1":@0.4,@"y1":@0.0,@"x2":@1.0,@"y2":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveAccelerateInterpolator(0.4, 0.0, 1.0, 1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/en-US/anticipate.md b/docs/en-US/anticipate.md new file mode 100644 index 0000000..d291088 --- /dev/null +++ b/docs/en-US/anticipate.md @@ -0,0 +1,104 @@ +# Anticipate + +## Demo + + + +## iOS + +Enumerate:GMCCurveTypeAnticipate + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeAnticipate + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +Interpolator:GaiaMotionCurveAnticipateInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveAnticipateInterpolator()); + animationView.startAnimation(animation); +``` + +## Customizable Parameters + +Configurable Parameters: + +``` + d: damping, controls the height of the highest point +x1: x coordinate of control point x1 +y1: y coordinate of control point y1 +x2: x coordinate of control point x2 +y2: y coordinate of control point y2 +x3: x coordinate of control point x3 +y3: y coordinate of control point y3 +x4: x coordinate of control point x4 +y4: y coordinate of control point y4 +``` + +Default: + +``` + d = 0.2 +x1 = 0.33 +y1 = 0.0 +x2 = 0.67 +y2 = 1.0 +x3 = 0.33 +y3 = 0.0 +x4 = 0.2 +y4 = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeAnticipate + customArgs:@{@"d":@0.2,@"x1":@0.33,@"y1":@0.0,@"x2":@0.67,@"y2":@1.0, + @"x3":@0.33,@"y3":@0.0,@"x4":@0.2,@"y4":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveAnticipateInterpolator(0.2,0.33,0.0,0.67,1.0,0.33,0.0,0.2,1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/en-US/bounce.md b/docs/en-US/bounce.md new file mode 100644 index 0000000..5f3a2f8 --- /dev/null +++ b/docs/en-US/bounce.md @@ -0,0 +1,89 @@ +# Bounce + +## Demo + + + +## iOS + +Enumerate:GMCCurveTypeBounce + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeBounce + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +Interpolator:GaiaMotionCurveBounceInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveBounceInterpolator()); + animationView.startAnimation(animation); +``` + +## Customizable Parameters + +Configurable Parameters: + +``` +v:velocity +d:damping +``` + +Default: + +``` +v = 1.0 +d = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeBounce + customArgs:@{@"v":@1.0,@"d":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveBounceInterpolator(1.0, 1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/en-US/cosine.md b/docs/en-US/cosine.md new file mode 100644 index 0000000..fb4ce8e --- /dev/null +++ b/docs/en-US/cosine.md @@ -0,0 +1,89 @@ +# Cosine + +## Demo + + + +## iOS + +Enumerate:GMCCurveTypeCosine + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeCosine + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +Interpolator:GaiaMotionCurveCosineInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveCosineInterpolator()); + animationView.startAnimation(animation); +``` + +## Customizable Parameters + +Configurable Parameters: + +``` +d:damping +c:cycles, multiples of 0.25 +``` + +Default: + +``` +d = 1.0 +c = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeCosine + customArgs:@{@"d":@1.0,@"c":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveCosineInterpolator(1.0, 1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/en-US/decelerate.md b/docs/en-US/decelerate.md new file mode 100644 index 0000000..60d518c --- /dev/null +++ b/docs/en-US/decelerate.md @@ -0,0 +1,93 @@ +# Decelerate + +## Demo + + + +## iOS + +Enumerate:GMCCurveTypeDecelerate + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeDecelerate + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +Interpolator:GaiaMotionCurveDecelerateInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveDecelerateInterpolator()); + animationView.startAnimation(animation); +``` + +## Customizable Parameters + +Configurable Parameters: + +``` +x1: x coordinate of control point x1 +y1: y coordinate of control point y1 +x2: x coordinate of control point x2 +y2: y coordinate of control point y2 +``` + +Default: + +``` +x1 = 0.0 +y1 = 0.0 +x2 = 0.1 +y2 = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeDecelerate + customArgs:@{@"x1":@0.0,@"y1":@0.0,@"x2":@0.1,@"y2":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveDecelerateInterpolator(0.0, 0.0, 0.1, 1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/en-US/linear.md b/docs/en-US/linear.md new file mode 100644 index 0000000..7ae8cc2 --- /dev/null +++ b/docs/en-US/linear.md @@ -0,0 +1,46 @@ +# Linear + +## Demo + + + +## iOS + +Enumerate:GMCCurveTypeLinear + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeLinear + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +Interpolator:GaiaMotionCurveLinearInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveLinearInterpolator()); + animationView.startAnimation(animation); +``` + +## Customizable Parameters + +None diff --git a/docs/en-US/overshoot.md b/docs/en-US/overshoot.md new file mode 100644 index 0000000..661ccfb --- /dev/null +++ b/docs/en-US/overshoot.md @@ -0,0 +1,106 @@ +# Overshoot + +## Demo + + + + + +## iOS + +Enumerate:GMCCurveTypeOvershoot + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeOvershoot + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +Interpolator:GaiaMotionCurveOvershootInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveOvershootInterpolator()); + animationView.startAnimation(animation); +``` + +## Customizable Parameters + +Configurable Parameters: + +``` + d: damping, controls the height of the highest point +x1: x coordinate of control point x1 +y1: y coordinate of control point y1 +x2: x coordinate of control point x2 +y2: y coordinate of control point y2 +x3: x coordinate of control point x3 +y3: y coordinate of control point y3 +x4: x coordinate of control point x4 +y4: y coordinate of control point y4 +``` + +Default: + +``` + d = 1.2 +x1 = 0.33 +y1 = 0.0 +x2 = 0.3 +y2 = 1.0 +x3 = 0.33 +y3 = 0.0 +x4 = 0.5 +y4 = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeOvershoot + customArgs:@{@"d":@1.2,@"x1":@0.33,@"y1":@0.0,@"x2":@0.3,@"y2":@1.0, + @"x3":@0.33,@"y3":@0.0,@"x4":@0.5,@"y4":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveOvershootInterpolator(1.2,0.33,0.0,0.3,1.0,0.33,0.0,0.5,1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/en-US/spring.md b/docs/en-US/spring.md new file mode 100644 index 0000000..6d2e495 --- /dev/null +++ b/docs/en-US/spring.md @@ -0,0 +1,132 @@ +# Spring + +## Demo + + + +## iOS + +Enumerate:GMCCurveTypeSpring + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeSpring + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +Interpolator:GaiaMotionCurveSpringInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveSpringInterpolator()); + animationView.startAnimation(animation); +``` + +## Customizable Parameters + +Configurable Parameters: + +Spring curve supports two parameter systems, Mass and RK4 (Android only supports Mass system temporarily) + +**Mass System:** + +``` +m: mass +s: stiffness +d: damping +iv:velocity +``` + +Default: + +``` +m = 0.7 +s = 380.0 +d = 10.0 +iv = -2.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeSpring + customArgs:@{@"m":@1.0,@"s":@380.0,@"d":@10.0,@"iv":@-2.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveSpringInterpolator(0.7, 380.0, 10.0, -2.0)); + animationView.startAnimation(animation); +``` + +**RK4 System:** + +``` +t: tension +f: friction +v: velocity +``` + +Default: + +``` +t = 533.0 +f = 14.0 +v = -2.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeSpring + customArgs:@{@"t":@533.0,@"f":@14.0,@"v":@-2.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + diff --git a/docs/en-US/standard.md b/docs/en-US/standard.md new file mode 100644 index 0000000..e9796a5 --- /dev/null +++ b/docs/en-US/standard.md @@ -0,0 +1,93 @@ +# Standard + +## Demo + + + +## iOS + +Enumerate:GMCCurveTypeStandard + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeStandard + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +Interpolator:GaiaMotionCurveStandardInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveStandardInterpolator()); + animationView.startAnimation(animation); +``` + +## Customizable Parameters + +Configurable Parameters: + +``` +x1: x coordinate of control point x1 +y1: y coordinate of control point y1 +x2: x coordinate of control point x2 +y2: y coordinate of control point y2 +``` + +Default: + +``` +x1 = 0.4 +y1 = 0.0 +x2 = 0.1 +y2 = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeStandard + customArgs:@{@"x1":@0.4,@"y1":@0.0,@"x2":@0.1,@"y2":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveStandardInterpolator(0.4, 0.0, 0.1, 1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/zh-CN/accelerate.md b/docs/zh-CN/accelerate.md new file mode 100644 index 0000000..009b108 --- /dev/null +++ b/docs/zh-CN/accelerate.md @@ -0,0 +1,93 @@ +# 加速曲线 + +## 演示 + + + +## iOS + +枚举:GMCCurveTypeAccelerate + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeAccelerate + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +差值器:GaiaMotionCurveAccelerateInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveAccelerateInterpolator()); + animationView.startAnimation(animation); +``` + +## 可自定义的参数 + +可配置的参数: + +``` +x1:控制点1的x坐标 +y1:控制点1的y坐标 +x2:控制点2的x坐标 +y2:控制点2的y坐标 +``` + +默认值: + +``` +x1 = 0.4 +y1 = 0.0 +x2 = 1.0 +y2 = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeAccelerate + customArgs:@{@"x1":@0.4,@"y1":@0.0,@"x2":@1.0,@"y2":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveAccelerateInterpolator(0.4, 0.0, 1.0, 1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/zh-CN/anticipate.md b/docs/zh-CN/anticipate.md new file mode 100644 index 0000000..fd40efc --- /dev/null +++ b/docs/zh-CN/anticipate.md @@ -0,0 +1,104 @@ +# 预期曲线 + +## 演示 + + + +## iOS + +枚举:GMCCurveTypeAnticipate + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeAnticipate + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +差值器:GaiaMotionCurveAnticipateInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveAnticipateInterpolator()); + animationView.startAnimation(animation); +``` + +## 可自定义的参数 + +可配置的参数: + +``` + d:damping的缩写,控制的是最低点的高低 +x1:控制点1的x坐标 +y1:控制点1的y坐标 +x2:控制点2的x坐标 +y2:控制点2的y坐标 +x3:控制点3的x坐标 +y3:控制点3的y坐标 +x4:控制点4的x坐标 +y4:控制点4的y坐标 +``` + +默认值为: + +``` + d = 0.2 +x1 = 0.33 +y1 = 0.0 +x2 = 0.67 +y2 = 1.0 +x3 = 0.33 +y3 = 0.0 +x4 = 0.2 +y4 = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeAnticipate + customArgs:@{@"d":@0.2,@"x1":@0.33,@"y1":@0.0,@"x2":@0.67,@"y2":@1.0, + @"x3":@0.33,@"y3":@0.0,@"x4":@0.2,@"y4":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveAnticipateInterpolator(0.2,0.33,0.0,0.67,1.0,0.33,0.0,0.2,1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/zh-CN/bounce.md b/docs/zh-CN/bounce.md new file mode 100644 index 0000000..fe8fdf6 --- /dev/null +++ b/docs/zh-CN/bounce.md @@ -0,0 +1,89 @@ +# 弹跳曲线 + +## 演示 + + + +## iOS + +枚举:GMCCurveTypeBounce + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeBounce + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +差值器:GaiaMotionCurveBounceInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveBounceInterpolator()); + animationView.startAnimation(animation); +``` + +## 可自定义的参数 + +可配置的参数: + +``` +v:velocity的缩写 +d:damping的缩写 +``` + +默认值为: + +``` +v = 1.0 +d = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeBounce + customArgs:@{@"v":@1.0,@"d":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveBounceInterpolator(1.0, 1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/zh-CN/cosine.md b/docs/zh-CN/cosine.md new file mode 100644 index 0000000..22770ba --- /dev/null +++ b/docs/zh-CN/cosine.md @@ -0,0 +1,89 @@ +# 余弦曲线 + +## 演示 + + + +## iOS + +枚举:GMCCurveTypeCosine + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeCosine + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +差值器:GaiaMotionCurveCosineInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveCosineInterpolator()); + animationView.startAnimation(animation); +``` + +## 可自定义的参数 + +可配置的参数: + +``` +d:damping的缩写,控制的是最高点的高低 +c:cycles的缩写,周期的次数且是0.25的倍数 +``` + +默认值为: + +``` +d = 1.0 +c = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeCosine + customArgs:@{@"d":@1.0,@"c":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveCosineInterpolator(1.0, 1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/zh-CN/decelerate.md b/docs/zh-CN/decelerate.md new file mode 100644 index 0000000..64f9f4d --- /dev/null +++ b/docs/zh-CN/decelerate.md @@ -0,0 +1,93 @@ +# 减速曲线 + +## 演示 + + + +## iOS + +枚举:GMCCurveTypeDecelerate + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeDecelerate + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +差值器:GaiaMotionCurveDecelerateInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveDecelerateInterpolator()); + animationView.startAnimation(animation); +``` + +## 可自定义的参数 + +可配置的参数: + +``` +x1:控制点1的x坐标 +y1:控制点1的y坐标 +x2:控制点2的x坐标 +y2:控制点2的y坐标 +``` + +默认值为: + +``` +x1 = 0.0 +y1 = 0.0 +x2 = 0.1 +y2 = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeDecelerate + customArgs:@{@"x1":@0.0,@"y1":@0.0,@"x2":@0.1,@"y2":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveDecelerateInterpolator(0.0, 0.0, 0.1, 1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/zh-CN/linear.md b/docs/zh-CN/linear.md new file mode 100644 index 0000000..bcd0919 --- /dev/null +++ b/docs/zh-CN/linear.md @@ -0,0 +1,46 @@ +# 线性曲线 + +## 演示 + + + +## iOS + +枚举:GMCCurveTypeLinear + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeLinear + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +差值器:GaiaMotionCurveLinearInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveLinearInterpolator()); + animationView.startAnimation(animation); +``` + +## 可自定义的参数 + +无 diff --git a/docs/zh-CN/overshoot.md b/docs/zh-CN/overshoot.md new file mode 100644 index 0000000..724d67b --- /dev/null +++ b/docs/zh-CN/overshoot.md @@ -0,0 +1,104 @@ +# 过度曲线 + +## 演示 + + + +## iOS + +枚举:GMCCurveTypeOvershoot + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeOvershoot + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +差值器:GaiaMotionCurveOvershootInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveOvershootInterpolator()); + animationView.startAnimation(animation); +``` + +## 可自定义的参数 + +可配置的参数: + +``` + d:damping的缩写,控制的是最高点的高低 +x1:控制点1的x坐标 +y1:控制点1的y坐标 +x2:控制点2的x坐标 +y2:控制点2的y坐标 +x3:控制点3的x坐标 +y3:控制点3的y坐标 +x4:控制点4的x坐标 +y4:控制点4的y坐标 +``` + +默认值为: + +``` + d = 1.2 +x1 = 0.33 +y1 = 0.0 +x2 = 0.3 +y2 = 1.0 +x3 = 0.33 +y3 = 0.0 +x4 = 0.5 +y4 = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeOvershoot + customArgs:@{@"d":@1.2,@"x1":@0.33,@"y1":@0.0,@"x2":@0.3,@"y2":@1.0, + @"x3":@0.33,@"y3":@0.0,@"x4":@0.5,@"y4":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveOvershootInterpolator(1.2,0.33,0.0,0.3,1.0,0.33,0.0,0.5,1.0)); + animationView.startAnimation(animation); +``` + diff --git a/docs/zh-CN/spring.md b/docs/zh-CN/spring.md new file mode 100644 index 0000000..d916f2d --- /dev/null +++ b/docs/zh-CN/spring.md @@ -0,0 +1,132 @@ +# 弹性曲线 + +## 演示 + + + +## iOS + +枚举:GMCCurveTypeSpring + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeSpring + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +差值器:GaiaMotionCurveSpringInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveSpringInterpolator()); + animationView.startAnimation(animation); +``` + +## 可自定义的参数 + +可配置的参数: + +弹性曲线支持Mass和RK4两套参数系统(Android暂时只支持Mass参数系统) + +**Mass参数系统:** + +``` +m: mass的缩写 +s: stiffness的缩写 +d: damping的缩写 +iv:velocity的缩写 +``` + +默认值为: + +``` +m = 0.7 +s = 380.0 +d = 10.0 +iv = -2.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeSpring + customArgs:@{@"m":@1.0,@"s":@380.0,@"d":@10.0,@"iv":@-2.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveSpringInterpolator(0.7, 380.0, 10.0, -2.0)); + animationView.startAnimation(animation); +``` + +**RK4参数系统:** + +``` +t: tension的缩写 +f: friction的缩写 +v: velocity的缩写 +``` + +默认值为: + +``` +t = 533.0 +f = 14.0 +v = -2.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeSpring + customArgs:@{@"t":@533.0,@"f":@14.0,@"v":@-2.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + diff --git a/docs/zh-CN/standard.md b/docs/zh-CN/standard.md new file mode 100644 index 0000000..ddc4cf5 --- /dev/null +++ b/docs/zh-CN/standard.md @@ -0,0 +1,93 @@ +# 标准曲线 + +## 演示 + + + +## iOS + +枚举:GMCCurveTypeStandard + +```objc + #import + + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeStandard + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +## Android + +差值器:GaiaMotionCurveStandardInterpolator + +```java + import com.gaia.MotionCurve.*; + + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveStandardInterpolator()); + animationView.startAnimation(animation); +``` + +## 可自定义的参数 + +可配置的参数: + +``` +x1:控制点1的x坐标 +y1:控制点1的y坐标 +x2:控制点2的x坐标 +y2:控制点2的y坐标 +``` + +默认值为: + +``` +x1 = 0.4 +y1 = 0.0 +x2 = 0.1 +y2 = 1.0 +``` + +iOS + +```objective-c + CGFloat right = [[UIScreen mainScreen] bounds].size.width - 80 - 20; + GMCModel *model = [GMCModel modelWithKeyPath:@"position.x" + duration:0.75 + delay:0 + curveType:GMCCurveTypeStandard + customArgs:@{@"x1":@0.4,@"y1":@0.0,@"x2":@0.1,@"y2":@1.0} + fromValue:[NSValue gmc_valueWithCGFloat:_animationView.center.x] + toValue:[NSValue gmc_valueWithCGFloat:right]]; + __weak typeof(self) weakSelf = self; + [_animationView.layer gmc_animateWithAnimationModels:@[model] completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.animationView.layer.frame = CGRectMake(80, 200, 40, 40); + }); + }]; +``` + +Android + +```java + View animationView = findViewById(R.id.animation_view); + TranslateAnimation animation = new TranslateAnimation(0, 700, 0, 0); + animation.setFillAfter(true); + animation.setDuration(700); + animation.setInterpolator(new GaiaMotionCurveStandardInterpolator(0.4, 0.0, 0.1, 1.0)); + animationView.startAnimation(animation); +``` + diff --git a/src/iOS/GaiaMotionCurve.podspec b/src/iOS/GaiaMotionCurve.podspec new file mode 100644 index 0000000..ebea566 --- /dev/null +++ b/src/iOS/GaiaMotionCurve.podspec @@ -0,0 +1,17 @@ +Pod::Spec.new do |s| + + s.name = "GaiaMotionCurve" + s.version = "0.1.0" + s.summary = "a library to solve the problems of high cost of developing dynamic effects and inconsistent effects and design expectations" + s.license = { :type => 'Apache License, Version 2.0' } + s.homepage = "https://github.com/alibaba/gaia-motion-curve" + + s.author = { "ronghui.zrh" => "ronghui.zrh@alibaba-inc.com" } + s.platform = :ios, "9.0" + s.source = { :git => "https://github.com/alibaba/gaia-motion-curve.git", :tag => s.version } + + + s.source_files = 'src/iOS/GaiaMotionCurve/**/*.{h,m,mm,c}' + s.xcconfig = { "ENABLE_BITCODE" => "NO" } + s.requires_arc = true +end diff --git a/src/iOS/GaiaMotionCurve.xcodeproj/project.pbxproj b/src/iOS/GaiaMotionCurve.xcodeproj/project.pbxproj new file mode 100644 index 0000000..ad00ded --- /dev/null +++ b/src/iOS/GaiaMotionCurve.xcodeproj/project.pbxproj @@ -0,0 +1,512 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + BD6BDF1C2783F60E001B1CA3 /* CALayer+GaiaMotionCurve.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6BDF1A2783F60E001B1CA3 /* CALayer+GaiaMotionCurve.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BD6BDF1D2783F60E001B1CA3 /* CALayer+GaiaMotionCurve.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6BDF1B2783F60E001B1CA3 /* CALayer+GaiaMotionCurve.m */; }; + BD6BDF212783F69E001B1CA3 /* GMCDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6BDF202783F69E001B1CA3 /* GMCDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BD6BDF242783F6E2001B1CA3 /* NSValue+GMCCGFloat.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6BDF222783F6E2001B1CA3 /* NSValue+GMCCGFloat.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BD6BDF252783F6E2001B1CA3 /* NSValue+GMCCGFloat.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6BDF232783F6E2001B1CA3 /* NSValue+GMCCGFloat.m */; }; + BD6BDF282783F6F7001B1CA3 /* NSValue+GMCUIColor.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6BDF262783F6F7001B1CA3 /* NSValue+GMCUIColor.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BD6BDF292783F6F7001B1CA3 /* NSValue+GMCUIColor.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6BDF272783F6F7001B1CA3 /* NSValue+GMCUIColor.m */; }; + BD6BDF2C2783F723001B1CA3 /* GMCModel.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6BDF2A2783F723001B1CA3 /* GMCModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BD6BDF2D2783F723001B1CA3 /* GMCModel.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6BDF2B2783F723001B1CA3 /* GMCModel.m */; }; + BD6BDF302783F741001B1CA3 /* GMCModel+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6BDF2E2783F741001B1CA3 /* GMCModel+Internal.h */; }; + BD6BDF312783F741001B1CA3 /* GMCModel+Internal.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6BDF2F2783F741001B1CA3 /* GMCModel+Internal.m */; }; + BD6BDF342783F75F001B1CA3 /* GMCHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6BDF322783F75F001B1CA3 /* GMCHelper.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BD6BDF352783F75F001B1CA3 /* GMCHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6BDF332783F75F001B1CA3 /* GMCHelper.m */; }; + BD6BDF3C2783F924001B1CA3 /* GMCFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6BDF3A2783F924001B1CA3 /* GMCFunctions.h */; }; + BD6BDF3D2783F924001B1CA3 /* GMCFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6BDF3B2783F924001B1CA3 /* GMCFunctions.m */; }; + BDD182A22783F4A3008547F4 /* GaiaMotionCurve.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDD182972783F4A2008547F4 /* GaiaMotionCurve.framework */; }; + BDD182A72783F4A3008547F4 /* GaiaMotionCurveTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BDD182A62783F4A3008547F4 /* GaiaMotionCurveTests.m */; }; + BDD182A82783F4A3008547F4 /* GaiaMotionCurve.h in Headers */ = {isa = PBXBuildFile; fileRef = BDD1829A2783F4A2008547F4 /* GaiaMotionCurve.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + BDD182A32783F4A3008547F4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BDD1828E2783F4A2008547F4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BDD182962783F4A2008547F4; + remoteInfo = GaiaMotionCurve; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + BD6BDF1A2783F60E001B1CA3 /* CALayer+GaiaMotionCurve.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CALayer+GaiaMotionCurve.h"; sourceTree = ""; }; + BD6BDF1B2783F60E001B1CA3 /* CALayer+GaiaMotionCurve.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CALayer+GaiaMotionCurve.m"; sourceTree = ""; }; + BD6BDF202783F69E001B1CA3 /* GMCDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GMCDefines.h; sourceTree = ""; }; + BD6BDF222783F6E2001B1CA3 /* NSValue+GMCCGFloat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSValue+GMCCGFloat.h"; sourceTree = ""; }; + BD6BDF232783F6E2001B1CA3 /* NSValue+GMCCGFloat.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSValue+GMCCGFloat.m"; sourceTree = ""; }; + BD6BDF262783F6F7001B1CA3 /* NSValue+GMCUIColor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSValue+GMCUIColor.h"; sourceTree = ""; }; + BD6BDF272783F6F7001B1CA3 /* NSValue+GMCUIColor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSValue+GMCUIColor.m"; sourceTree = ""; }; + BD6BDF2A2783F723001B1CA3 /* GMCModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GMCModel.h; sourceTree = ""; }; + BD6BDF2B2783F723001B1CA3 /* GMCModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GMCModel.m; sourceTree = ""; }; + BD6BDF2E2783F741001B1CA3 /* GMCModel+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "GMCModel+Internal.h"; sourceTree = ""; }; + BD6BDF2F2783F741001B1CA3 /* GMCModel+Internal.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "GMCModel+Internal.m"; sourceTree = ""; }; + BD6BDF322783F75F001B1CA3 /* GMCHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GMCHelper.h; sourceTree = ""; }; + BD6BDF332783F75F001B1CA3 /* GMCHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GMCHelper.m; sourceTree = ""; }; + BD6BDF3A2783F924001B1CA3 /* GMCFunctions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GMCFunctions.h; sourceTree = ""; }; + BD6BDF3B2783F924001B1CA3 /* GMCFunctions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GMCFunctions.m; sourceTree = ""; }; + BDD182972783F4A2008547F4 /* GaiaMotionCurve.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GaiaMotionCurve.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BDD1829A2783F4A2008547F4 /* GaiaMotionCurve.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GaiaMotionCurve.h; sourceTree = ""; }; + BDD182A12783F4A3008547F4 /* GaiaMotionCurveTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GaiaMotionCurveTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BDD182A62783F4A3008547F4 /* GaiaMotionCurveTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GaiaMotionCurveTests.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + BDD182942783F4A2008547F4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BDD1829E2783F4A3008547F4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BDD182A22783F4A3008547F4 /* GaiaMotionCurve.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + BDD1828D2783F4A2008547F4 = { + isa = PBXGroup; + children = ( + BDD182992783F4A2008547F4 /* GaiaMotionCurve */, + BDD182A52783F4A3008547F4 /* GaiaMotionCurveTests */, + BDD182982783F4A2008547F4 /* Products */, + ); + sourceTree = ""; + }; + BDD182982783F4A2008547F4 /* Products */ = { + isa = PBXGroup; + children = ( + BDD182972783F4A2008547F4 /* GaiaMotionCurve.framework */, + BDD182A12783F4A3008547F4 /* GaiaMotionCurveTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + BDD182992783F4A2008547F4 /* GaiaMotionCurve */ = { + isa = PBXGroup; + children = ( + BDD1829A2783F4A2008547F4 /* GaiaMotionCurve.h */, + BD6BDF1A2783F60E001B1CA3 /* CALayer+GaiaMotionCurve.h */, + BD6BDF1B2783F60E001B1CA3 /* CALayer+GaiaMotionCurve.m */, + BD6BDF202783F69E001B1CA3 /* GMCDefines.h */, + BD6BDF222783F6E2001B1CA3 /* NSValue+GMCCGFloat.h */, + BD6BDF232783F6E2001B1CA3 /* NSValue+GMCCGFloat.m */, + BD6BDF262783F6F7001B1CA3 /* NSValue+GMCUIColor.h */, + BD6BDF272783F6F7001B1CA3 /* NSValue+GMCUIColor.m */, + BD6BDF2A2783F723001B1CA3 /* GMCModel.h */, + BD6BDF2B2783F723001B1CA3 /* GMCModel.m */, + BD6BDF2E2783F741001B1CA3 /* GMCModel+Internal.h */, + BD6BDF2F2783F741001B1CA3 /* GMCModel+Internal.m */, + BD6BDF322783F75F001B1CA3 /* GMCHelper.h */, + BD6BDF332783F75F001B1CA3 /* GMCHelper.m */, + BD6BDF3A2783F924001B1CA3 /* GMCFunctions.h */, + BD6BDF3B2783F924001B1CA3 /* GMCFunctions.m */, + ); + path = GaiaMotionCurve; + sourceTree = ""; + }; + BDD182A52783F4A3008547F4 /* GaiaMotionCurveTests */ = { + isa = PBXGroup; + children = ( + BDD182A62783F4A3008547F4 /* GaiaMotionCurveTests.m */, + ); + path = GaiaMotionCurveTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + BDD182922783F4A2008547F4 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + BD6BDF342783F75F001B1CA3 /* GMCHelper.h in Headers */, + BD6BDF212783F69E001B1CA3 /* GMCDefines.h in Headers */, + BD6BDF282783F6F7001B1CA3 /* NSValue+GMCUIColor.h in Headers */, + BD6BDF1C2783F60E001B1CA3 /* CALayer+GaiaMotionCurve.h in Headers */, + BD6BDF2C2783F723001B1CA3 /* GMCModel.h in Headers */, + BD6BDF242783F6E2001B1CA3 /* NSValue+GMCCGFloat.h in Headers */, + BDD182A82783F4A3008547F4 /* GaiaMotionCurve.h in Headers */, + BD6BDF3C2783F924001B1CA3 /* GMCFunctions.h in Headers */, + BD6BDF302783F741001B1CA3 /* GMCModel+Internal.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + BDD182962783F4A2008547F4 /* GaiaMotionCurve */ = { + isa = PBXNativeTarget; + buildConfigurationList = BDD182AB2783F4A3008547F4 /* Build configuration list for PBXNativeTarget "GaiaMotionCurve" */; + buildPhases = ( + BDD182922783F4A2008547F4 /* Headers */, + BDD182932783F4A2008547F4 /* Sources */, + BDD182942783F4A2008547F4 /* Frameworks */, + BDD182952783F4A2008547F4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = GaiaMotionCurve; + productName = GaiaMotionCurve; + productReference = BDD182972783F4A2008547F4 /* GaiaMotionCurve.framework */; + productType = "com.apple.product-type.framework"; + }; + BDD182A02783F4A3008547F4 /* GaiaMotionCurveTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = BDD182AE2783F4A3008547F4 /* Build configuration list for PBXNativeTarget "GaiaMotionCurveTests" */; + buildPhases = ( + BDD1829D2783F4A3008547F4 /* Sources */, + BDD1829E2783F4A3008547F4 /* Frameworks */, + BDD1829F2783F4A3008547F4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BDD182A42783F4A3008547F4 /* PBXTargetDependency */, + ); + name = GaiaMotionCurveTests; + productName = GaiaMotionCurveTests; + productReference = BDD182A12783F4A3008547F4 /* GaiaMotionCurveTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BDD1828E2783F4A2008547F4 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1320; + TargetAttributes = { + BDD182962783F4A2008547F4 = { + CreatedOnToolsVersion = 13.2.1; + }; + BDD182A02783F4A3008547F4 = { + CreatedOnToolsVersion = 13.2.1; + }; + }; + }; + buildConfigurationList = BDD182912783F4A2008547F4 /* Build configuration list for PBXProject "GaiaMotionCurve" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = BDD1828D2783F4A2008547F4; + productRefGroup = BDD182982783F4A2008547F4 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BDD182962783F4A2008547F4 /* GaiaMotionCurve */, + BDD182A02783F4A3008547F4 /* GaiaMotionCurveTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + BDD182952783F4A2008547F4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BDD1829F2783F4A3008547F4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BDD182932783F4A2008547F4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BD6BDF312783F741001B1CA3 /* GMCModel+Internal.m in Sources */, + BD6BDF352783F75F001B1CA3 /* GMCHelper.m in Sources */, + BD6BDF292783F6F7001B1CA3 /* NSValue+GMCUIColor.m in Sources */, + BD6BDF2D2783F723001B1CA3 /* GMCModel.m in Sources */, + BD6BDF1D2783F60E001B1CA3 /* CALayer+GaiaMotionCurve.m in Sources */, + BD6BDF252783F6E2001B1CA3 /* NSValue+GMCCGFloat.m in Sources */, + BD6BDF3D2783F924001B1CA3 /* GMCFunctions.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BDD1829D2783F4A3008547F4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BDD182A72783F4A3008547F4 /* GaiaMotionCurveTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + BDD182A42783F4A3008547F4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BDD182962783F4A2008547F4 /* GaiaMotionCurve */; + targetProxy = BDD182A32783F4A3008547F4 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + BDD182A92783F4A3008547F4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + BDD182AA2783F4A3008547F4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + BDD182AC2783F4A3008547F4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 13.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.youku.GaiaMotionCurve; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BDD182AD2783F4A3008547F4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 13.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.youku.GaiaMotionCurve; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + BDD182AF2783F4A3008547F4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.youku.GaiaMotionCurveTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BDD182B02783F4A3008547F4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.youku.GaiaMotionCurveTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BDD182912783F4A2008547F4 /* Build configuration list for PBXProject "GaiaMotionCurve" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BDD182A92783F4A3008547F4 /* Debug */, + BDD182AA2783F4A3008547F4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BDD182AB2783F4A3008547F4 /* Build configuration list for PBXNativeTarget "GaiaMotionCurve" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BDD182AC2783F4A3008547F4 /* Debug */, + BDD182AD2783F4A3008547F4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BDD182AE2783F4A3008547F4 /* Build configuration list for PBXNativeTarget "GaiaMotionCurveTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BDD182AF2783F4A3008547F4 /* Debug */, + BDD182B02783F4A3008547F4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BDD1828E2783F4A2008547F4 /* Project object */; +} diff --git a/src/iOS/GaiaMotionCurve/CALayer+GaiaMotionCurve.h b/src/iOS/GaiaMotionCurve/CALayer+GaiaMotionCurve.h new file mode 100644 index 0000000..8cf776b --- /dev/null +++ b/src/iOS/GaiaMotionCurve/CALayer+GaiaMotionCurve.h @@ -0,0 +1,53 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import "GMCModel.h" +#import "NSValue+GMCCGFloat.h" +#import "NSValue+GMCUIColor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface CALayer (GaiaMotionCurve) + +/** + when finished,will auto set toValue to layer,do not need set in completion block again + @param models animation models array + @param completion finished block + @return animation unique id + */ +- (NSString *)gmc_animateWithGMCModels:(NSArray *)models + completion:(GMCCompletionBlock)completion; + + +//serial animation +- (NSString *)gmc_serialAnimationWithGMCModels:(NSArray *)models + completion:(GMCCompletionBlock)completion; + +/** + cancel animation with animationID + @param animationID animation unique id + */ +- (void)gmc_cancelAnimationWithID:(NSString *)animationID; + +// cancel all animations +- (void)gmc_cancelAllAnimations; + + +@property(nonatomic, strong) NSMutableDictionary *gmc_animationKeys; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/iOS/GaiaMotionCurve/CALayer+GaiaMotionCurve.m b/src/iOS/GaiaMotionCurve/CALayer+GaiaMotionCurve.m new file mode 100644 index 0000000..b6b4857 --- /dev/null +++ b/src/iOS/GaiaMotionCurve/CALayer+GaiaMotionCurve.m @@ -0,0 +1,285 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#import "CALayer+GaiaMotionCurve.h" +#import "GMCHelper.h" +#import "GMCModel+Internal.h" +#import + + +static const void *kGMCAnimationKeys = &kGMCAnimationKeys; + +@implementation CALayer (GaiaMotionCurve) + +- (NSString *)gmc_animateWithGMCModels:(NSArray *)models + completion:(GMCCompletionBlock)completion { + return [self gmc_animateWithGMCModels:models animationKey:nil completion:completion]; +} + + +- (NSString *)gmc_animateWithGMCModels:(NSArray *)models + animationKey:(NSString *)animationKey + completion:(GMCCompletionBlock)completion { + if (models == nil || models.count <= 0) { + return nil; + } + NSMutableArray *animations = [NSMutableArray array]; + NSString *uuidString = animationKey; + + if (self.gmc_animationKeys == nil) { + self.gmc_animationKeys = [NSMutableDictionary dictionary]; + } + + int durationIndex = 0; + CFTimeInterval currentDuration = 0; + for (int i = 0; i < models.count; i++) { + GMCModel *model = [models objectAtIndex:i]; + NSArray *values = [GMCHelper valuesByCurveModel:model]; + if (values == nil || values.count <= 0) { + continue; + } + + if ([model.keyPath hasPrefix:@"bounds"]) { + [self addGMCAnimation:model]; + } + + CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:model.keyPath]; + animation.fillMode = kCAFillModeForwards; + animation.removedOnCompletion = NO; + animation.delegate = self; + animation.beginTime = model.delay; + animation.duration = model.duration; + animation.repeatCount = model.repeatCount; + animation.autoreverses = model.autoReverse; + [animation setValues:values]; + [animations addObject:animation]; + + CFTimeInterval tmpDuration = (MAX(model.repeatCount, 1) + (model.autoReverse ? 1 : 0)) * (model.duration) + model.delay; + if (tmpDuration > currentDuration) { + durationIndex = i; + currentDuration = tmpDuration; + } + } + + NSMutableArray *animationKeys = (animationKey == nil ? [NSMutableArray array] : self.gmc_animationKeys[animationKey]); + for (int i = 0; i < animations.count; i++) { + CAKeyframeAnimation *animation = [animations objectAtIndex:i]; + NSString *identifier = [[NSUUID UUID] UUIDString]; + [animation setValue:identifier forKey:@"gmc_identifier"]; + if (i == durationIndex) { + if (completion != nil) { + [animation setValue:completion forKey:@"gmc_completion_block"]; + } + if (uuidString == nil) { + uuidString = identifier; + } + } + [animationKeys addObject:identifier]; + [self addAnimation:animation forKey:identifier]; + } + + [self.gmc_animationKeys setObject:animationKeys forKey:uuidString]; + + return uuidString; +} + +- (void)addGMCAnimation:(GMCModel *)model { + UIView *associatedView = (UIView *) self.delegate; + CGRect fromRect = [model.fromValue CGRectValue]; + CGRect toRect = [model.toValue CGRectValue]; + if ([associatedView isKindOfClass:[UIView class]] && + associatedView.autoresizesSubviews && + associatedView.subviews.count > 0) { + for (UIView *view in associatedView.subviews) { + UIViewAutoresizing mask = view.autoresizingMask; + if (mask != UIViewAutoresizingNone) { + CGFloat dx = toRect.size.width - fromRect.size.width; + CGFloat dy = toRect.size.height - fromRect.size.height; + + dx /= ((mask & UIViewAutoresizingFlexibleLeftMargin) ? 1 : 0) + + ((mask & UIViewAutoresizingFlexibleWidth) ? 1 : 0) + + ((mask & UIViewAutoresizingFlexibleRightMargin) ? 1 : 0); + dy /= ((mask & UIViewAutoresizingFlexibleTopMargin) ? 1 : 0) + + ((mask & UIViewAutoresizingFlexibleHeight) ? 1 : 0) + + ((mask & UIViewAutoresizingFlexibleBottomMargin) ? 1 : 0); + + CGFloat scale = [UIScreen mainScreen].scale; + if ((fabs(dx) < 1.0f / scale) && (fabs(dy) < 1.0f / scale)) { + return; + } + CGRect bounds = view.bounds; + bounds.size.width += (mask & UIViewAutoresizingFlexibleWidth) ? dx : 0; + bounds.size.height += + (mask & UIViewAutoresizingFlexibleHeight) ? dy : 0; + CGFloat originX = 0; + if (mask & UIViewAutoresizingFlexibleLeftMargin) { + originX = dx; + } else if (mask & UIViewAutoresizingFlexibleWidth) { + originX = dx * view.layer.anchorPoint.x; + }; + CGFloat originY = 0; + if (mask & UIViewAutoresizingFlexibleTopMargin) { + originY = dy; + } else if (mask & UIViewAutoresizingFlexibleHeight) { + originY = dy * view.layer.anchorPoint.y; + }; + NSMutableArray *array = [[NSMutableArray alloc] init]; + if (!CGRectEqualToRect(view.bounds, bounds)) { + GMCModel *model1 = [GMCModel + modelWithKeyPath:@"bounds" + duration:model.duration + delay:model.delay + curveType:model.curveType + argsModel:model.argsModel + fromValue:[NSValue valueWithCGRect:view.bounds] + toValue:[NSValue valueWithCGRect:bounds]]; + [array addObject:model1]; + } + CGPoint toPoint = CGPointMake(view.layer.position.x + originX, + view.layer.position.y + originY); + if (!CGPointEqualToPoint(view.layer.position, toPoint)) { + GMCModel *model2 = [GMCModel + modelWithKeyPath:@"position" + duration:model.duration + delay:model.delay + curveType:model.curveType + argsModel:model.argsModel + fromValue:[NSValue + valueWithCGPoint:view.layer + .position] + toValue:[NSValue valueWithCGPoint:toPoint]]; + [array addObject:model2]; + } + if ([array count] > 0) { + [view.layer gmc_animateWithGMCModels:array completion:^(BOOL finished) { + }]; + } + } + } + } +} + +- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { + if ([anim isKindOfClass:[CAKeyframeAnimation class]] && [anim valueForKey:@"gmc_identifier"]) { + CAKeyframeAnimation *keyAnimation = (CAKeyframeAnimation *) anim; + + if (flag) { + NSArray *values = [keyAnimation values]; + if (values && values.count > 0) { + if (!keyAnimation.autoreverses) { + [self setValue:[values lastObject] forKeyPath:keyAnimation.keyPath]; + } else { + [self setValue:[values firstObject] forKeyPath:keyAnimation.keyPath]; + } + } + } + + + NSString *animationID = [anim valueForKey:@"gmc_identifier"]; + + + [self removeAnimationForKey:animationID]; + + [self.gmc_animationKeys removeObjectForKey:animationID]; + + + GMCCompletionBlock block = [keyAnimation valueForKey:@"gmc_completion_block"]; + if (block) { + block(flag); + } + } +} + + +- (NSString *)gmc_serialAnimationWithGMCModels:(NSArray *)models + completion:(GMCCompletionBlock)completion { + NSString *animationKey = [[NSUUID UUID] UUIDString]; + if (self.gmc_animationKeys == nil) { + self.gmc_animationKeys = [NSMutableDictionary dictionary]; + } + [self serialAnimationWithGMCModels:models currentIndex:0 animationKey:animationKey completion:completion]; + return animationKey; +} + + +- (void)serialAnimationWithGMCModels:(NSArray *)models + currentIndex:(NSInteger)currentIndex + animationKey:(NSString *)animationKey + completion:(GMCCompletionBlock)completion { + NSInteger count = models.count; + GMCModel *model = [models objectAtIndex:currentIndex]; + [self.gmc_animationKeys setObject:[NSMutableArray array] forKey:animationKey]; + GMCWeakSelf(self) + [self gmc_animateWithGMCModels:@[model] animationKey:animationKey completion:^(BOOL finished) { + if (finished) { + NSInteger index = currentIndex; + index++; + GMCStrongSelf(self); + if (index < count) { + [self serialAnimationWithGMCModels:models currentIndex:index animationKey:animationKey completion:completion]; + } else { + if (completion != nil) { + completion(finished); + } + } + } else { + if (completion != nil) { + completion(finished); + } + } + }]; + +} + + +- (void)gmc_cancelAnimationWithID:(NSString *)animationID { + if (animationID.length > 0) { + if (self.gmc_animationKeys) { + if (self.gmc_animationKeys[animationID] != nil) { + NSArray *animations = self.gmc_animationKeys[animationID]; + for (NSUInteger i = 0; i < animations.count; i++) { + [self removeAnimationForKey:animations[i]]; + } + [self removeAnimationForKey:animationID]; + } else { + [self removeAnimationForKey:animationID]; + } + } else { + [self removeAnimationForKey:animationID]; + } + [self.gmc_animationKeys removeObjectForKey:animationID]; + } +} + + +- (void)gmc_cancelAllAnimations { + if (self.gmc_animationKeys != nil) { + for (NSString *animationID in self.gmc_animationKeys) { + [self gmc_cancelAnimationWithID:animationID]; + } + [self.gmc_animationKeys removeAllObjects]; + } +} + + +- (void)setGmc_animationKeys:(NSMutableDictionary *)gmc_animationKeys { + objc_setAssociatedObject(self, &kGMCAnimationKeys, gmc_animationKeys, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSMutableDictionary *)gmc_animationKeys { + return objc_getAssociatedObject(self, &kGMCAnimationKeys); +} + +@end diff --git a/src/iOS/GaiaMotionCurve/GMCDefines.h b/src/iOS/GaiaMotionCurve/GMCDefines.h new file mode 100644 index 0000000..2570447 --- /dev/null +++ b/src/iOS/GaiaMotionCurve/GMCDefines.h @@ -0,0 +1,38 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef GMCDefines_h +#define GMCDefines_h + +#import + +typedef NS_ENUM(NSInteger, GMCCurveType) { + GMCCurveTypeLinear, + GMCCurveTypeCosine, + GMCCurveTypeAccelerate, + GMCCurveTypeDecelerate, + GMCCurveTypeStandard, + GMCCurveTypeCubicBezier = GMCCurveTypeStandard, + GMCCurveTypeAnticipate, + GMCCurveTypeOvershoot, + GMCCurveTypeBounce, + GMCCurveTypeSpring, +}; + +typedef void (^GMCCompletionBlock)(BOOL finished); + +#define GMCWeakSelf(type) __weak typeof(type) weak##type = type; +#define GMCStrongSelf(type) __strong typeof(type) type = weak##type; + +#endif /* GMCDefines_h */ diff --git a/src/iOS/GaiaMotionCurve/GMCFunctions.h b/src/iOS/GaiaMotionCurve/GMCFunctions.h new file mode 100644 index 0000000..8662be6 --- /dev/null +++ b/src/iOS/GaiaMotionCurve/GMCFunctions.h @@ -0,0 +1,53 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#import +#import + + +#ifndef GMCFunctions_h +#define GMCFunctions_h + +CGFloat gmc_linear_curve(CGFloat p); + +CGFloat gmc_cosine_curve(CGFloat p, CGFloat d, CGFloat c); + +CGFloat gmc_decelerate_curve(CGFloat p, CGFloat x1, CGFloat y1, CGFloat x2, + CGFloat y2); + +CGFloat gmc_accelerate_curve(CGFloat p, CGFloat x1, CGFloat y1, CGFloat x2, + CGFloat y2); + +CGFloat gmc_standard_curve(CGFloat p, CGFloat x1, CGFloat y1, CGFloat x2, + CGFloat y2); + +CGFloat gmc_anticipate_curve(CGFloat p, CGFloat d, CGFloat x1, CGFloat y1, + CGFloat x2, CGFloat y2, CGFloat x3, CGFloat y3, + CGFloat x4, CGFloat y4); + +CGFloat gmc_overshoot_curve(CGFloat p, CGFloat d, CGFloat x1, CGFloat y1, + CGFloat x2, CGFloat y2, CGFloat x3, CGFloat y3, + CGFloat x4, CGFloat y4); + +CGFloat gmc_bounce_curve(CGFloat p, CGFloat v, CGFloat d); + +CGFloat gmc_spring_motion_curve(CGFloat p, CGFloat m, CGFloat s, CGFloat d, + CGFloat iv); + +CGFloat gmc_spring_rk4_motion_curve(CGFloat p, CGFloat *lastValue, + CGFloat t, CGFloat f, + CGFloat *v); + +#endif /* GMCFunctions_h */ diff --git a/src/iOS/GaiaMotionCurve/GMCFunctions.m b/src/iOS/GaiaMotionCurve/GMCFunctions.m new file mode 100644 index 0000000..37f30d2 --- /dev/null +++ b/src/iOS/GaiaMotionCurve/GMCFunctions.m @@ -0,0 +1,208 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "GMCFunctions.h" + +CGFloat gmc_linear_curve(CGFloat p) { + return p; +} + +CGFloat gmc_cosine_curve(CGFloat p, CGFloat d, CGFloat c) { + return d * 0.5 * (1 - cos(p * 2 * c * M_PI)); +} + +CGFloat gmc_decelerate_curve(CGFloat p, CGFloat x1, CGFloat y1, CGFloat x2, + CGFloat y2) { + return gmc_standard_curve(p, x1, y1, x2, y2); +} + +CGFloat gmc_accelerate_curve(CGFloat p, CGFloat x1, CGFloat y1, CGFloat x2, + CGFloat y2) { + return gmc_standard_curve(p, x1, y1, x2, y2); +} + +CGFloat gmc_standard_curve(CGFloat p, CGFloat x1, CGFloat y1, CGFloat x2, + CGFloat y2) { + CGFloat x = p; + CGFloat z; + CGFloat ax, ay, bx, by, cx, cy; + for (int i = 1; i < 14; i++) { + cx = 3.0f * x1; + bx = 3.0f * (x2 - x1) - cx; + ax = 1.0f - cx - bx; + CGFloat b = x * (cx + x * (bx + x * ax)); + z = b - p; + if (fabs(z) < 1e-3) { + break; + } + CGFloat d = cx + x * (2.0f * bx + 3.0f * ax * x); + x -= z / d; + } + cy = 3.0f * y1; + by = 3.0f * (y2 - y1) - cy; + ay = 1.0f - cy - by; + return x * (cy + x * (by + x * ay)); +} + +CGFloat gmc_anticipate_curve(CGFloat p, CGFloat d, CGFloat x1, CGFloat y1, CGFloat x2, + CGFloat y2, CGFloat x3, CGFloat y3, CGFloat x4, + CGFloat y4) { + // Anticipate (0.33,0,0.67,1 )time:0.3 (0.33,0,0.2,1 ) + if (p <= 0.3f) { + return -d * gmc_standard_curve(p * 3.33, x1, y1, x2, y2); + } else { + return -d * gmc_standard_curve(0.3 * 3.33, x1, y1, x2, y2) + (1 + d) * gmc_standard_curve((p - 0.3) * 1.42, x3, y3, x4, y4); + } +} + +CGFloat gmc_bounce_curve(CGFloat p, CGFloat v, CGFloat d) { + CGFloat tp = p; + CGFloat b = 2.75 * v; + if (tp < (1 / b)) { + return pow(b, 2) * tp * tp; + } else if (tp < (2 / b)) { + return pow(b * d, 2) * (tp - 1.5 / b) * (tp - 1.5 / b) + (1 - pow(b * d, 2) * (1 / b - 1.5 / b) * (1 / b - 1.5 / b)); + } else if (tp < (2.5 / b)) { + return pow(b * d, 2) * (tp - 2.25 / b) * (tp - 2.25 / b) + (1 - pow(b * d, 2) * (2 / b - 2.25 / b) * (2 / b - 2.25 / b)); + } else { + return MIN(1.0f, pow(b * d, 2) * (tp - 2.625 / b) * (tp - 2.625 / b) + (1 - pow(b * d, 2) * (2.5 / b - 2.625 / b) * (2.5 / b - 2.625 / b))); + } +} + +CGFloat gmc_overshoot_curve(CGFloat p, CGFloat d, CGFloat x1, CGFloat y1, CGFloat x2, + CGFloat y2, CGFloat x3, CGFloat y3, CGFloat x4, + CGFloat y4) { + //Overshoot (0.33,0,0.3,1 )time:0.5 (0.33,0,0.5,1 ) + if (p <= 0.5f) { + return d * gmc_standard_curve(p * 2, x1, y1, x2, y2); + } else { + return d * gmc_standard_curve(0.5 * 2, x1, y1, x2, y2) - (d - 1) * gmc_standard_curve((p - 0.5) * 2, x3, y3, x4, y4); + } +} + +CGFloat gmc_spring_motion_curve(CGFloat p, CGFloat m, CGFloat s, CGFloat d, + CGFloat iv) { + CGFloat m_w0 = sqrt(s / m); + CGFloat m_zeta = d / (2.0f * sqrt(s * m)); + CGFloat m_wd; + CGFloat m_A; + CGFloat m_B; + if (m_zeta < 1.0f) { + m_wd = m_w0 * sqrt(1.0f - m_zeta * m_zeta); + m_A = 1.0f; + m_B = (m_zeta * m_w0 + -iv) / m_wd; + } else { + m_wd = 0.0f; + m_A = 1.0f; + m_B = -iv + m_w0; + } + CGFloat t; + if (m_zeta < 1.0f) { + t = exp(-p * m_zeta * m_w0) * (m_A * cos(m_wd * p) + m_B * sin(m_wd * p)); + } else { + t = (m_A + m_B * p) * exp(-p * m_w0); + } + return 1.0f - t; +} + +typedef struct { + CGFloat x; + CGFloat v; + CGFloat tension; + CGFloat friction; +} GMCSpringState; + +typedef struct { + CGFloat dx; + CGFloat dv; +} GMCSpringDerivative; + +CGFloat normalizeSpringValue(CGFloat value) { + if (isnan(value)) { + return 0; + } else if (isinf(value)) { + return 0; + } else { + return value; + } +} + +CGFloat springAccelerationForState(GMCSpringState state) { + return -state.tension * state.x - state.friction * state.v; +} + +GMCSpringDerivative springEvaluateState(GMCSpringState initialState) { + GMCSpringDerivative output; + output.dx = initialState.v; + output.dv = springAccelerationForState(initialState); + return output; +} + +GMCSpringDerivative springEvaluateStateWithDerivative(GMCSpringState initialState, CGFloat dt, GMCSpringDerivative derivative) { + + GMCSpringState state; + state.x = initialState.x + derivative.dx * dt; + state.v = initialState.v + derivative.dv * dt; + state.tension = initialState.tension; + state.friction = initialState.friction; + + GMCSpringDerivative output; + output.dx = state.v; + output.dv = springAccelerationForState(state); + + return output; +} + +GMCSpringState springIntegrateState(GMCSpringState state, CGFloat speed) { + GMCSpringDerivative a = springEvaluateState(state); + GMCSpringDerivative b = springEvaluateStateWithDerivative(state, speed * 0.5, a); + GMCSpringDerivative c = springEvaluateStateWithDerivative(state, speed * 0.5, b); + GMCSpringDerivative d = springEvaluateStateWithDerivative(state, speed, c); + + CGFloat tx = (a.dx + 2.0 * (b.dx + c.dx) + d.dx); + CGFloat ty = (a.dv + 2.0 * (b.dv + c.dv) + d.dv); + + CGFloat dxdt = 1.0 / 6.0 * tx; + CGFloat dvdt = 1.0 / 6.0 * ty; + + GMCSpringState output = state; + + output.x = state.x + dxdt * speed; + output.v = state.v + dvdt * speed; + + return output; +} + +CGFloat gmc_spring_rk4_motion_curve(CGFloat p, CGFloat *lastValue, CGFloat t, CGFloat f, CGFloat *v) { + CGFloat result; + if (p == 0.0f) { + result = 0.0f; + } else { + GMCSpringState before; + before.x = *lastValue - 1.0f; + before.v = *v; + before.tension = t; + before.friction = f; + + GMCSpringState after = springIntegrateState(before, 1 / 60.0f); + *lastValue = 1 + after.x; + *v = after.v; + + *lastValue = normalizeSpringValue(*lastValue); + *v = normalizeSpringValue(*v); + + result = *lastValue; + } + return result; +} diff --git a/src/iOS/GaiaMotionCurve/GMCHelper.h b/src/iOS/GaiaMotionCurve/GMCHelper.h new file mode 100644 index 0000000..128e5d8 --- /dev/null +++ b/src/iOS/GaiaMotionCurve/GMCHelper.h @@ -0,0 +1,42 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#import +#import + +@class GMCModel; + +NS_ASSUME_NONNULL_BEGIN + +@interface GMCHelper : NSObject + + ++ (NSArray *)valuesByCurveModel:(GMCModel *)model; + +/* + count > 1 + */ ++ (NSArray *)valuesByCurveModel:(GMCModel *)model valueCount:(NSInteger)count; + + +/* + 0 < percent <= 1 + */ ++ (NSValue *)valueByCurveModel:(GMCModel *)model progressPercent:(CGFloat)percent; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/iOS/GaiaMotionCurve/GMCHelper.m b/src/iOS/GaiaMotionCurve/GMCHelper.m new file mode 100644 index 0000000..c96955c --- /dev/null +++ b/src/iOS/GaiaMotionCurve/GMCHelper.m @@ -0,0 +1,363 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "GMCHelper.h" +#import "GMCModel.h" +#import "GMCModel+Internal.h" +#import "GMCFunctions.h" + +@implementation GMCHelper + ++ (NSArray *)valuesByCurveModel:(GMCModel *)model { + return [self valuesByCurveModel:model valueCount:60]; +} + + ++ (NSValue *)valueByCurveModel:(GMCModel *)model progressPercent:(CGFloat)percent { + CGFloat tc = 60; + NSMutableArray *values = [NSMutableArray array]; + CGFloat t = 0.0; + CGFloat dt = 1.0 / (tc - 1); + CGFloat count = MAX(1, round(tc * percent)); + if (model.fromValue == nil || model.toValue == nil || + strcmp(model.fromValue.objCType, model.toValue.objCType) != 0) { + } else { + if (strcmp(model.fromValue.objCType, @encode(CGFloat)) == 0) { + CGFloat fromValue = [model.fromValue gmc_CGFloatValue]; + CGFloat toValue = [model.toValue gmc_CGFloatValue]; + CGFloat lastValue = 0.0f; + CGFloat lastVelocity = (model.curveType == GMCCurveTypeSpring && [model.argsModel isKindOfClass:[GMCRK4SpringArgsModel class]]) ? [(GMCRK4SpringArgsModel *) model.argsModel velocity] : 0.0f; + for (NSUInteger frame = 0; frame < count; ++frame, t += dt) { + CGFloat delta = [GMCHelper computeCurvedStep:t + lastValue:&lastValue + lastVelocity:&lastVelocity + type:model.curveType + model:model.argsModel]; + CGFloat value = fromValue + delta * (toValue - fromValue); + [values addObject:[NSValue gmc_valueWithCGFloat:(CGFloat) value]]; + } + } else if (strcmp(model.fromValue.objCType, @encode(CGPoint)) == 0) { + CGPoint fromPoint = [model.fromValue CGPointValue]; + CGPoint toPoint = [model.toValue CGPointValue]; + CGFloat lastValue = 0.0f; + CGFloat lastVelocity = (model.curveType == GMCCurveTypeSpring && [model.argsModel isKindOfClass:[GMCRK4SpringArgsModel class]]) ? [(GMCRK4SpringArgsModel *) model.argsModel velocity] : 0.0f; + for (NSUInteger frame = 0; frame < count; ++frame, t += dt) { + CGFloat delta = [GMCHelper computeCurvedStep:t + lastValue:&lastValue + lastVelocity:&lastVelocity + type:model.curveType + model:model.argsModel]; + CGFloat x = fromPoint.x + delta * (toPoint.x - fromPoint.x); + CGFloat y = fromPoint.y + delta * (toPoint.y - fromPoint.y); + [values addObject:[NSValue valueWithCGPoint:CGPointMake(x, y)]]; + } + } else if (strcmp(model.fromValue.objCType, @encode(CGSize)) == 0) { + CGSize fromSize = [model.fromValue CGSizeValue]; + CGSize toSize = [model.toValue CGSizeValue]; + CGFloat lastValue = 0.0f; + CGFloat lastVelocity = (model.curveType == GMCCurveTypeSpring && [model.argsModel isKindOfClass:[GMCRK4SpringArgsModel class]]) ? [(GMCRK4SpringArgsModel *) model.argsModel velocity] : 0.0f; + for (NSUInteger frame = 0; frame < count; ++frame, t += dt) { + CGFloat delta = [GMCHelper computeCurvedStep:t + lastValue:&lastValue + lastVelocity:&lastVelocity + type:model.curveType + model:model.argsModel]; + CGFloat width = fromSize.width + delta * (toSize.width - fromSize.width); + CGFloat height = fromSize.height + delta * (toSize.height - fromSize.height); + [values addObject:[NSValue valueWithCGSize:CGSizeMake(width, height)]]; + } + } else if (strcmp(model.fromValue.objCType, @encode(CGRect)) == 0) { + CGRect fromRect = [model.fromValue CGRectValue]; + CGRect toRect = [model.toValue CGRectValue]; + CGFloat lastValue = 0.0f; + CGFloat lastVelocity = (model.curveType == GMCCurveTypeSpring && [model.argsModel isKindOfClass:[GMCRK4SpringArgsModel class]]) ? [(GMCRK4SpringArgsModel *) model.argsModel velocity] : 0.0f; + for (NSUInteger frame = 0; frame < count; ++frame, t += dt) { + CGFloat delta = [GMCHelper computeCurvedStep:t + lastValue:&lastValue + lastVelocity:&lastVelocity + type:model.curveType + model:model.argsModel]; + CGFloat x = fromRect.origin.x + delta * (toRect.origin.x - fromRect.origin.x); + CGFloat y = fromRect.origin.y + delta * (toRect.origin.y - fromRect.origin.y); + CGFloat width = fromRect.size.width + delta * (toRect.size.width - fromRect.size.width); + CGFloat height = fromRect.size.height + delta * (toRect.size.height - fromRect.size.height); + [values addObject:[NSValue + valueWithCGRect:CGRectMake(x, y, width, height)]]; + } + } else if (strcmp(model.fromValue.objCType, @encode(CGAffineTransform)) == + 0) { + CGAffineTransform fromTransform = + [model.fromValue CGAffineTransformValue]; + CGAffineTransform toTransform = [model.toValue CGAffineTransformValue]; + CGPoint fromT = CGPointMake(fromTransform.tx, fromTransform.ty); + CGPoint toT = CGPointMake(toTransform.tx, toTransform.ty); + CGFloat fromS = hypot(fromTransform.a, fromTransform.c); + CGFloat toS = hypot(toTransform.a, toTransform.c); + CGFloat fromR = atan2(fromTransform.c, fromTransform.a); + CGFloat toR = atan2(toTransform.c, toTransform.a); + CGFloat deltaR = toR - fromR; + if (deltaR < -M_PI) { + deltaR += (2 * M_PI); + } else if (deltaR > M_PI) { + deltaR -= (2 * M_PI); + } + CGFloat lastValue = 0.0f; + CGFloat lastVelocity = (model.curveType == GMCCurveTypeSpring && [model.argsModel isKindOfClass:[GMCRK4SpringArgsModel class]]) ? [(GMCRK4SpringArgsModel *) model.argsModel velocity] : 0.0f; + for (NSUInteger frame = 0; frame < count; ++frame, t += dt) { + CGFloat inter = [GMCHelper computeCurvedStep:t + lastValue:&lastValue + lastVelocity:&lastVelocity + type:model.curveType + model:model.argsModel]; + CGFloat translateX = + fromT.x + inter * (toT.x - fromT.x); + CGFloat translateY = + fromT.y + inter * (toT.y - fromT.y); + CGFloat scale = fromS + inter * (toS - fromS); + CGFloat rotate = fromR + inter * deltaR; + CGAffineTransform affineTransform = CGAffineTransformMake( + scale * cos(rotate), -scale * sin(rotate), scale * sin(rotate), + scale * cos(rotate), translateX, translateY); + CATransform3D transform = + CATransform3DMakeAffineTransform(affineTransform); + [values addObject:[NSValue valueWithCATransform3D:transform]]; + } + } else if (strcmp(model.fromValue.objCType, @encode(CGColorRef)) == 0) { + UIColor *fromColor = [UIColor colorWithCGColor:[model.fromValue gmc_CGColorValue]]; + UIColor *toColor = [UIColor colorWithCGColor:[model.toValue gmc_CGColorValue]]; + if (fromColor && toColor) { + if ([fromColor isKindOfClass:[UIColor class]] && + [toColor isKindOfClass:[UIColor class]]) { + CGFloat fromRed, fromGreen, fromBlue, fromAlpha; + CGFloat toRed, toGreen, toBlue, toAlpha; + [fromColor getRed:&fromRed + green:&fromGreen + blue:&fromBlue + alpha:&fromAlpha]; + [toColor getRed:&toRed green:&toGreen blue:&toBlue alpha:&toAlpha]; + CGFloat lastValue = 0.0f; + CGFloat lastVelocity = (model.curveType == GMCCurveTypeSpring && [model.argsModel isKindOfClass:[GMCRK4SpringArgsModel class]]) ? [(GMCRK4SpringArgsModel *) model.argsModel velocity] : 0.0f; + for (NSUInteger frame = 0; frame < count; ++frame, t += dt) { + CGFloat delta = [GMCHelper computeCurvedStep:t + lastValue:&lastValue + lastVelocity:&lastVelocity + type:model.curveType + model:model.argsModel]; + CGFloat red = fromRed + delta * (toRed - fromRed); + CGFloat green = fromGreen + delta * (toGreen - fromGreen); + CGFloat blue = fromBlue + delta * (toBlue - fromBlue); + CGFloat alpha = fromAlpha + delta * (toAlpha - fromAlpha); + [values addObject:(id) [UIColor colorWithRed:red + green:green + blue:blue + alpha:alpha] + .CGColor]; + } + } + } + } + } + + return [values lastObject]; +} + ++ (NSArray *)valuesByCurveModel:(GMCModel *)model valueCount:(NSInteger)count { + NSMutableArray *values = [NSMutableArray array]; + CGFloat t = 0.0; + CGFloat dt = 1.0 / (count - 1); + + if (model.fromValue == nil || model.toValue == nil || + strcmp(model.fromValue.objCType, model.toValue.objCType) != 0) { + } else { + if (strcmp(model.fromValue.objCType, @encode(CGFloat)) == 0) { + CGFloat fromValue = [model.fromValue gmc_CGFloatValue]; + CGFloat toValue = [model.toValue gmc_CGFloatValue]; + CGFloat lastValue = 0.0f; + CGFloat lastVelocity = (model.curveType == GMCCurveTypeSpring && [model.argsModel isKindOfClass:[GMCRK4SpringArgsModel class]]) ? [(GMCRK4SpringArgsModel *) model.argsModel velocity] : 0.0f; + for (NSUInteger frame = 0; frame < count; ++frame, t += dt) { + CGFloat delta = [GMCHelper computeCurvedStep:t + lastValue:&lastValue + lastVelocity:&lastVelocity + type:model.curveType + model:model.argsModel]; + CGFloat value = fromValue + delta * (toValue - fromValue); + [values addObject:[NSNumber numberWithFloat:(CGFloat) value]]; + } + } else if (strcmp(model.fromValue.objCType, @encode(CGPoint)) == 0) { + CGPoint fromPoint = [model.fromValue CGPointValue]; + CGPoint toPoint = [model.toValue CGPointValue]; + CGFloat lastValue = 0.0f; + CGFloat lastVelocity = (model.curveType == GMCCurveTypeSpring && [model.argsModel isKindOfClass:[GMCRK4SpringArgsModel class]]) ? [(GMCRK4SpringArgsModel *) model.argsModel velocity] : 0.0f; + for (NSUInteger frame = 0; frame < count; ++frame, t += dt) { + CGFloat delta = [GMCHelper computeCurvedStep:t + lastValue:&lastValue + lastVelocity:&lastVelocity + type:model.curveType + model:model.argsModel]; + CGFloat x = fromPoint.x + delta * (toPoint.x - fromPoint.x); + CGFloat y = fromPoint.y + delta * (toPoint.y - fromPoint.y); + [values addObject:[NSValue valueWithCGPoint:CGPointMake(x, y)]]; + } + } else if (strcmp(model.fromValue.objCType, @encode(CGSize)) == 0) { + CGSize fromSize = [model.fromValue CGSizeValue]; + CGSize toSize = [model.toValue CGSizeValue]; + CGFloat lastValue = 0.0f; + CGFloat lastVelocity = (model.curveType == GMCCurveTypeSpring && [model.argsModel isKindOfClass:[GMCRK4SpringArgsModel class]]) ? [(GMCRK4SpringArgsModel *) model.argsModel velocity] : 0.0f; + for (NSUInteger frame = 0; frame < count; ++frame, t += dt) { + CGFloat delta = [GMCHelper computeCurvedStep:t + lastValue:&lastValue + lastVelocity:&lastVelocity + type:model.curveType + model:model.argsModel]; + CGFloat width = fromSize.width + delta * (toSize.width - fromSize.width); + CGFloat height = fromSize.height + delta * (toSize.height - fromSize.height); + [values addObject:[NSValue valueWithCGSize:CGSizeMake(width, height)]]; + } + } else if (strcmp(model.fromValue.objCType, @encode(CGRect)) == 0) { + CGRect fromRect = [model.fromValue CGRectValue]; + CGRect toRect = [model.toValue CGRectValue]; + CGFloat lastValue = 0.0f; + CGFloat lastVelocity = (model.curveType == GMCCurveTypeSpring && [model.argsModel isKindOfClass:[GMCRK4SpringArgsModel class]]) ? [(GMCRK4SpringArgsModel *) model.argsModel velocity] : 0.0f; + for (NSUInteger frame = 0; frame < count; ++frame, t += dt) { + CGFloat delta = [GMCHelper computeCurvedStep:t + lastValue:&lastValue + lastVelocity:&lastVelocity + type:model.curveType + model:model.argsModel]; + CGFloat x = fromRect.origin.x + delta * (toRect.origin.x - fromRect.origin.x); + CGFloat y = fromRect.origin.y + delta * (toRect.origin.y - fromRect.origin.y); + CGFloat width = fromRect.size.width + delta * (toRect.size.width - fromRect.size.width); + CGFloat height = fromRect.size.height + delta * (toRect.size.height - fromRect.size.height); + [values addObject:[NSValue + valueWithCGRect:CGRectMake(x, y, width, height)]]; + } + } else if (strcmp(model.fromValue.objCType, @encode(CGAffineTransform)) == + 0) { + CGAffineTransform fromTransform = + [model.fromValue CGAffineTransformValue]; + CGAffineTransform toTransform = [model.toValue CGAffineTransformValue]; + CGPoint fromT = CGPointMake(fromTransform.tx, fromTransform.ty); + CGPoint toT = CGPointMake(toTransform.tx, toTransform.ty); + CGFloat fromS = hypot(fromTransform.a, fromTransform.c); + CGFloat toS = hypot(toTransform.a, toTransform.c); + CGFloat fromR = atan2(fromTransform.c, fromTransform.a); + CGFloat toR = atan2(toTransform.c, toTransform.a); + CGFloat deltaR = toR - fromR; + if (deltaR < -M_PI) { + deltaR += (2 * M_PI); + } else if (deltaR > M_PI) { + deltaR -= (2 * M_PI); + } + CGFloat lastValue = 0.0f; + CGFloat lastVelocity = (model.curveType == GMCCurveTypeSpring && [model.argsModel isKindOfClass:[GMCRK4SpringArgsModel class]]) ? [(GMCRK4SpringArgsModel *) model.argsModel velocity] : 0.0f; + for (NSUInteger frame = 0; frame < count; ++frame, t += dt) { + CGFloat inter = [GMCHelper computeCurvedStep:t + lastValue:&lastValue + lastVelocity:&lastVelocity + type:model.curveType + model:model.argsModel]; + CGFloat translateX = + fromT.x + inter * (toT.x - fromT.x); + CGFloat translateY = + fromT.y + inter * (toT.y - fromT.y); + CGFloat scale = fromS + inter * (toS - fromS); + CGFloat rotate = fromR + inter * deltaR; + CGAffineTransform affineTransform = CGAffineTransformMake( + scale * cos(rotate), -scale * sin(rotate), scale * sin(rotate), + scale * cos(rotate), translateX, translateY); + CATransform3D transform = + CATransform3DMakeAffineTransform(affineTransform); + [values addObject:[NSValue valueWithCATransform3D:transform]]; + } + } else if (strcmp(model.fromValue.objCType, @encode(CGColorRef)) == 0) { + UIColor *fromColor = [UIColor colorWithCGColor:[model.fromValue gmc_CGColorValue]]; + UIColor *toColor = [UIColor colorWithCGColor:[model.toValue gmc_CGColorValue]]; + if (fromColor && toColor) { + if ([fromColor isKindOfClass:[UIColor class]] && + [toColor isKindOfClass:[UIColor class]]) { + CGFloat fromRed, fromGreen, fromBlue, fromAlpha; + CGFloat toRed, toGreen, toBlue, toAlpha; + [fromColor getRed:&fromRed + green:&fromGreen + blue:&fromBlue + alpha:&fromAlpha]; + [toColor getRed:&toRed green:&toGreen blue:&toBlue alpha:&toAlpha]; + CGFloat lastValue = 0.0f; + CGFloat lastVelocity = (model.curveType == GMCCurveTypeSpring && [model.argsModel isKindOfClass:[GMCRK4SpringArgsModel class]]) ? [(GMCRK4SpringArgsModel *) model.argsModel velocity] : 0.0f; + for (NSUInteger frame = 0; frame < count; ++frame, t += dt) { + CGFloat delta = [GMCHelper computeCurvedStep:t + lastValue:&lastValue + lastVelocity:&lastVelocity + type:model.curveType + model:model.argsModel]; + CGFloat red = fromRed + delta * (toRed - fromRed); + CGFloat green = fromGreen + delta * (toGreen - fromGreen); + CGFloat blue = fromBlue + delta * (toBlue - fromBlue); + CGFloat alpha = fromAlpha + delta * (toAlpha - fromAlpha); + [values addObject:(id) [UIColor colorWithRed:red + green:green + blue:blue + alpha:alpha] + .CGColor]; + } + } + } + } + } + + return values; +} + ++ (CGFloat)computeCurvedStep:(CGFloat)step + lastValue:(CGFloat *)lastValue + lastVelocity:(CGFloat *)lastVelocity + type:(GMCCurveType)curveType + model:(GMCBaseArgsModel *)argsModel { + CGFloat value; + if (curveType == GMCCurveTypeOvershoot) { + GMCAnticipateArgsModel *overshootArgsModel = (GMCAnticipateArgsModel *) argsModel; + value = gmc_overshoot_curve(step, overshootArgsModel.d, overshootArgsModel.x1, overshootArgsModel.y1, overshootArgsModel.x2, overshootArgsModel.y2, overshootArgsModel.x3, overshootArgsModel.y3, overshootArgsModel.x4, overshootArgsModel.y4); + } else if (curveType == GMCCurveTypeAccelerate) { + GMCAccelerateArgsModel *accelerateArgsModel = (GMCAccelerateArgsModel *) argsModel; + value = gmc_accelerate_curve(step, accelerateArgsModel.x1, accelerateArgsModel.y1, accelerateArgsModel.x2, accelerateArgsModel.y2); + } else if (curveType == GMCCurveTypeDecelerate) { + GMCDecelerateArgsModel *decelerateArgsModel = (GMCDecelerateArgsModel *) argsModel; + value = gmc_decelerate_curve(step, decelerateArgsModel.x1, decelerateArgsModel.y1, decelerateArgsModel.x2, decelerateArgsModel.y2); + } else if (curveType == GMCCurveTypeBounce) { + GMCBounceArgsModel *bounceArgsModel = (GMCBounceArgsModel *) argsModel; + value = gmc_bounce_curve(step, bounceArgsModel.v, bounceArgsModel.d); + } else if (curveType == GMCCurveTypeCosine) { + GMCCosineArgsModel *cosineArgsModel = (GMCCosineArgsModel *) argsModel; + value = gmc_cosine_curve(step, cosineArgsModel.d, cosineArgsModel.c); + } else if (curveType == GMCCurveTypeAnticipate) { + GMCAnticipateArgsModel *anticipateArgsModel = (GMCAnticipateArgsModel *) argsModel; + value = gmc_anticipate_curve(step, anticipateArgsModel.d, anticipateArgsModel.x1, anticipateArgsModel.y1, anticipateArgsModel.x2, anticipateArgsModel.y2, anticipateArgsModel.x3, anticipateArgsModel.y3, anticipateArgsModel.x4, anticipateArgsModel.y4); + } else if (curveType == GMCCurveTypeStandard) { + GMCStandardArgsModel *standardArgsModel = (GMCStandardArgsModel *) argsModel; + value = gmc_standard_curve(step, standardArgsModel.x1, standardArgsModel.y1, standardArgsModel.x2, standardArgsModel.y2); + } else if (curveType == GMCCurveTypeSpring) { + if ([argsModel isKindOfClass:[GMCDefaultSpringArgsModel class]]) { + GMCDefaultSpringArgsModel *springArgsModel = (GMCDefaultSpringArgsModel *) argsModel; + value = gmc_spring_motion_curve(step, springArgsModel.mass, springArgsModel.stiffness, springArgsModel.damping, springArgsModel.velocity); + } else { + GMCRK4SpringArgsModel *rk4ArgsModel = (GMCRK4SpringArgsModel *) argsModel; + value = gmc_spring_rk4_motion_curve(step, lastValue, rk4ArgsModel.tension, rk4ArgsModel.friction, lastVelocity); + } + } else { + value = gmc_linear_curve(step); + } + return value; +} + +@end diff --git a/src/iOS/GaiaMotionCurve/GMCModel+Internal.h b/src/iOS/GaiaMotionCurve/GMCModel+Internal.h new file mode 100644 index 0000000..255383b --- /dev/null +++ b/src/iOS/GaiaMotionCurve/GMCModel+Internal.h @@ -0,0 +1,105 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#import "GMCModel.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GMCBaseArgsModel : NSObject + +@end + +@interface GMCStandardArgsModel : GMCBaseArgsModel + +@property(nonatomic, assign) CGFloat x1; +@property(nonatomic, assign) CGFloat y1; +@property(nonatomic, assign) CGFloat x2; +@property(nonatomic, assign) CGFloat y2; + +@end + +@interface GMCAccelerateArgsModel : GMCStandardArgsModel + +@end + +@interface GMCDecelerateArgsModel : GMCStandardArgsModel + +@end + +@interface GMCAnticipateArgsModel : GMCBaseArgsModel + +@property(nonatomic, assign) CGFloat d; +@property(nonatomic, assign) CGFloat x1; +@property(nonatomic, assign) CGFloat y1; +@property(nonatomic, assign) CGFloat x2; +@property(nonatomic, assign) CGFloat y2; +@property(nonatomic, assign) CGFloat x3; +@property(nonatomic, assign) CGFloat y3; +@property(nonatomic, assign) CGFloat x4; +@property(nonatomic, assign) CGFloat y4; + +@end + +@interface GMCOvershootArgsModel : GMCAnticipateArgsModel + +@end + +@interface GMCBounceArgsModel : GMCBaseArgsModel + +@property(nonatomic, assign) CGFloat v; +@property(nonatomic, assign) CGFloat d; + +@end + +@interface GMCDefaultSpringArgsModel : GMCBaseArgsModel + +@property(nonatomic, assign) CGFloat mass; +@property(nonatomic, assign) CGFloat stiffness; +@property(nonatomic, assign) CGFloat damping; +@property(nonatomic, assign) CGFloat velocity; + +@end + +@interface GMCRK4SpringArgsModel : GMCBaseArgsModel + +@property(nonatomic, assign) CGFloat tension; +@property(nonatomic, assign) CGFloat friction; +@property(nonatomic, assign) CGFloat velocity; + +@end + +@interface GMCCosineArgsModel : GMCBaseArgsModel + +@property(nonatomic, assign) CGFloat d; +@property(nonatomic, assign) CGFloat c; + +@end + + +@interface GMCModel (Internal) + +@property(nonatomic, strong) GMCBaseArgsModel *argsModel; + ++ (GMCModel *)modelWithKeyPath:(NSString *)keyPath + duration:(CFTimeInterval)duration + delay:(CFTimeInterval)delay + curveType:(GMCCurveType)curveType + argsModel:(nullable GMCBaseArgsModel *)argsModel + fromValue:(NSValue *)fromValue + toValue:(NSValue *)toValue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/iOS/GaiaMotionCurve/GMCModel+Internal.m b/src/iOS/GaiaMotionCurve/GMCModel+Internal.m new file mode 100644 index 0000000..353a4fb --- /dev/null +++ b/src/iOS/GaiaMotionCurve/GMCModel+Internal.m @@ -0,0 +1,79 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "GMCModel+Internal.h" +#import + +@implementation GMCBaseArgsModel +@end + +@implementation GMCStandardArgsModel +@end + +@implementation GMCAccelerateArgsModel +@end + +@implementation GMCDecelerateArgsModel +@end + +@implementation GMCAnticipateArgsModel +@end + +@implementation GMCOvershootArgsModel +@end + +@implementation GMCBounceArgsModel +@end + +@implementation GMCDefaultSpringArgsModel +@end + +@implementation GMCRK4SpringArgsModel +@end + +@implementation GMCCosineArgsModel +@end + + +@implementation GMCModel (Internal) + +static NSString *kGMCModelArgsModelKey = @"kGMCModelArgsModelKey"; + +- (void)setArgsModel:(GMCBaseArgsModel *)argsModel { + objc_setAssociatedObject(self, &kGMCModelArgsModelKey, argsModel, + OBJC_ASSOCIATION_RETAIN); +} + +- (GMCBaseArgsModel *)argsModel { + return objc_getAssociatedObject(self, &kGMCModelArgsModelKey); +} + ++ (GMCModel *)modelWithKeyPath:(NSString *)keyPath + duration:(CFTimeInterval)duration + delay:(CFTimeInterval)delay + curveType:(GMCCurveType)curveType + argsModel:(nullable GMCBaseArgsModel *)argsModel + fromValue:(NSValue *)fromValue + toValue:(NSValue *)toValue { + GMCModel *model = [GMCModel modelWithKeyPath:keyPath + duration:duration + delay:delay + curveType:curveType + fromValue:fromValue + toValue:toValue]; + model.argsModel = argsModel; + return model; +} + +@end diff --git a/src/iOS/GaiaMotionCurve/GMCModel.h b/src/iOS/GaiaMotionCurve/GMCModel.h new file mode 100644 index 0000000..344608a --- /dev/null +++ b/src/iOS/GaiaMotionCurve/GMCModel.h @@ -0,0 +1,88 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#import +#import +#import "GMCDefines.h" +#import "NSValue+GMCCGFloat.h" +#import "NSValue+GMCUIColor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GMCModel : NSObject + +//default 0 +@property(nonatomic, assign) NSInteger repeatCount; + +//default NO +@property(nonatomic, assign) BOOL autoReverse; + +//animation key path +@property(nonatomic, strong) NSString *keyPath; + +//second +@property(nonatomic, assign) CFTimeInterval duration; + +//second +@property(nonatomic, assign) CFTimeInterval delay; + +//motion curve type +@property(nonatomic, assign) GMCCurveType curveType; + +@property(nonatomic, strong) NSValue *fromValue; + +@property(nonatomic, strong) NSValue *toValue; + + ++ (GMCModel *)modelWithKeyPath:(NSString *)keyPath + duration:(CFTimeInterval)duration + delay:(CFTimeInterval)delay + curveType:(GMCCurveType)curveType + fromValue:(NSValue *)fromValue + toValue:(NSValue *)toValue; + + ++ (GMCModel *)modelWithKeyPath:(NSString *)keyPath + duration:(CFTimeInterval)duration + delay:(CFTimeInterval)delay + curveType:(GMCCurveType)curveType + customArgs:(nullable NSDictionary *)customArgs + fromValue:(NSValue *)fromValue + toValue:(NSValue *)toValue; + + ++ (GMCModel *)modelWithKeyPath:(NSString *)keyPath + duration:(CFTimeInterval)duration + delay:(CFTimeInterval)delay + repeatCount:(NSInteger)repeatCount + autoReverse:(BOOL)autoReverse + curveType:(GMCCurveType)curveType + fromValue:(NSValue *)fromValue + toValue:(NSValue *)toValue; + + ++ (GMCModel *)modelWithKeyPath:(NSString *)keyPath + duration:(CFTimeInterval)duration + delay:(CFTimeInterval)delay + repeatCount:(NSInteger)repeatCount + autoReverse:(BOOL)autoReverse + curveType:(GMCCurveType)curveType + customArgs:(nullable NSDictionary *)customArgs + fromValue:(NSValue *)fromValue + toValue:(NSValue *)toValue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/iOS/GaiaMotionCurve/GMCModel.m b/src/iOS/GaiaMotionCurve/GMCModel.m new file mode 100644 index 0000000..2ec1195 --- /dev/null +++ b/src/iOS/GaiaMotionCurve/GMCModel.m @@ -0,0 +1,169 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#import "GMCModel.h" +#import "GMCModel+Internal.h" + +@implementation GMCModel + ++ (GMCModel *)modelWithKeyPath:(NSString *)keyPath + duration:(CFTimeInterval)duration + delay:(CFTimeInterval)delay + curveType:(GMCCurveType)curveType + fromValue:(NSValue *)fromValue + toValue:(NSValue *)toValue { + return [GMCModel modelWithKeyPath:keyPath + duration:duration + delay:delay + curveType:curveType + customArgs:nil + fromValue:fromValue + toValue:toValue]; +} + ++ (GMCModel *)modelWithKeyPath:(NSString *)keyPath + duration:(CFTimeInterval)duration + delay:(CFTimeInterval)delay + curveType:(GMCCurveType)curveType + customArgs:(nullable NSDictionary *)customArgs + fromValue:(NSValue *)fromValue + toValue:(NSValue *)toValue { + return [GMCModel modelWithKeyPath:keyPath + duration:duration + delay:delay + repeatCount:0 + autoReverse:NO + curveType:curveType + customArgs:customArgs + fromValue:fromValue + toValue:toValue]; + +} + + ++ (GMCModel *)modelWithKeyPath:(NSString *)keyPath + duration:(CFTimeInterval)duration + delay:(CFTimeInterval)delay + repeatCount:(NSInteger)repeatCount + autoReverse:(BOOL)autoReverse + curveType:(GMCCurveType)curveType + fromValue:(NSValue *)fromValue + toValue:(NSValue *)toValue { + return [GMCModel modelWithKeyPath:keyPath + duration:duration + delay:delay + repeatCount:repeatCount + autoReverse:autoReverse + curveType:curveType + customArgs:nil + fromValue:fromValue + toValue:toValue]; +} + + ++ (GMCModel *)modelWithKeyPath:(NSString *)keyPath + duration:(CFTimeInterval)duration + delay:(CFTimeInterval)delay + repeatCount:(NSInteger)repeatCount + autoReverse:(BOOL)autoReverse + curveType:(GMCCurveType)curveType + customArgs:(nullable NSDictionary *)customArgs + fromValue:(NSValue *)fromValue + toValue:(NSValue *)toValue { + GMCModel *model = [[GMCModel alloc] init]; + model.keyPath = keyPath; + model.duration = duration; + model.delay = delay; + model.repeatCount = repeatCount; + model.autoReverse = autoReverse; + model.curveType = curveType; + model.fromValue = fromValue; + model.toValue = toValue; + if (curveType == GMCCurveTypeCosine) { + GMCCosineArgsModel *argsModel = [[GMCCosineArgsModel alloc] init]; + argsModel.d = customArgs[@"d"] != nil ? [customArgs[@"d"] floatValue] : 1.0f; + argsModel.c = customArgs[@"c"] != nil ? [customArgs[@"c"] floatValue] : 1.0f; + model.argsModel = argsModel; + } else if (curveType == GMCCurveTypeBounce) { + GMCBounceArgsModel *argsModel = [[GMCBounceArgsModel alloc] init]; + argsModel.v = customArgs[@"v"] != nil ? [customArgs[@"v"] floatValue] : 1.0f; + argsModel.d = customArgs[@"d"] != nil ? [customArgs[@"d"] floatValue] : 1.0f; + model.argsModel = argsModel; + } else if (curveType == GMCCurveTypeAccelerate) { + GMCAccelerateArgsModel *argsModel = [[GMCAccelerateArgsModel alloc] init]; + argsModel.x1 = customArgs[@"x1"] != nil ? [customArgs[@"x1"] floatValue] : 0.4f; + argsModel.y1 = customArgs[@"y1"] != nil ? [customArgs[@"y1"] floatValue] : 0.0f; + argsModel.x2 = customArgs[@"x2"] != nil ? [customArgs[@"x2"] floatValue] : 1.0f; + argsModel.y2 = customArgs[@"y2"] != nil ? [customArgs[@"y2"] floatValue] : 1.0f; + model.argsModel = argsModel; + } else if (curveType == GMCCurveTypeDecelerate) { + GMCDecelerateArgsModel *argsModel = [[GMCDecelerateArgsModel alloc] init]; + argsModel.x1 = customArgs[@"x1"] != nil ? [customArgs[@"x1"] floatValue] : 0.0f; + argsModel.y1 = customArgs[@"y1"] != nil ? [customArgs[@"y1"] floatValue] : 0.0f; + argsModel.x2 = customArgs[@"x2"] != nil ? [customArgs[@"x2"] floatValue] : 0.1f; + argsModel.y2 = customArgs[@"y2"] != nil ? [customArgs[@"y2"] floatValue] : 1.0f; + model.argsModel = argsModel; + } else if (curveType == GMCCurveTypeStandard) { + GMCStandardArgsModel *argsModel = [[GMCStandardArgsModel alloc] init]; + argsModel.x1 = customArgs[@"x1"] != nil ? [customArgs[@"x1"] floatValue] : 0.4f; + argsModel.y1 = customArgs[@"y1"] != nil ? [customArgs[@"y1"] floatValue] : 0.0f; + argsModel.x2 = customArgs[@"x2"] != nil ? [customArgs[@"x2"] floatValue] : 0.1f; + argsModel.y2 = customArgs[@"y2"] != nil ? [customArgs[@"y2"] floatValue] : 1.0f; + model.argsModel = argsModel; + } else if (curveType == GMCCurveTypeOvershoot) { + GMCOvershootArgsModel *argsModel = [[GMCOvershootArgsModel alloc] init]; + argsModel.d = customArgs[@"d"] != nil ? [customArgs[@"d"] floatValue] : 1.2f; + argsModel.x1 = customArgs[@"x1"] != nil ? [customArgs[@"x1"] floatValue] : 0.33f; + argsModel.y1 = customArgs[@"y1"] != nil ? [customArgs[@"y1"] floatValue] : 0.0f; + argsModel.x2 = customArgs[@"x2"] != nil ? [customArgs[@"x2"] floatValue] : 0.3f; + argsModel.y2 = customArgs[@"y2"] != nil ? [customArgs[@"y2"] floatValue] : 1.0f; + argsModel.x3 = customArgs[@"x3"] != nil ? [customArgs[@"x3"] floatValue] : 0.33f; + argsModel.y3 = customArgs[@"y3"] != nil ? [customArgs[@"y3"] floatValue] : 0.0f; + argsModel.x4 = customArgs[@"x4"] != nil ? [customArgs[@"x4"] floatValue] : 0.5f; + argsModel.y4 = customArgs[@"y4"] != nil ? [customArgs[@"y4"] floatValue] : 1.0f; + model.argsModel = argsModel; + } else if (curveType == GMCCurveTypeAnticipate) { + GMCAnticipateArgsModel *argsModel = [[GMCAnticipateArgsModel alloc] init]; + argsModel.d = customArgs[@"d"] != nil ? [customArgs[@"d"] floatValue] : 0.2f; + argsModel.x1 = customArgs[@"x1"] != nil ? [customArgs[@"x1"] floatValue] : 0.33f; + argsModel.y1 = customArgs[@"y1"] != nil ? [customArgs[@"y1"] floatValue] : 0.0f; + argsModel.x2 = customArgs[@"x2"] != nil ? [customArgs[@"x2"] floatValue] : 0.67f; + argsModel.y2 = customArgs[@"y2"] != nil ? [customArgs[@"y2"] floatValue] : 1.0f; + argsModel.x3 = customArgs[@"x3"] != nil ? [customArgs[@"x3"] floatValue] : 0.33f; + argsModel.y3 = customArgs[@"y3"] != nil ? [customArgs[@"y3"] floatValue] : 0.0f; + argsModel.x4 = customArgs[@"x4"] != nil ? [customArgs[@"x4"] floatValue] : 0.2f; + argsModel.y4 = customArgs[@"y4"] != nil ? [customArgs[@"y4"] floatValue] : 1.0f; + model.argsModel = argsModel; + } else if (curveType == GMCCurveTypeSpring) { + if (customArgs[@"m"] != nil || customArgs[@"s"] != nil || customArgs[@"d"] != nil || customArgs[@"iv"] != nil) { + GMCDefaultSpringArgsModel *argsModel = [[GMCDefaultSpringArgsModel alloc] init]; + argsModel.mass = customArgs[@"m"] != nil ? [customArgs[@"m"] floatValue] : 0.7f; + argsModel.stiffness = customArgs[@"s"] != nil ? [customArgs[@"s"] floatValue] : 380.0f; + argsModel.damping = customArgs[@"d"] != nil ? [customArgs[@"d"] floatValue] : 10.0f; + argsModel.velocity = customArgs[@"iv"] != nil ? [customArgs[@"iv"] floatValue] : -2.0f; + model.argsModel = argsModel; + } else { + GMCRK4SpringArgsModel *argsModel = [[GMCRK4SpringArgsModel alloc] init]; + argsModel.tension = customArgs[@"t"] != nil ? [customArgs[@"t"] floatValue] : 533.0f; + argsModel.friction = customArgs[@"f"] != nil ? [customArgs[@"f"] floatValue] : 14.0f; + argsModel.velocity = customArgs[@"v"] != nil ? [customArgs[@"v"] floatValue] : -2.0f; + model.argsModel = argsModel; + } + } + return model; +} + + +@end diff --git a/src/iOS/GaiaMotionCurve/GaiaMotionCurve.h b/src/iOS/GaiaMotionCurve/GaiaMotionCurve.h new file mode 100644 index 0000000..683481b --- /dev/null +++ b/src/iOS/GaiaMotionCurve/GaiaMotionCurve.h @@ -0,0 +1,32 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +//! Project version number for GaiaMotionCurve. +FOUNDATION_EXPORT double GaiaMotionCurveVersionNumber; + +//! Project version string for GaiaMotionCurve. +FOUNDATION_EXPORT const unsigned char GaiaMotionCurveVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + +#import +#import +#import +#import +#import +#import +#import \ No newline at end of file diff --git a/src/iOS/GaiaMotionCurve/NSValue+GMCCGFloat.h b/src/iOS/GaiaMotionCurve/NSValue+GMCCGFloat.h new file mode 100644 index 0000000..f0b2ce2 --- /dev/null +++ b/src/iOS/GaiaMotionCurve/NSValue+GMCCGFloat.h @@ -0,0 +1,29 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSValue (GMCCGFloat) + +- (CGFloat)gmc_CGFloatValue; + ++ (NSValue *)gmc_valueWithCGFloat:(CGFloat)floatValue; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/iOS/GaiaMotionCurve/NSValue+GMCCGFloat.m b/src/iOS/GaiaMotionCurve/NSValue+GMCCGFloat.m new file mode 100644 index 0000000..9a74eea --- /dev/null +++ b/src/iOS/GaiaMotionCurve/NSValue+GMCCGFloat.m @@ -0,0 +1,33 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#import "NSValue+GMCCGFloat.h" + +@implementation NSValue (GMCCGFloat) + +- (CGFloat)gmc_CGFloatValue { + CGFloat floatValue; + [self getValue:&floatValue]; + return floatValue; +} + ++ (NSValue *)gmc_valueWithCGFloat:(CGFloat)floatValue { + NSValue *returnValue = [NSValue valueWithBytes:&floatValue + objCType:@encode(CGFloat)]; + return returnValue; +} + + +@end diff --git a/src/iOS/GaiaMotionCurve/NSValue+GMCUIColor.h b/src/iOS/GaiaMotionCurve/NSValue+GMCUIColor.h new file mode 100644 index 0000000..494c419 --- /dev/null +++ b/src/iOS/GaiaMotionCurve/NSValue+GMCUIColor.h @@ -0,0 +1,32 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#import +#import + +@class UIColor; + +NS_ASSUME_NONNULL_BEGIN + +@interface NSValue (GMCUIColor) + + +- (CGColorRef)gmc_CGColorValue; + ++ (NSValue *)gmc_valueWithCGColor:(CGColorRef)color; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/iOS/GaiaMotionCurve/NSValue+GMCUIColor.m b/src/iOS/GaiaMotionCurve/NSValue+GMCUIColor.m new file mode 100644 index 0000000..c805249 --- /dev/null +++ b/src/iOS/GaiaMotionCurve/NSValue+GMCUIColor.m @@ -0,0 +1,30 @@ +// Copyright (c) 2022, Alibaba Group Holding Limited; +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#import "NSValue+GMCUIColor.h" + +@implementation NSValue (GMCUIColor) + ++ (NSValue *)gmc_valueWithCGColor:(CGColorRef)color { + return [NSValue valueWithBytes:&color objCType:@encode(CGColorRef)]; +} + +- (CGColorRef)gmc_CGColorValue { + CGColorRef color; + [self getValue:&color]; + return color; +} + +@end diff --git a/src/iOS/GaiaMotionCurveTests/GaiaMotionCurveTests.m b/src/iOS/GaiaMotionCurveTests/GaiaMotionCurveTests.m new file mode 100644 index 0000000..d3ec7ca --- /dev/null +++ b/src/iOS/GaiaMotionCurveTests/GaiaMotionCurveTests.m @@ -0,0 +1,36 @@ +// +// GaiaMotionCurveTests.m +// GaiaMotionCurveTests +// +// Created by 周荣慧 on 2022/1/4. +// + +#import + +@interface GaiaMotionCurveTests : XCTestCase + +@end + +@implementation GaiaMotionCurveTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/src/iOS/Podfile b/src/iOS/Podfile new file mode 100644 index 0000000..b649ae2 --- /dev/null +++ b/src/iOS/Podfile @@ -0,0 +1,7 @@ + +target 'GaiaMotionCurve' do + platform :ios, '9.0' + # Uncomment the next line if you're using Swift or would like to use dynamic frameworks + # use_frameworks! + +end