我想在我开发的应用中使用YouTube来使播放/暂停动画变形。看起来像动画here。我不确定如何去做。我知道我需要CABasicAnimation
和CAShapeLayer
。我知道我需要两个(或更多?)UIBezierPath
-s。一个是动画shapeLayer
的路径,另一个是动画toValue
的路径。像这样:
shape.path = pausePath().cgPath
let animation = CABasicAnimation(keyPath: "path")
animation.toValue = trainglePath().cgPath
animation.duration = 1
shape.add(animation, forKey: animation.keyPath)
但是我不确定如何走这些路。我有以下问题:
path.move(to: startPoint)
; path.addLine(to: endPoint1)
反对path.move(to: endPoint1)
; path.addLine(to: startPoint)
答案 0 :(得分:6)
使路径动画起作用的关键在于,起始路径和结束路径都必须具有相同数量的控制点。如果您看该动画,对我来说,就像初始的“播放”三角形被绘制为两个接触的闭合四边形一样,其中右四边形具有2个点,因此它看起来像一个三角形。然后动画将这些四边形的控制点分开,并将它们都变成暂停符号的矩形。
如果将其绘制在方格纸上,则在控制点之前和之后创建控件应该非常容易。
请考虑以下(非常粗糙的)插图。顶部显示分成两个四边形的“游戏”三角形,并显示为四边形的右侧部分(黄色),其右侧的点略微分开以显示该想法。实际上,您可以使用两个具有完全相同坐标的控制点来设置该四边形。
我用数字标记了每个四边形的控制点,以显示它们的绘制顺序(对于每个四边形:moveTo第一点,lineTo第二/第三/第四点)
底部的插图显示了已移动以给出暂停符号的点。
如果您创建CAShapeLayer的CABasicAnimation,并从顶部的控制点开始,到底部的控制点结束,那么您应该获得与链接的动画非常相似的动画。
答案 1 :(得分:4)
这是一个基于CAShapeLayer
的小型实现,基本上克隆了链接中描述的行为。
class PlayPauseButton: UIControl {
// public function can be called from out interface
func setPlaying(_ playing: Bool) {
self.playing = playing
animateLayer()
}
private (set) var playing: Bool = false
private let leftLayer: CAShapeLayer
private let rightLayer: CAShapeLayer
override init(frame: CGRect) {
leftLayer = CAShapeLayer()
rightLayer = CAShapeLayer()
super.init(frame: frame)
backgroundColor = UIColor.white
setupLayers()
}
required init?(coder aDecoder: NSCoder) {
fatalError("Not implemented")
}
private func setupLayers() {
layer.addSublayer(leftLayer)
layer.addSublayer(rightLayer)
leftLayer.fillColor = UIColor.black.cgColor
rightLayer.fillColor = UIColor.black.cgColor
addTarget(self,
action: #selector(pressed),
for: .touchUpInside)
}
@objc private func pressed() {
setPlaying(!playing)
}
private func animateLayer() {
let fromLeftPath = leftLayer.path
let toLeftPath = leftPath()
leftLayer.path = toLeftPath
let fromRightPath = rightLayer.path
let toRightPath = rightPath()
rightLayer.path = toRightPath
let leftPathAnimation = pathAnimation(fromPath: fromLeftPath,
toPath: toLeftPath)
let rightPathAnimation = pathAnimation(fromPath: fromRightPath,
toPath: toRightPath)
leftLayer.add(leftPathAnimation,
forKey: nil)
rightLayer.add(rightPathAnimation,
forKey: nil)
}
private func pathAnimation(fromPath: CGPath?,
toPath: CGPath) -> CAAnimation {
let animation = CABasicAnimation(keyPath: "path")
animation.duration = 0.33
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
animation.fromValue = fromPath
animation.toValue = toPath
return animation
}
override func layoutSubviews() {
leftLayer.frame = leftLayerFrame
rightLayer.frame = rightLayerFrame
leftLayer.path = leftPath()
rightLayer.path = rightPath()
}
private let pauseButtonLineSpacing: CGFloat = 10
private var leftLayerFrame: CGRect {
return CGRect(x: 0,
y: 0,
width: bounds.width * 0.5,
height: bounds.height)
}
private var rightLayerFrame: CGRect {
return leftLayerFrame.offsetBy(dx: bounds.width * 0.5,
dy: 0)
}
private func leftPath() -> CGPath {
if playing {
let bound = leftLayer.bounds
.insetBy(dx: pauseButtonLineSpacing,
dy: 0)
.offsetBy(dx: -pauseButtonLineSpacing,
dy: 0)
return UIBezierPath(rect: bound).cgPath
}
return leftLayerPausedPath()
}
private func rightPath() -> CGPath {
if playing {
let bound = rightLayer.bounds
.insetBy(dx: pauseButtonLineSpacing,
dy: 0)
.offsetBy(dx: pauseButtonLineSpacing,
dy: 0)
return UIBezierPath(rect: bound).cgPath
}
return rightLayerPausedPath()
}
private func leftLayerPausedPath() -> CGPath {
let y1 = leftLayerFrame.width * 0.5
let y2 = leftLayerFrame.height - leftLayerFrame.width * 0.5
let path = UIBezierPath()
path.move(to:CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: leftLayerFrame.width,
y: y1))
path.addLine(to: CGPoint(x: leftLayerFrame.width,
y: y2))
path.addLine(to: CGPoint(x: 0,
y: leftLayerFrame.height))
path.close()
return path.cgPath
}
private func rightLayerPausedPath() -> CGPath {
let y1 = rightLayerFrame.width * 0.5
let y2 = rightLayerFrame.height - leftLayerFrame.width * 0.5
let path = UIBezierPath()
path.move(to:CGPoint(x: 0, y: y1))
path.addLine(to: CGPoint(x: rightLayerFrame.width,
y: rightLayerFrame.height * 0.5))
path.addLine(to: CGPoint(x: rightLayerFrame.width,
y: rightLayerFrame.height * 0.5))
path.addLine(to: CGPoint(x: 0,
y: y2))
path.close()
return path.cgPath
}
}
代码应该很简单。这是使用方法,
let playButton = PlayPauseButton(frame: CGRect(x: 0,
y: 0,
width: 200,
height: 200))
view.addSubview(playButton)
您不需要从外部处理任何东西。该按钮本身可以处理动画。您可以使用目标/动作来更改检测按钮的单击。另外,您可以使用公共方法从外部添加动画状态
playButton.setPlaying(true) // animate to playing
playButton.setPlaying(false) // animate to paused state
这是它的外观