iOS 使用陀螺仪实现裸眼3d效果

请添加图片描述

请添加图片描述

直接上完整代码

//
//  BannerView.m
//  Test
//
//  Created by liubo on 2023/7/20.
//

#import "LB3DBannerView.h"
#import <Masonry/Masonry.h>
#import <CoreMotion/CoreMotion.h>

@interface LB3DBannerView ()

{
    CGFloat maxOffset;
    CGFloat lastGravigyX;
    CGFloat lastGravityY;
    NSTimeInterval deviceMotionUpdateInterval;
    CGPoint frontImageViewCenter;
    CGPoint secondFrontImageViewCenter;
    CGPoint backImageViewCenter;
}

@property (nonatomic, strong) UIImageView *frontImageView;

@property (nonatomic, strong) UIImageView *secondFrontImageView;

@property (nonatomic, strong) UIImageView *middleImageView;

@property (nonatomic, strong) UIImageView *backImageView;

@property (nonatomic, strong) CMMotionManager *motionManager;

@end

@implementation LB3DBannerView

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        [self setUpUI];
        [self setUpContraints];
        self.clipsToBounds = YES;
    }
    return self;
}

- (void)setUpUI
{
    maxOffset = 15;
    deviceMotionUpdateInterval = 1 / 120.0;
    frontImageViewCenter = CGPointZero;
    backImageViewCenter = CGPointZero;
    secondFrontImageViewCenter = CGPointZero;
    [self addSubview:self.backImageView];
    [self addSubview:self.middleImageView];
    [self addSubview:self.secondFrontImageView];
    [self addSubview:self.frontImageView];
}

- (void)setUpContraints {
    [self.backImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(self.mas_width).with.offset(maxOffset * 2);
        make.height.equalTo(self.mas_height).with.offset(maxOffset * 2);
        make.centerX.mas_equalTo(0);
        make.centerY.mas_equalTo(0);
    }];
    
    [self.middleImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(self.mas_width).with.offset(maxOffset * 2/3);
        make.height.equalTo(self.mas_height).with.offset(maxOffset * 2/3);
        make.centerX.mas_equalTo(0);
        make.centerY.mas_equalTo(0);
    }];
    
    [self.secondFrontImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(self.mas_width).with.offset(maxOffset);
        make.height.equalTo(self.mas_height).with.offset(maxOffset);
        make.centerX.mas_equalTo(0);
        make.centerY.mas_equalTo(0);
    }];
    
    [self.frontImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(self.mas_width).with.offset(maxOffset *  2);
        make.height.equalTo(self.mas_height).with.offset(maxOffset * 2);
        make.centerX.centerY.mas_equalTo(0);
    }];
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    backImageViewCenter = self.center;
    frontImageViewCenter = self.center;
}

- (void)start
{
    [self startMotion];
}

- (void)stop
{
    [self.motionManager stopDeviceMotionUpdates];
}

- (void)updateWithArray:(NSArray *)imageNames
{
    self.frontImageView.image = [UIImage imageNamed:imageNames[0]];
    self.secondFrontImageView.image = [UIImage imageNamed:imageNames[1]];
    self.middleImageView.image = [UIImage imageNamed:imageNames[2]];
    self.backImageView.image = [UIImage imageNamed:imageNames[3]];

}

- (void)startMotion
{
    if (!self.motionManager.isDeviceMotionAvailable) {
        return;
    }
    
    __weak LB3DBannerView *weakSelf = self;
    [self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
        if (!motion) {
            return;
        }
        [weakSelf updateWithGravityX:motion.gravity.x
                            gravityY:motion.gravity.y
                            gravityZ:motion.gravity.z];
    }];
}

- (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.middleImageView.center;
    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.middleImageView.layer removeAnimationForKey:animationKey];
    [self.middleImageView.layer addAnimation:secondfrontImageViewAnimation forKey:animationKey];
    
    backImageViewCenter = newBackImageViewCenter;
    frontImageViewCenter = newFrontImageViewCenter;
    secondFrontImageViewCenter = newSecondFrontImageViewCenter;
}

#pragma mark - lazy load

- (UIImageView *)frontImageView
{
    if (!_frontImageView) {
        _frontImageView = [[UIImageView alloc] init];
        _frontImageView.contentMode = UIViewContentModeScaleAspectFill;
    }
    return _frontImageView;
}

- (UIImageView *)secondFrontImageView
{
    if (!_secondFrontImageView) {
        _secondFrontImageView = [[UIImageView alloc] init];
        _secondFrontImageView.contentMode = UIViewContentModeScaleAspectFill;
    }
    return _secondFrontImageView;
}

- (UIImageView *)middleImageView
{
    if (!_middleImageView) {
        _middleImageView = [[UIImageView alloc] init];
        _middleImageView.contentMode = UIViewContentModeScaleAspectFill;
    }
    return _middleImageView;
}

- (UIImageView *)backImageView
{
    if (!_backImageView) {
        _backImageView = [[UIImageView alloc] init];
        _backImageView.contentMode = UIViewContentModeScaleAspectFill;
    }
    return _backImageView;
}

- (CMMotionManager *)motionManager
{
    if (!_motionManager) {
        _motionManager = [[CMMotionManager alloc] init];
        _motionManager.deviceMotionUpdateInterval = deviceMotionUpdateInterval;
    }
    return _motionManager;
}

@end

猜你喜欢

转载自blog.csdn.net/LIUXIAOXIAOBO/article/details/132892573