Reputation: 341
Basically my goal is to make an atom-like movement similar to this around my logo https://media.giphy.com/media/9oHZQ2gEez8ti/giphy.gif
let rect = CGRect(origin: CGPoint(x: Logo.position.x - (Logo.frame.width/3.6), y: Logo.position.y - (Logo.frame.height/2) - kViewSize.height/32), size: CGSize(width: Logo.frame.width/2, height: Logo.frame.height + kViewSize.height/16))
let ellipse = CGPath(ellipseIn: rect, transform: nil)
YellowMagic.run(SKAction.repeatForever(SKAction.follow(ellipse, asOffset: false, orientToPath: false, duration: 1.5)))
With the above code I get the middle ellipse, and the sprite runs around the path as desired.
The problem is getting the other two ellipses by rotating the path. I don't think that rotating a path is possible, so I tried rotating the rect with the following code.
var rect2 = rect
rect2 = rect2.applying(CGAffineTransform(rotationAngle: CGFloat(M_PI/4)))
let ellipse2 = CGPath(ellipseIn: rect2, transform: nil)
The above code doesn't rotate the rectangle, in fact it makes the rectangle a square. There must be a simpler solution that I am missing here. Any help is greatly appretiated. Thanks!
Upvotes: 1
Views: 2147
Reputation: 341
I modified the function found here and produced the following function:
func createPathRotatedAroundBoundingBoxCenter(path: CGPath, radians: CGFloat) -> CGPath {
let bounds = path.boundingBox; // might want to use CGPathGetPathBoundingBox
let center = CGPoint(x: bounds.midX, y: bounds.midY)
var transform = CGAffineTransform(translationX: 0, y: 0)
transform = transform.translatedBy(x: center.x, y: center.y)
transform = transform.rotated(by: radians)
transform = transform.translatedBy(x: -center.x, y: -center.y)
return path.copy(using: &transform)!
}
I then modified my code for the desired result.
let rect = CGRect(origin: CGPoint(x: Logo.position.x - (Logo.frame.width/3.6), y: Logo.position.y - (Logo.frame.height/2) - kViewSize.height/32), size: CGSize(width: Logo.frame.width/2, height: Logo.frame.height + kViewSize.height/16))
let ellipse = CGPath(ellipseIn: rect, transform: nil)
let ellipse2a = CGPath(ellipseIn: rect, transform: nil)
let ellipse3a = CGPath(ellipseIn: rect, transform: nil)
let ellipse2 = createPathRotatedAroundBoundingBoxCenter(path: ellipse2b, radians: CGFloat(M_PI/4.0))
let ellipse3 = createPathRotatedAroundBoundingBoxCenter(path: ellipse3b, radians: CGFloat(-M_PI/4.0))
YellowMagic.run(SKAction.repeatForever(SKAction.follow(ellipse, asOffset: false, orientToPath: false, duration: 1)))
RedMagic.run(SKAction.repeatForever(SKAction.follow(ellipse2, asOffset: false, orientToPath: false, duration: 1)))
GreenMagic.run(SKAction.repeatForever(SKAction.follow(ellipse3, asOffset: false, orientToPath: false, duration: 1)))
Upvotes: 0
Reputation: 3580
Goal to achieve
My Xcode example project
https://github.com/junaidios/Oval-path-Animation
Animation View
My try
Adding Center Circle
let centerCirclePath = UIBezierPath(ovalIn: CGRect(x: 162, y: 188, width: 20, height: 20))
colorCircleLayerCenter.setFill()
centerCirclePath.fill()
Adding Ovals
let bezierPath1 = UIBezierPath()
bezierPath1.move(to: CGPoint(x: 145.5, y: 213.93))
bezierPath1.addCurve(to: CGPoint(x: 84.68, y: 131.46), controlPoint1: CGPoint(x: 83.75, y: 163.11), controlPoint2: CGPoint(x: 79.26, y: 136.87))
bezierPath1.addCurve(to: CGPoint(x: 193.5, y: 178.49), controlPoint1: CGPoint(x: 87.4, y: 128.75), controlPoint2: CGPoint(x: 130.21, y: 128.39))
bezierPath1.addCurve(to: CGPoint(x: 258.01, y: 269.86), controlPoint1: CGPoint(x: 256.33, y: 228.23), controlPoint2: CGPoint(x: 262.32, y: 266.63))
bezierPath1.addCurve(to: CGPoint(x: 145.5, y: 213.93), controlPoint1: CGPoint(x: 249.34, y: 276.35), controlPoint2: CGPoint(x: 207.25, y: 264.75))
bezierPath1.close()
let bezierPath2 = UIBezierPath()
bezierPath2.move(to: CGPoint(x: 145.5, y: 184.8))
bezierPath2.addCurve(to: CGPoint(x: 254.5, y: 131.5), controlPoint1: CGPoint(x: 195.5, y: 146.16), controlPoint2: CGPoint(x: 238.27, y: 131.5))
bezierPath2.addCurve(to: CGPoint(x: 200.5, y: 217.47), controlPoint1: CGPoint(x: 262.64, y: 131.5), controlPoint2: CGPoint(x: 261.5, y: 159.92))
bezierPath2.addCurve(to: CGPoint(x: 84.5, y: 272.49), controlPoint1: CGPoint(x: 139.94, y: 274.6), controlPoint2: CGPoint(x: 91.01, y: 272.49))
bezierPath2.addCurve(to: CGPoint(x: 145.5, y: 184.8), controlPoint1: CGPoint(x: 71.42, y: 272.49), controlPoint2: CGPoint(x: 95.5, y: 223.44))
bezierPath2.close()
let bezierPath3 = UIBezierPath()
bezierPath3.move(to: CGPoint(x: 143.5, y: 201.45))
bezierPath3.addCurve(to: CGPoint(x: 172, y: 89.5), controlPoint1: CGPoint(x: 143.5, y: 154.31), controlPoint2: CGPoint(x: 155.77, y: 89.5))
bezierPath3.addCurve(to: CGPoint(x: 200.5, y: 201.45), controlPoint1: CGPoint(x: 180.14, y: 89.5), controlPoint2: CGPoint(x: 200.5, y: 124.29))
bezierPath3.addCurve(to: CGPoint(x: 172, y: 307.5), controlPoint1: CGPoint(x: 200.5, y: 278.04), controlPoint2: CGPoint(x: 178.51, y: 307.5))
bezierPath3.addCurve(to: CGPoint(x: 143.5, y: 201.45), controlPoint1: CGPoint(x: 158.92, y: 307.5), controlPoint2: CGPoint(x: 143.5, y: 248.58))
bezierPath3.close()
let linLayer1 = CAShapeLayer()
linLayer1.path = bezierPath1.cgPath;
linLayer1.strokeColor = colorLine.cgColor
linLayer1.fillColor = UIColor.clear.cgColor
self.layer.addSublayer(linLayer1);
let lineLayer2 = CAShapeLayer()
lineLayer2.path = bezierPath2.cgPath;
lineLayer2.strokeColor = colorLine.cgColor
lineLayer2.fillColor = UIColor.clear.cgColor
self.layer.addSublayer(lineLayer2);
let lineLayer3 = CAShapeLayer()
lineLayer3.path = bezierPath3.cgPath;
lineLayer3.strokeColor = colorLine.cgColor
lineLayer3.fillColor = UIColor.clear.cgColor
self.layer.addSublayer(lineLayer3);
Adding Moving Circles on Ovals
let circleLayer1 = CAShapeLayer()
circleLayer1.path = UIBezierPath(ovalIn: CGRect(x: -10, y: -10, width: 20, height: 20)).cgPath;
circleLayer1.fillColor = colorCircleLayer1.cgColor
let circleLayer2 = CAShapeLayer()
circleLayer2.path = UIBezierPath(ovalIn: CGRect(x: -10, y: -10, width: 20, height: 20)).cgPath;
circleLayer2.fillColor = colorCircleLayer2.cgColor
let circleLayer3 = CAShapeLayer()
circleLayer3.path = UIBezierPath(ovalIn: CGRect(x: -10, y: -10, width: 20, height: 20)).cgPath;
circleLayer3.fillColor = colorCircleLayer3.cgColor
self.layer.addSublayer(circleLayer1);
self.layer.addSublayer(circleLayer2);
self.layer.addSublayer(circleLayer3);
Apply animation to circles
circleLayer1.add(self.addAnimation(ovalPath: bezierPath1), forKey: "1");
circleLayer2.add(self.addAnimation(ovalPath: bezierPath2), forKey: "2");
circleLayer3.add(self.addAnimation(ovalPath: bezierPath3), forKey: "3");
Animation Funcation
func addAnimation(ovalPath : UIBezierPath) -> CAKeyframeAnimation {
let animation = CAKeyframeAnimation()
animation.keyPath = "position"
animation.repeatCount = HUGE
animation.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionLinear);
animation.fillMode = kCAFillModeForwards;
animation.repeatCount = .infinity
animation.beginTime = 0.0
animation.duration = 2.5
animation.calculationMode = kCAAnimationPaced
animation.path = ovalPath.cgPath
return animation;
}
Final output
Upvotes: 4