Swift SceneKit网格角度数组循环动画仅生效最后值的原因与全数组动画实现方案咨询
问题分析
你碰到的这个情况,核心原因是循环会在极短时间内把所有动画事务一次性提交。SCNTransaction的动画是异步执行的,但你的循环执行速度远快于动画时长,所有rotateMesh调用里的变换操作会几乎同时触发,后面的变换会直接覆盖前面的,最终就只显示最后一组角度的动画效果。
解决方案
下面提供两种可靠的实现方式,帮你实现依次执行数组中所有角度的动画:
方案1:递归逐次执行动画
这种方式会在一个动画完成后,再启动下一个动画,能保证严格的顺序:
func animateMeshAngles(_ angles: [[Double]], index: Int = 0) { guard index < angles.count else { return } let val = angles[index] let oldTransform = mesh!.transform let xAngle = SCNMatrix4MakeRotation(degToRad(deg: val[0]), 1, 0, 0) let yAngle = SCNMatrix4MakeRotation(degToRad(deg: val[1]), 0, 1, 0) let zAngle = SCNMatrix4MakeRotation(degToRad(deg: val[2]), 0, 0, 1) let rotationMatrix = SCNMatrix4Mult(SCNMatrix4Mult(xAngle, yAngle), zAngle) SCNTransaction.begin() SCNTransaction.animationDuration = 3 // 设置动画完成后的回调,启动下一个动画 SCNTransaction.completionBlock = { self.animateMeshAngles(angles, index: index + 1) } mesh!.transform = SCNMatrix4Mult(rotationMatrix, oldTransform) SCNTransaction.commit() } // 调用方式 animateMeshAngles(meshAngles)
原理:利用SCNTransaction的completionBlock,在当前动画完全结束后,递归调用自身执行下一组角度的动画,完美保证顺序。
方案2:基于索引设置递增延迟
如果不想用递归,也可以给每个动画设置递增的延迟时间,让它们按顺序启动:
for (index, val) in meshAngles.enumerated() { DispatchQueue.main.asyncAfter(deadline: .now() + 3.0 * Double(index)) { let oldTransform = self.mesh!.transform let xAngle = SCNMatrix4MakeRotation(self.degToRad(deg: val[0]), 1, 0, 0) let yAngle = SCNMatrix4MakeRotation(self.degToRad(deg: val[1]), 0, 1, 0) let zAngle = SCNMatrix4MakeRotation(self.degToRad(deg: val[2]), 0, 0, 1) let rotationMatrix = SCNMatrix4Mult(SCNMatrix4Mult(xAngle, yAngle), zAngle) SCNTransaction.begin() SCNTransaction.animationDuration = 3 self.mesh!.transform = SCNMatrix4Mult(rotationMatrix, oldTransform) SCNTransaction.commit() } }
原理:通过enumerated()获取每个角度数组的索引,第一个动画延迟0秒(立即执行),第二个延迟3秒(第一个动画结束时启动),第三个延迟6秒,以此类推,确保每个动画在前一个完成后再开始。
你之前尝试的延迟无效,是因为循环中所有asyncAfter的延迟时间都是同一个固定值,导致所有动画几乎同时启动,还是会出现覆盖问题。
内容的提问来源于stack exchange,提问作者Alek Szczęsnowicz
相关产品推荐
相关产品推荐

