ジャイロスコープを使用しているときに、scrollView をドラッグしてスクロールすると、3D 効果の画像が揺れるという問題が発生しました。
3D効果を実現するコード
- (void)updateWithGravityX:(double)gravityX
gravityY:(double)gravityY
gravityZ:(double)gravityZ
{
//因为在斜向上45度角的时候,gravity的值是-0.5,设计要求以这个位置为基准,所以要减去-0.5
gravityY -= (-0.5);
gravityY *= 2;
//最大的便宜量是maxoffset,所以gravityY最大为1
gravityY = MIN(1, MAX(-1, gravityY));
gravityX *= 2;
gravityX = MIN(1, MAX(-1, gravityX));
double timeInterval = sqrt(pow((gravityX - lastGravigyX),2) + pow((gravityY - lastGravityY), 2)) * deviceMotionUpdateInterval;
NSString *animationKey = @"positionAnimation";
CGPoint newBackImageViewCenter = self.backImageView.center;
newBackImageViewCenter.x = (newBackImageViewCenter.x - gravityX * maxOffset);
newBackImageViewCenter.y = (newBackImageViewCenter.y + gravityY * maxOffset);
CABasicAnimation *backImageViewAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
backImageViewAnimation.fromValue = [NSValue valueWithCGPoint:backImageViewCenter];
backImageViewAnimation.toValue = [NSValue valueWithCGPoint:newBackImageViewCenter];
backImageViewAnimation.duration = timeInterval;
backImageViewAnimation.fillMode = kCAFillModeForwards;
backImageViewAnimation.removedOnCompletion = NO;
[self.backImageView.layer removeAnimationForKey:animationKey];
[self.backImageView.layer addAnimation:backImageViewAnimation forKey:animationKey];
CGPoint newFrontImageViewCenter = self.frontImageView.center;
newFrontImageViewCenter.x += gravityX * maxOffset;
newFrontImageViewCenter.y -= gravityY * maxOffset;
CABasicAnimation *frontImageViewAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
frontImageViewAnimation.fromValue = [NSValue valueWithCGPoint:frontImageViewCenter];
frontImageViewAnimation.toValue = [NSValue valueWithCGPoint:newFrontImageViewCenter];
frontImageViewAnimation.duration = timeInterval;
frontImageViewAnimation.fillMode = kCAFillModeForwards;
frontImageViewAnimation.removedOnCompletion = NO;
[self.frontImageView.layer removeAnimationForKey:animationKey];
[self.frontImageView.layer addAnimation:frontImageViewAnimation forKey:animationKey];
CGPoint newSecondFrontImageViewCenter = self.smallMovementView.center;
if (self.smallMovementView == self.secondFrontImageView) {
newSecondFrontImageViewCenter.x += gravityX * maxOffset/3;
newSecondFrontImageViewCenter.y -= gravityY * maxOffset/3;
} else if (self.smallMovementView == self.middleImageView) {
newSecondFrontImageViewCenter.x -= gravityX * maxOffset/3;
newSecondFrontImageViewCenter.y += gravityY * maxOffset/3;
}
CABasicAnimation *secondfrontImageViewAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
secondfrontImageViewAnimation.fromValue = [NSValue valueWithCGPoint:secondFrontImageViewCenter];
secondfrontImageViewAnimation.toValue = [NSValue valueWithCGPoint:newSecondFrontImageViewCenter];
secondfrontImageViewAnimation.duration = timeInterval;
secondfrontImageViewAnimation.fillMode = kCAFillModeForwards;
secondfrontImageViewAnimation.removedOnCompletion = NO;
[self.smallMovementView.layer removeAnimationForKey:animationKey];
[self.smallMovementView.layer addAnimation:secondfrontImageViewAnimation forKey:animationKey];
backImageViewCenter = newBackImageViewCenter;
frontImageViewCenter = newFrontImageViewCenter;
secondFrontImageViewCenter = newSecondFrontImageViewCenter;
}
上記のコードから、画像のジッターは画像の位置の突然変異によって引き起こされていることがわかります。スクロール
ビューがスクロールしているときには突然変異がありますが、スクロールしていないときには突然変異はありません。
位置の変化を印刷してみたところ、印刷頻度が高すぎることが判明したため、時間を 1/40 から 1 に変更しました。
ドラッグしなければ、位置の変化に突然変異が発生しないことがわかりました。しかし、時刻の
出力は依然として比較的頻繁に発生するため、時刻を 10 に変更しました。このとき、
ドラッグするかどうかに関係なく、各変更のサイズが非常に大きく、16 を超えてしまうという問題が見つかりまし
た。次の想像
このことから、設定したジャイロスコープの更新頻度が十分高くなく、位置を更新するまでに長い距離を移動することになり、突然変異現象が発生していると推測できます。
更新された位置は、ジャイロスコープ、つまり、
ジャイロスコープがコールバックを行う前に大きな位置を移動したため、ジッターが発生しました。
ジャイロスコープのコールバックが非常に頻繁である場合、少し距離を移動するとジャイロスコープが
コールバックし、この時点ではジッターの影響はありません。
このことから、ジャイロスコープの修正時期を考えることができますが、
最新の Apple 携帯電話の更新周波数は 120hz であるため、
ここでは 1/120 に設定します。
deviceMotionUpdateInterval = 1 / 120.0;
- (CMMotionManager *)motionManager
{
if (!_motionManager) {
_motionManager = [[CMMotionManager alloc] init];
_motionManager.deviceMotionUpdateInterval = deviceMotionUpdateInterval;
}
return _motionManager;
}
このことから、ジッターが見つかった場合は、
位置に突然の変化があった可能性があると推測できます。