gpt4 book ai didi

Swift:沿动画路径动画对象

转载 作者:行者123 更新时间:2023-12-04 17:27:21 25 4
gpt4 key购买 nike

我想为围绕以脉冲方式扩展的圆圈旋转的小红点设置动画(从小到大,然后从小开始)。似乎那个小点一直在围绕原始形状旋转,并且没有考虑到它正在扩展的圆圈......我在代码中有这个:

// MARK: - Properties

private lazy var containerView = UIView()

let littleCircleRadius: CGFloat = 10

private lazy var littleRedDot: CALayer = {
let layer = CALayer()
layer.backgroundColor = UIColor.red.cgColor
let littleDotSize = CGSize(width: 10, height: 10)
layer.frame = CGRect(x: containerView.bounds.center.x - littleDotSize.width / 2,
y: containerView.bounds.center.y - littleCircleRadius - littleDotSize.width/2 ,
width: littleDotSize.width,
height: littleDotSize.height)
return layer
}()

private lazy var littleCircleLayer: CAShapeLayer = {
let layer = CAShapeLayer()
layer.lineWidth = 1.5
layer.lineCap = .round
layer.strokeColor = UIColor.black.cgColor
layer.fillColor = UIColor.clear.cgColor
return layer
}()

// MARK: - Setup
func setup() {
view.addSubview(containerView)
containerView.frame = CGRect(x: 40, y: 200, width: 300, height: 300)
containerView.backgroundColor = UIColor.gray.withAlphaComponent(0.2)

littleCircleLayer.path = makeArcPath(arcCenter: containerView.bounds.center, radius: 10)
containerView.layer.addSublayer(littleCircleLayer)
containerView.layer.addSublayer(littleRedDot)
}

// MARK: - Animations
func animate() {
CATransaction.begin()
CATransaction.setAnimationDuration(1.5)
animateLittleRedDotRotation()
animateCircleExpanding()
CATransaction.commit()
}

func animateLittleRedDotRotation() {
let anim = CAKeyframeAnimation(keyPath: "position")
anim.duration = 1.5
anim.rotationMode = .rotateAuto
anim.repeatCount = Float.infinity
anim.path = littleCircleLayer.path
littleRedDot.add(anim, forKey: "rotate")
}

func animateCircleExpanding() {
let maxCircle = makeArcPath(arcCenter: containerView.bounds.center, radius: 100)
let circleExpandingAnim = CABasicAnimation(keyPath: "path")
circleExpandingAnim.fromValue = littleCircleLayer.path
circleExpandingAnim.toValue = maxCircle
circleExpandingAnim.repeatCount = Float.infinity
circleExpandingAnim.duration = 1.5
littleCircleLayer.add(circleExpandingAnim, forKey: "pulseCircuitAnimation")
}

这会产生以下效果:

enter image description here

但是我想实现小点沿着扩展的圆路径旋转(因为它从小圆到大圆的动画),而不是原来的小圆路径。有什么想法吗?

最佳答案

使用 CoreAnimation 根据路径为红点的位置设置动画假定路径没有改变。从理论上讲,您可以定义一条反射(reflect)不断扩大的圆圈的螺旋路径。就个人而言,我只使用 CADisplayLink,这是一种专为屏幕刷新而优化设计的特殊计时器,并完全取消 CoreAnimation 调用。例如

func startDisplayLink() {
let displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
displayLink.add(to: .main, forMode: .common)
}

@objc func handleDisplayLink(_ displayLink: CADisplayLink) {
let percent = CGFloat(displayLink.timestamp).truncatingRemainder(dividingBy: duration) / duration
let radius = ...
let center = containerView.bounds.center
circleLayer.path = makeArcPath(arcCenter: center, radius: radius)
let angle = percent * .pi * 2
let dotCenter = CGPoint(x: center.x + cos(angle) * radius, y: center.y + sin(angle) * radius)
redDot.path = makeArcPath(arcCenter: dotCenter, radius: 5)
}

产生:

enter image description here


完整示例:

class ViewController: UIViewController {

private let radiusRange: ClosedRange<CGFloat> = 10...100
private let duration: CGFloat = 1.5

private lazy var containerView: UIView = {
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
return containerView
}()

private lazy var redDot: CAShapeLayer = {
let layer = CAShapeLayer()
layer.fillColor = UIColor.red.cgColor
return layer
}()

private lazy var circleLayer: CAShapeLayer = {
let layer = CAShapeLayer()
layer.lineWidth = 1.5
layer.strokeColor = UIColor.black.cgColor
layer.fillColor = UIColor.clear.cgColor
return layer
}()

private weak var displayLink: CADisplayLink?

override func viewDidLoad() {
super.viewDidLoad()

setup()
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startDisplayLink()
}

override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
stopDisplayLink()
}
}

// MARK: Private utility methods

private extension ViewController {
func setup() {
addContainer()

containerView.layer.addSublayer(circleLayer)
containerView.layer.addSublayer(redDot)
}

func addContainer() {
view.addSubview(containerView)

NSLayoutConstraint.activate([
containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
containerView.topAnchor.constraint(equalTo: view.topAnchor),
containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}

func makeArcPath(arcCenter: CGPoint, radius: CGFloat) -> CGPath {
UIBezierPath(arcCenter: arcCenter, radius: radius, startAngle: 0, endAngle: .pi * 2, clockwise: true).cgPath
}
}

// MARK: - DisplayLink related methods

private extension ViewController {
func startDisplayLink() {
stopDisplayLink() // stop existing display link, if any

let displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
displayLink.add(to: .main, forMode: .common)
self.displayLink = displayLink
}

func stopDisplayLink() {
displayLink?.invalidate()
}

@objc func handleDisplayLink(_ displayLink: CADisplayLink) {
let percent = CGFloat(displayLink.timestamp).truncatingRemainder(dividingBy: duration) / duration
let radius = radiusRange.percent(percent)
let center = containerView.bounds.center
circleLayer.path = makeArcPath(arcCenter: center, radius: radius)
let angle = percent * .pi * 2
let dotCenter = CGPoint(x: center.x + cos(angle) * radius, y: center.y + sin(angle) * radius)
redDot.path = makeArcPath(arcCenter: dotCenter, radius: 5)
}
}

// MARK: - CGRect extension

extension CGRect {
var center: CGPoint { return CGPoint(x: midX, y: midY) }
}

// MARK: - ClosedRange extension

extension ClosedRange where Bound: FloatingPoint {
func percent(_ percent: Bound) -> Bound {
(upperBound - lowerBound) * percent + lowerBound
}
}

关于Swift:沿动画路径动画对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62443962/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com