iOS 轉(zhuǎn)盤(pán)動(dòng)畫(huà)效果實(shí)現(xiàn)

近期公司項(xiàng)目告一段落,閑來(lái)無(wú)事,看到山東中國(guó)移動(dòng)客戶(hù)端有個(gè)轉(zhuǎn)盤(pán)動(dòng)畫(huà)挺酷的。于是試著實(shí)現(xiàn)一下,看似簡(jiǎn)單,可在coding時(shí)卻發(fā)現(xiàn)不少坑,填坑的同時(shí)還順便復(fù)習(xí)了一下高中數(shù)學(xué)知識(shí)(三角函數(shù)),收獲不小。


Demo & 效果圖:

轉(zhuǎn)盤(pán)旋轉(zhuǎn)效果圖


1、首先初始化6個(gè)UIImageView


1)分析imgView的中心點(diǎn)位置
imgView的中心點(diǎn)位置

Δx = _radius * sin(α);
Δy = _radius * cos(α);
所以,
imgView.center.x = centerX + _radius * sin(currentAngle);
imgView.center.y = centerY - _radius * cos(currentAngle);

2)代碼


- (void)customUI {
    CGFloat centerX = CGRectGetWidth(self.frame) * 0.5f;
    CGFloat centerY = centerX;
    _centerPoint = CGPointMake(centerX, centerY);//中心點(diǎn)
    _blueView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];//藍(lán)色view
    _blueView.center = _centerPoint;
    _blueView.backgroundColor = [UIColor blueColor];
    [self addSubview:_blueView];
    UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];//紅色view
    redView.backgroundColor = [UIColor redColor];
    [_blueView addSubview:redView];
    
    _deltaAngle = M_PI / 3.0f;//6個(gè)imgView的間隔角度
    CGFloat currentAngle = 0;
    CGFloat imgViewCenterX = 0;
    CGFloat imgViewCenterY = 0;
    CGFloat imgViewW = 80;
    CGFloat imgViewH =imgViewW;
    _radius = centerX - imgViewW * 0.5f;//imgView.center到self.center的距離
    for (int i = 0; i < 6; i++) {
        currentAngle = _deltaAngle * i;
        imgViewCenterX = centerX + _radius * sin(currentAngle);
        imgViewCenterY = centerY - _radius * cos(currentAngle);
        UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, imgViewW, imgViewH)];
        imgView.tag = kTag + i;
        imgView.center = CGPointMake(imgViewCenterX, imgViewCenterY);
        imgView.image = [UIImage imageNamed:[NSString stringWithFormat:@"circle%d", i]];
        [self addSubview:imgView];
    }
}


2、轉(zhuǎn)起來(lái)


1)計(jì)算任意點(diǎn)相對(duì)于原點(diǎn)(O)及x軸的夾角?



A點(diǎn)相對(duì)于原點(diǎn)及x軸角度

以點(diǎn)A為例:


CGFloat currentPointRadius = sqrt(pow(currentPoint.y - _centerPoint.y, 2) + pow(currentPoint.x - _centerPoint.x, 2));


所以,? = arccos((A.x - O.x) / OA),CGFloat curentPointAngle = acos((currentPoint.x - _centerPoint.x) / currentPointRadius); ?。?!注意,此處有坑


坑在哪?cos(x)和sin(x)的周期是2π,所以必定在求arccos(x)時(shí)一個(gè)值對(duì)應(yīng)兩個(gè)角度,此時(shí)需要判斷點(diǎn)是在哪個(gè)象限(對(duì)于cos函數(shù)來(lái)說(shuō),要判斷點(diǎn)在第一、二象限還是在第三、四象限,如果是sin函數(shù),則應(yīng)該判斷點(diǎn)是第二、三象限還是在第一、四象限)再進(jìn)行計(jì)算。

下面是以cos函數(shù)分析:
如圖003,點(diǎn)C和點(diǎn)D,C在第二象限,D在第三象限,它們對(duì)應(yīng)的值相同但對(duì)應(yīng)的角度不同。
如果是C點(diǎn),則CGFloat curentPointAngle = acos((currentPoint.x - _centerPoint.x) / currentPointRadius);
如果是D點(diǎn),則curentPointAngle = (π - curentPointAngle) + π = 2 * π - curentPointAngle;

003


另外一個(gè)坑?。?!

不要嘗試用tan函數(shù)計(jì)算點(diǎn)相對(duì)于原點(diǎn)及x軸的夾角!

不要嘗試用tan函數(shù)計(jì)算點(diǎn)相對(duì)于原點(diǎn)及x軸的夾角?。?/h3>

不要嘗試用tan函數(shù)計(jì)算點(diǎn)相對(duì)于原點(diǎn)及x軸的夾角?。?!



因?yàn)閠an函數(shù)在[0,2π]區(qū)間有2個(gè)周期,用的話(huà)會(huì)有出現(xiàn)各種bug!

2)計(jì)算變化的角度 Δ

以1)方法計(jì)算當(dāng)前點(diǎn)的角度curentPointAngle
上一個(gè)點(diǎn)的角度lastAngle
則變化角度CGFloat angle = lastAngle - curentPointAngle;

3)改變6個(gè)imgView中心點(diǎn)位置


    _lastImgViewAngle = fmod(_lastImgViewAngle + angle, 2 * M_PI);//對(duì)當(dāng)前角度取模
    CGFloat currentAngle = 0;
    CGFloat imgViewCenterX = 0;
    CGFloat imgViewCenterY = 0;
    for (int i = 0; i < 6; i++) {
        UIImageView *imgView = [self viewWithTag:kTag];
        currentAngle = _deltaAngle * i + _lastImgViewAngle;
        imgViewCenterX = _centerPoint.x + _radius * sin(currentAngle);
        imgViewCenterY = _centerPoint.x - _radius * cos(currentAngle);
        imgView = [self viewWithTag:kTag + i];
        imgView.center = CGPointMake(imgViewCenterX, imgViewCenterY);
    }


4)旋轉(zhuǎn)邏輯代碼


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    //計(jì)算上一個(gè)點(diǎn)相對(duì)于x軸的角度
    CGFloat lastPointRadius = sqrt(pow(point.y - _centerPoint.y, 2) + pow(point.x - _centerPoint.x, 2));
    if (lastPointRadius == 0) {
        return;
    }
    _lastPointAngle = acos((point.x - _centerPoint.x) / lastPointRadius);
    if (point.y > _centerPoint.y) {
        _lastPointAngle = 2 * M_PI - _lastPointAngle;
    }
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    UITouch *touch = [touches anyObject];
    CGPoint currentPoint = [touch locationInView:self];
    
    //1.計(jì)算當(dāng)前點(diǎn)相對(duì)于x軸的角度
    CGFloat currentPointRadius = sqrt(pow(currentPoint.y - _centerPoint.y, 2) + pow(currentPoint.x - _centerPoint.x, 2));
    if (currentPointRadius == 0) {//當(dāng)點(diǎn)在中心點(diǎn)時(shí),被除數(shù)不能為0
        return;
    }
    CGFloat curentPointAngle = acos((currentPoint.x - _centerPoint.x) / currentPointRadius);
    if (currentPoint.y > _centerPoint.y) {
        curentPointAngle = 2 * M_PI - curentPointAngle;
    }
    //2.變化的角度
    CGFloat angle = _lastPointAngle - curentPointAngle;
    
    _blueView.transform = CGAffineTransformRotate(_blueView.transform, angle);
    
    _lastImgViewAngle = fmod(_lastImgViewAngle + angle, 2 * M_PI);//對(duì)當(dāng)前角度取模
    CGFloat currentAngle = 0;
    CGFloat imgViewCenterX = 0;
    CGFloat imgViewCenterY = 0;
    for (int i = 0; i < 6; i++) {
        UIImageView *imgView = [self viewWithTag:kTag];
        currentAngle = _deltaAngle * i + _lastImgViewAngle;
        imgViewCenterX = _centerPoint.x + _radius * sin(currentAngle);
        imgViewCenterY = _centerPoint.x - _radius * cos(currentAngle);
        imgView = [self viewWithTag:kTag + i];
        imgView.center = CGPointMake(imgViewCenterX, imgViewCenterY);
    }
    
    _lastPointAngle = curentPointAngle;
}



最后,推薦一個(gè)同事@shelin分享的實(shí)用小工具,iOS-Images-Extractor,能快速獲得iOS *.ipa包中的圖片(包括Assets.car)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容