Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create spinning circle loading animation

I'm trying to create a spinning circle loader animation like in the following Android project (https://github.com/pedant/sweet-alert-dialog).

enter image description here

I don't need the entire popup dialog - just the spinning part. It changes colors and spins indefinitly (until I choose to dismiss it).

I'm kind of new to swift and I've never been the kind to do animations. Here's what I have so far (found code in similar project for iOS):

The layers setup:

    outlineLayer.position = CGPointMake(0,         0);     outlineLayer.path = outlineCircle     outlineLayer.fillColor = UIColor.clearColor().CGColor;     outlineLayer.strokeColor = UIColor(red: 150.0/255.0, green: 216.0/255.0, blue: 115.0/255.0, alpha: 1.0).CGColor;     outlineLayer.lineCap = kCALineCapRound     outlineLayer.lineWidth = 4;     outlineLayer.opacity = 0.1     self.layer.addSublayer(outlineLayer)      circleLayer.position = CGPointMake(0,         0);     circleLayer.path = path     circleLayer.fillColor = UIColor.clearColor().CGColor;     circleLayer.strokeColor = UIColor(red: 150.0/255.0, green: 216.0/255.0, blue: 115.0/255.0, alpha: 1.0).CGColor;     circleLayer.lineCap = kCALineCapRound     circleLayer.lineWidth = 4;     circleLayer.actions = [         "strokeStart": NSNull(),         "strokeEnd": NSNull(),         "transform": NSNull()     ]     self.layer.addSublayer(circleLayer) 

Animation:

let strokeStart = CABasicAnimation(keyPath: "strokeStart")     let strokeEnd = CABasicAnimation(keyPath: "strokeEnd")     let factor = 0.545     let timing = CAMediaTimingFunction(controlPoints: 0.3, 0.6, 0.8, 1.2)      strokeEnd.fromValue = 0.00     strokeEnd.toValue = 0.93     strokeEnd.duration = 10.0 * factor     strokeEnd.timingFunction = timing     strokeEnd.autoreverses = true      strokeStart.fromValue = 0.0     strokeStart.toValue = 0.68     strokeStart.duration =  10.0 * factor     strokeStart.beginTime =  CACurrentMediaTime() + 3.0 * factor     strokeStart.fillMode = kCAFillModeBackwards     strokeStart.timingFunction = timing     strokeStart.repeatCount = HUGE      circleLayer.strokeStart = 0.68     circleLayer.strokeEnd = 0.93      self.circleLayer.addAnimation(strokeEnd, forKey: "strokeEnd")     self.circleLayer.addAnimation(strokeStart, forKey: "strokeStart") 

but what I have is not nearly close and I have no idea where to go from here. What I'm doing is changing a value and running seeing how it affects but I feel like I'm lost here.

How can I achieve such animation like in the example?

like image 898
developer82 Avatar asked Oct 31 '15 16:10

developer82


People also ask

How to create an animated loading spinner using CSS?

Earlier, many web developers were using animated loading spinner .gifs. But CSS animations allow us to easily create a loading spinner. We are going to show how to create a simple circle spinning around its center. Create a <div> with a class name "spin" in the body section. Create a circle setting the width and the height of it.

How do I make a spinning circle in HTML?

Create a <div> with a class name "spin" in the body section. Create a circle setting the width and the height of it. Set the border-radius to 50% to make it rounded. Give color to the border. Give color to the spinner with the border-bottom-color property. Specify the animation, which has four values.

What makes the loading spinner below unique?

The loading spinner below is an SVG circle made up of three "dashes," which start as dots and then expand until they form a circle. The animation loops infinitely. What makes the loading spinner below unique is its gradient background color. It also contains span elements, which have their own gradient background colors.

How fast does the loading spinner spin?

The loading spinner below is animated to spin at a speed of two seconds, infinitely. It also contains a div with the word "Loading" that's also animated to become fully transparent and fully opaque as the loader spins. The loading spinner below is an SVG circle made up of three "dashes," which start as dots and then expand until they form a circle.


1 Answers

I didn't closely analyze the exact parameters of the animation, but this looks good to me:

spinner view

import UIKit  @IBDesignable class SpinnerView : UIView {      override var layer: CAShapeLayer {         get {             return super.layer as! CAShapeLayer         }     }      override class var layerClass: AnyClass {         return CAShapeLayer.self     }      override func layoutSubviews() {         super.layoutSubviews()         layer.fillColor = nil         layer.strokeColor = UIColor.black.cgColor         layer.lineWidth = 3         setPath()     }      override func didMoveToWindow() {         animate()     }      private func setPath() {         layer.path = UIBezierPath(ovalIn: bounds.insetBy(dx: layer.lineWidth / 2, dy: layer.lineWidth / 2)).cgPath     }      struct Pose {         let secondsSincePriorPose: CFTimeInterval         let start: CGFloat         let length: CGFloat         init(_ secondsSincePriorPose: CFTimeInterval, _ start: CGFloat, _ length: CGFloat) {             self.secondsSincePriorPose = secondsSincePriorPose             self.start = start             self.length = length         }     }      class var poses: [Pose] {         get {             return [                 Pose(0.0, 0.000, 0.7),                 Pose(0.6, 0.500, 0.5),                 Pose(0.6, 1.000, 0.3),                 Pose(0.6, 1.500, 0.1),                 Pose(0.2, 1.875, 0.1),                 Pose(0.2, 2.250, 0.3),                 Pose(0.2, 2.625, 0.5),                 Pose(0.2, 3.000, 0.7),             ]         }     }      func animate() {         var time: CFTimeInterval = 0         var times = [CFTimeInterval]()         var start: CGFloat = 0         var rotations = [CGFloat]()         var strokeEnds = [CGFloat]()          let poses = type(of: self).poses         let totalSeconds = poses.reduce(0) { $0 + $1.secondsSincePriorPose }          for pose in poses {             time += pose.secondsSincePriorPose             times.append(time / totalSeconds)             start = pose.start             rotations.append(start * 2 * .pi)             strokeEnds.append(pose.length)         }          times.append(times.last!)         rotations.append(rotations[0])         strokeEnds.append(strokeEnds[0])          animateKeyPath(keyPath: "strokeEnd", duration: totalSeconds, times: times, values: strokeEnds)         animateKeyPath(keyPath: "transform.rotation", duration: totalSeconds, times: times, values: rotations)          animateStrokeHueWithDuration(duration: totalSeconds * 5)     }      func animateKeyPath(keyPath: String, duration: CFTimeInterval, times: [CFTimeInterval], values: [CGFloat]) {         let animation = CAKeyframeAnimation(keyPath: keyPath)         animation.keyTimes = times as [NSNumber]?         animation.values = values         animation.calculationMode = .linear         animation.duration = duration         animation.repeatCount = Float.infinity         layer.add(animation, forKey: animation.keyPath)     }      func animateStrokeHueWithDuration(duration: CFTimeInterval) {         let count = 36         let animation = CAKeyframeAnimation(keyPath: "strokeColor")         animation.keyTimes = (0 ... count).map { NSNumber(value: CFTimeInterval($0) / CFTimeInterval(count)) }         animation.values = (0 ... count).map {             UIColor(hue: CGFloat($0) / CGFloat(count), saturation: 1, brightness: 1, alpha: 1).cgColor         }         animation.duration = duration         animation.calculationMode = .linear         animation.repeatCount = Float.infinity         layer.add(animation, forKey: animation.keyPath)     }  } 
like image 113
rob mayoff Avatar answered Sep 25 '22 07:09

rob mayoff