简体   繁体   中英

How to animate only a percentage of the full CALayer animation along a UIBezierPath?

I manage to animate an CALayer along a UIBezierPath.

What i'm trying to accomplish is to animate only a percentage of the path, for example, only 25% of the path, with the layer staying at that position (at 25%).

What is the way to do this? Here is my code, it always animate the full path.

let aPath = UIBizierPath(CGPath: somePath)
let anim = CAKeyframeAnimation(keyPath: "position")
anim.path = aPath.CGPath
anim.rotationMode = kCAAnimationRotateAuto
anim.repeatCount = 1
anim.fillMode = kCAFillModeForwards
anim.removedOnCompletion = false
anim.duration = 3.0
anim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

ticker.addAnimation(anim, forKey: "animate_ticker")

Simple change one property:

anim.repeatCount = 0.25    

Here is an excellent article on animation timing ,you can learn how to have even more fine-grained control over your animations.

Append:
1. To achieve what you want ,the closest way is to have an 25% subPath ,here is some helper method .

2.If you can stand the speed difference,Use the method above, and set back the position when animation ends:

ticker.position = ticker.presentationLayer().position

Not only CGPath is opaque, but also CAAnimation doesn't support any updates or notifications for ongoing animations (ie after started but before finished). The only entities involved are animation itself and CALayer -s it is applied to.

So, options are:

(some options may sound too scary but they are not, so I made an example project, see below)

repeatCount

As wj2061 mentioned is his answer you can tweak animation's repeatCount . Cons is it will animate 0.25 of animation time, not 0.25 of path

CAShapeLayer

If, by any chance, you can represent your ticker with CAShapeLayer segment then you can animate strokeStart and strokeEnd so it will look like segment is moving along the path

Clever repeatCount

You can calculate such repeatCount value that animation will stop at 0.25 of path.

  • grab some bezier lib
  • extract bezier from timing function with getControlPointAtIndex:
  • solve bezier to get its "progress value" (usually named t ) for which bezier's 'y' value will match to your "required progress" (0.25)
  • calculate bezier's 'x' value for that t - this is exact value of repeatCount you need

Animating with CAAnimation, all by yourself

kind of advanced

  • grab some bezier lib
  • create custom CALayer subclass with dynamic property, let's say rideProgress
  • add synthesized properties for bezier path (from bezier lib, not CGPath) and sublayer (say, rideLayer ) to animate
  • override needsDisplayForKey: for rideProgress key and initWithLayer: for all introduced properties BUT use self.rideLayer?.presentationLayer() instead of copying rideLayer
  • override one of these: drawInContext: and draw what you need in it; or (better) display , in display retrieve current rideProgress value from presentationLayer and update sublayer, then call super. Either way, use bezier lib to calculate position and rotation for sublayer accordingly to current rideProgress value
  • don't forget to CATransaction.setDisableActions(true) before setting any animatable property of any layer
  • finally, use any kind of CAAnimation or implicit animation on rideProgress property

Split path

Again, suggested by wj2061 . You can split the path so one of halves will represent 0.25 of original


Here is example implementation of everything above EXCEPT "Split path". I didn't run any field tests on this code, it's just a working concept. XCode 7.3.1, Swift.


Materials used: Wiki on Cubic function , Great article about operations on beziers , Cubic equations solving method line-by-line

Did you try to set fromValue and toValue properties? It should work if you set fromValue to 0.0 and toValue to 0.25

I was running into the same issue, and I wanted to animate the drawing of a Path but only a percentage of it.

What I ended up doing was setting the layer's strokeStart and strokeEnd to same floating point values as the animation.

So in your example, try setting anim.fromValue = 0.0f , anim.toValue = 0.6f and also ticker.strokeStart=0.0f and ticker.strokeEnd = 0.6f .

This successfully drew only 60% of the path and animated it too!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM