Created
December 22, 2014 19:48
-
-
Save d-ronnqvist/996b53b59d6f06b66235 to your computer and use it in GitHub Desktop.
Hexagon Ripple in a Swift Playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import AppKit | |
import QuartzCore | |
import XCPlayground | |
// Parameters that define the style | |
let hexSideLength: CGFloat = 15.0 | |
let hexLineWidth: CGFloat = 3.0 | |
let colors = [NSColor.redColor(), NSColor.cyanColor(), NSColor.greenColor(), NSColor.yellowColor(), NSColor.redColor()].map { $0.CGColor } | |
// The shape used at every point in the hex grid | |
let π = CGFloat(M_PI) | |
let hexAngle = 2.0*π/3.0 | |
func pointWithAngle(angle: CGFloat, distance: CGFloat, fromPoint: CGPoint) -> CGPoint { | |
return CGPoint( | |
x: fromPoint.x + distance * cos(angle), | |
y: fromPoint.y + distance * sin(angle) | |
) | |
} | |
let center = CGPointZero | |
let points = [0, 1, 2] | |
.map { -π/2.0 + hexAngle*$0 } | |
.map { pointWithAngle($0, hexSideLength, center) } | |
let path = CGPathCreateMutable() | |
for point in points { | |
CGPathMoveToPoint(path, nil, center.x, center.y) | |
CGPathAddLineToPoint(path, nil, point.x, point.y) | |
} | |
// A layer hosting view, to host the shape layers | |
let size = CGSize(width: 250, height: 200) | |
let view = NSView(frame: CGRect(origin: CGPointZero, size: size)) | |
let rootLayer = CALayer() | |
rootLayer.backgroundColor = NSColor.blackColor().CGColor | |
view.layer = rootLayer // for a layer _hosting_ view | |
view.wantsLayer = true | |
XCPShowView("hex view", view) | |
// The grid of points for the hex grid | |
var hexPoints:[CGPoint] = [] | |
let rowHeight = -cos(hexAngle)*hexSideLength + hexSideLength | |
var isEven = true | |
var row: CGFloat = hexSideLength | |
while row < view.frame.height { | |
let xOffset = isEven ? hexSideLength*sin(hexAngle) : 0.0 | |
var col: CGFloat = hexSideLength/2.0 - xOffset | |
while col < view.frame.width + hexSideLength { | |
hexPoints.append(CGPoint(x: col, y: row)) | |
col += 2.0*hexSideLength*sin(hexAngle) | |
} | |
row += rowHeight | |
isEven = !isEven | |
} | |
// The two animations (rotation and color) | |
let timing = CAMediaTimingFunction(controlPoints: 0.85, 0.0, 0.15, 1.0) | |
var rotation = CABasicAnimation(keyPath: "transform.rotation.z") | |
rotation.fromValue = 0.0 | |
rotation.byValue = -π/3.0 | |
rotation.duration = 1.0 | |
rotation.repeatCount = HUGE | |
rotation.timingFunction = timing | |
rotation.cumulative = true | |
var colorShift = CAKeyframeAnimation(keyPath: "strokeColor") | |
colorShift.values = colors | |
colorShift.duration = Double(colors.count-1)*rotation.duration | |
colorShift.repeatCount = HUGE | |
colorShift.timingFunctions = [timing, timing, timing] | |
let viewCenter = CGPoint(x: view.frame.midX, y: view.frame.midY) | |
func distanceToViewCenter(point: CGPoint) -> CGFloat { | |
return sqrt((point.x - viewCenter.x)*(point.x - viewCenter.x) + (point.y - viewCenter.y)*(point.y - viewCenter.y)) | |
} | |
for point in hexPoints { | |
let layer = CAShapeLayer() | |
layer.lineWidth = hexLineWidth | |
layer.position = point | |
layer.path = path | |
view.layer?.addSublayer(layer) | |
var timeOffset = Double(distanceToViewCenter(point)/view.frame.midX) | |
rotation.timeOffset = -timeOffset | |
colorShift.timeOffset = -timeOffset | |
layer.addAnimation(rotation, forKey: "spin") | |
layer.addAnimation(colorShift, forKey: "shift color") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment