動(dòng)感小球(iOS)

偶然間發(fā)現(xiàn)QQ的消息挺好玩的,應(yīng)用內(nèi)收到新消息,紅色的提醒圓圈可以拉伸并拖動(dòng)。很有意思,決定自己試一下。

先上效果圖:


效果.gif

接下來(lái),我們一步一步來(lái)實(shí)現(xiàn)之。

我們?cè)谕蟿?dòng)過(guò)程中,已知哪些信息:
初始的半圓心位置A,半徑R。

接下來(lái)我們可以通過(guò)用戶觸摸的位置獲取以下信息:
拖動(dòng)的半圓形位置B。
(注:并不是觸摸的點(diǎn)就是新的圓點(diǎn),需要進(jìn)行加工。開始觸摸的點(diǎn)為Touch,觸摸變化的點(diǎn)為Touch',新的圓點(diǎn)為A+(Touch'-Touch))。

我們根據(jù)當(dāng)前拖動(dòng)的距離與最大拖動(dòng)距離進(jìn)行求比率,再乘以初始半徑R得到拖動(dòng)半徑R'。

我們先取一個(gè)中間狀態(tài)來(lái)分析一下:

分析圖

我們?cè)谕蟿?dòng)時(shí),小球會(huì)被分為兩個(gè)部分,兩個(gè)半圓圓心的連線與水平線的夾角為α。

最關(guān)鍵的一步是:計(jì)算出α的值。

根據(jù)同角的余角相等,以及三角函數(shù)能的到:

sinα = (B.x-A.x) / AB的長(zhǎng)度

然后通過(guò)反正弦函數(shù),可以得到角α的值。

(注:雖然使用正切函數(shù)在數(shù)值計(jì)算時(shí)很方便,但是反正切函數(shù)在0位置會(huì)發(fā)生突變,無(wú)法滿足我們拖動(dòng)時(shí)漸變的效果,故舍棄。數(shù)學(xué)不行,在這個(gè)坑里困了好久。。)

計(jì)算出角α就能開始繪圖了。
我們一一計(jì)算出圖中M、N、P、Q四個(gè)點(diǎn)的坐標(biāo),然后開始操作:

1 從M點(diǎn)開始,A為圓心,N為終點(diǎn),繪制半圓。(使用α角)
2 從N點(diǎn)開始,P點(diǎn)為終點(diǎn),繪制貝塞爾曲線。
3 從P點(diǎn)開始,B為圓心,Q為終點(diǎn),繪制半圓。(使用α角)
4 從Q點(diǎn)開始,M點(diǎn)為終點(diǎn),繪制貝塞爾曲線。

繪制時(shí),會(huì)發(fā)現(xiàn)步驟2和4的控制點(diǎn)不好確定,但是如果不確定的話,沒(méi)法繪制。如果使用兩個(gè)圓心的中點(diǎn)為控制點(diǎn),會(huì)發(fā)現(xiàn)初始拖動(dòng)時(shí)兩個(gè)半圓中間縫隙很大,不夠平滑。

此時(shí)我們精簡(jiǎn)一下模型,看下部的圖:
C是AB中點(diǎn),過(guò)C點(diǎn)作MN的平行線。

NP的控制點(diǎn)位于C點(diǎn)的右上側(cè)。
MQ的控制點(diǎn)位于C點(diǎn)點(diǎn)左下側(cè)。

兩個(gè)控制點(diǎn)是根據(jù)拖動(dòng)的距離變化的。取極限即可得到兩個(gè)點(diǎn)對(duì)應(yīng)的一次比率關(guān)系。

這樣就能夠完成步驟2和4了。

此時(shí)我們就完成了拖動(dòng)時(shí)的效果了。

- (void)updateCircleWithOriginCenter:(CGPoint)originCenter withNewCenter:(CGPoint)newCenter
{
   
     //拖拽的距離,等于兩個(gè)圓心的距離
   double moveDistance = (double)sqrt((newCenter.y-originCenter.y)*(newCenter.y-originCenter.y) + (newCenter.x-originCenter.x)*(newCenter.x-originCenter.x));
   if (moveDistance == 0) {
       return;
   }
   //移動(dòng)的水平角度(兩圓心連線與水平面夾角)
   //反正切函數(shù)很方便,但因?yàn)榉凑泻瘮?shù)在0的位置突變,從-M_PI/2變?yōu)?M_PI/2,無(wú)法滿足我們拖動(dòng)時(shí)的漸變需求,故舍棄。我們使用反正弦函數(shù)。
   //正弦函數(shù)=對(duì)邊/斜邊  兩個(gè)圓心之間的連線為斜邊,對(duì)邊是Y軸的垂直距離
   double sinValue = (double)(newCenter.y-originCenter.y)/(double)sqrt((newCenter.y-originCenter.y)*(newCenter.y-originCenter.y) + (newCenter.x-originCenter.x)*(newCenter.x-originCenter.x));
   //獲取弧度
   double angle = asin(sinValue);
   
   double rate = moveDistance/self.pullDis;
   
   if (rate >= 1) {
       rate = 1;
       
       //如果拖拽結(jié)束了,那就不做操作,否則會(huì)死循環(huán)。因?yàn)橥献ЫY(jié)束,會(huì)進(jìn)行恢復(fù)繪制,恢復(fù)繪制方法中會(huì)調(diào)用本方法,然后本方法再調(diào)用恢復(fù)繪制方法。。。
       if (self.isEnding) {
           return;
       }
       
       //達(dá)到最大值,斷開
       self.isEnding = YES;
       
       //如果有回調(diào),則觸發(fā)
       if (self.pullBlock) {
           self.pullBlock();
       }        
       
       if (self.needAnimation) {
           
           //將原始位置的圓進(jìn)行恢復(fù)繪制,就是把原始位置的小圓拉過(guò)來(lái)。在手指觸摸的位置合二為一
           [self backCircleWithTotal:100 withCurrent:0 withTotalTime:0.05 withOriginCenter:newCenter withEndCenter:originCenter];
       }else{
           
           //拉伸到最大值時(shí),如果不需要?jiǎng)赢嫞沂怯脩粼O(shè)置的類型,那么隱藏layer
           if (self.circleType == CircleTypeSet) {
               self.isSetMaxValue = YES;
               [self resetOriginCircle];
               self.pointLayer.hidden = YES;
           }

       }
       
       //如果有文本,隱藏文本
       if (self.contentString) {
           self.contentTextLayer.hidden = YES;
       }
       return;
   }
   
   CGFloat bigCircleRate = self.bigChangeRate > 0 ? self.bigChangeRate:1/3;
   CGFloat smallCircleRate = self.smallChangeRate > 0 ? self.smallChangeRate:1;
   
   //新的半徑
   CGFloat newRadius = 0;
   switch (self.pullType) {
       case PullTypeLeaveBig:
       {
           //如果是拉到最大值,結(jié)束繪制,合二為一的過(guò)程,那么起始圓和目標(biāo)圓半徑互換
           //比如說(shuō):大半徑留在原地,小半徑被拖拽走的這種情況
           //當(dāng)我向外拉的時(shí)候:拖動(dòng)的圓半徑較小,小圓移動(dòng)
           //當(dāng)我未拉到最大值,小圓回去,小圓移動(dòng)
           //當(dāng)我拉到最大值,大圓向小圓合并。大圓移動(dòng)
           if (self.isEnding) {
               self.circleR = self.originR*((1-rate*smallCircleRate)>self.minRate?(1-rate*smallCircleRate):self.minRate);
               newRadius = self.originR*((1-rate*bigCircleRate)>self.minRate?(1-rate*bigCircleRate):self.minRate);

           }else{
               newRadius = self.originR*((1-rate*smallCircleRate)>self.minRate?(1-rate*smallCircleRate):self.minRate);
               self.circleR = self.originR*((1-rate*bigCircleRate)>self.minRate?(1-rate*bigCircleRate):self.minRate);
           }
       }
           break;
           
       case PullTypeMoveBig:
       {
           if (self.isEnding) {
               newRadius = self.originR*((1-rate*smallCircleRate)>self.minRate?(1-rate*smallCircleRate):self.minRate);
               self.circleR = self.originR*((1-rate*bigCircleRate)>self.minRate?(1-rate*bigCircleRate):self.minRate);

           }else{
               self.circleR = self.originR*((1-rate*smallCircleRate)>self.minRate?(1-rate*smallCircleRate):self.minRate);
               newRadius = self.originR*((1-rate*bigCircleRate)>self.minRate?(1-rate*bigCircleRate):self.minRate);
           }
           
       }
           break;
       default:
           break;
   }
       
   //創(chuàng)建新的BezierPath
   UIBezierPath *path = [UIBezierPath bezierPath];
   if (newCenter.x-originCenter.x < 0) {
       
       //圓的中心對(duì)稱軸,左側(cè)與右側(cè)的計(jì)算不一樣
       
       //初始圓的底部的點(diǎn)
       CGPoint originBottomPoint = CGPointMake(originCenter.x+sin(angle)*self.circleR, originCenter.y+cos(angle)*self.circleR);
       //初始圓的頂部的點(diǎn)
       CGPoint originTopPoint = CGPointMake(originCenter.x-sin(angle)*self.circleR, originCenter.y-cos(angle)*self.circleR);
       
       //新圓心的左上角的點(diǎn)
       CGPoint newTopPoint = CGPointMake(newCenter.x-sin(angle)*newRadius, newCenter.y-cos(angle)*newRadius);
       //新圓心的下部的點(diǎn)
       CGPoint newBottomPoint = CGPointMake(newCenter.x+sin(angle)*newRadius, newCenter.y+cos(angle)*newRadius);
       
       //兩圓心連線中點(diǎn)
       CGPoint controlPoint = CGPointMake(originCenter.x+(newCenter.x-originCenter.x)/2, originCenter.y+(newCenter.y-originCenter.y)/2);
       
       //初始圓頂部點(diǎn)與新圓的頂部點(diǎn)連線的中點(diǎn)
       CGPoint topMiddlePoint = CGPointMake((newTopPoint.x+originTopPoint.x)/2, (newTopPoint.y+originTopPoint.y)/2);
       
       //上部控制點(diǎn)的X坐標(biāo),與拉伸比例有關(guān),未拉伸時(shí),取頂部點(diǎn)連線中點(diǎn),拉伸最大時(shí),取兩圓心連線中點(diǎn)
       CGFloat topX = topMiddlePoint.x + (controlPoint.x-topMiddlePoint.x)*rate;
       //上部控制點(diǎn)的Y坐標(biāo),與拉伸比例有關(guān)
       CGFloat topY = topMiddlePoint.y + (controlPoint.y-topMiddlePoint.y)*rate;
       
       //拉伸時(shí),上部的控制點(diǎn)。不斷變化的
       CGPoint topControlPoint = CGPointMake(topX, topY);
       
       //兩個(gè)圓下部點(diǎn)連線的中點(diǎn)
       CGPoint bottomMiddlePoint = CGPointMake((newBottomPoint.x+originBottomPoint.x)/2, (newBottomPoint.y+originBottomPoint.y)/2);
       //下部點(diǎn)的x,隨比例變化
       CGFloat bottomX = bottomMiddlePoint.x + (controlPoint.x-bottomMiddlePoint.x)*rate;
       //下部點(diǎn)的y,隨比例變化
       CGFloat bottomY = bottomMiddlePoint.y + (controlPoint.y-bottomMiddlePoint.y)*rate;
       //拉伸時(shí),下部控制點(diǎn)
       CGPoint bottomControlPoint = CGPointMake(bottomX, bottomY);
       
        //移動(dòng)到初始圓的下部點(diǎn)
       [path moveToPoint:originBottomPoint];
       
       //原始的圓,右半側(cè),逆時(shí)針畫圓
       [path addArcWithCenter:originCenter radius:self.circleR startAngle:M_PI/2-angle endAngle:M_PI*3/2-angle clockwise:NO];
       
       //從原始圓的頂部,連線到新圓的頂部,上部點(diǎn)為控制點(diǎn)
       [path addQuadCurveToPoint:newTopPoint controlPoint:topControlPoint];
       
       //新圓的左側(cè),逆時(shí)針畫圓
       [path addArcWithCenter:newCenter radius:newRadius startAngle:M_PI*3/2-angle endAngle:M_PI*5/2-angle clockwise:NO];
       
        //從新圓的底部,連接到初始圓的底部點(diǎn),下部點(diǎn)為控制點(diǎn)
       [path addQuadCurveToPoint:originBottomPoint controlPoint:bottomControlPoint];
       
        }else{
       
       //初始圓的下部點(diǎn)
       CGPoint originBottomPoint = CGPointMake(originCenter.x-sin(angle)*self.circleR, originCenter.y+cos(angle)*self.circleR);
       
       CGPoint originTopPoint = CGPointMake(originCenter.x+sin(angle)*self.circleR, originCenter.y-cos(angle)*self.circleR);
       
       //新圓心的左上角的點(diǎn)
       CGPoint newTopPoint = CGPointMake(newCenter.x+sin(angle)*newRadius, newCenter.y-cos(angle)*newRadius);
       CGPoint newBottomPoint = CGPointMake(newCenter.x-sin(angle)*newRadius, newCenter.y+cos(angle)*newRadius);
       
       //兩圓心連線中點(diǎn)
       CGPoint controlPoint = CGPointMake(originCenter.x+(newCenter.x-originCenter.x)/2, originCenter.y+(newCenter.y-originCenter.y)/2);
       
       
       //初始圓頂部點(diǎn)與新圓的頂部點(diǎn)連線的中點(diǎn)
       CGPoint topMiddlePoint = CGPointMake((newTopPoint.x+originTopPoint.x)/2, (newTopPoint.y+originTopPoint.y)/2);
       
       //上部控制點(diǎn)的X坐標(biāo),與拉伸比例有關(guān),未拉伸時(shí),取頂部點(diǎn)連線中點(diǎn),拉伸最大時(shí),取兩圓心連線中點(diǎn)
       CGFloat topX = topMiddlePoint.x + (controlPoint.x-topMiddlePoint.x)*rate;
       //上部控制點(diǎn)的Y坐標(biāo),與拉伸比例有關(guān)
       CGFloat topY = topMiddlePoint.y + (controlPoint.y-topMiddlePoint.y)*rate;
       
       //拉伸時(shí),上部的控制點(diǎn)。不斷變化的
       CGPoint topControlPoint = CGPointMake(topX, topY);
       
       //兩個(gè)圓下部點(diǎn)連線的中點(diǎn)
       CGPoint bottomMiddlePoint = CGPointMake((newBottomPoint.x+originBottomPoint.x)/2, (newBottomPoint.y+originBottomPoint.y)/2);
       //下部點(diǎn)的x,隨比例變化
       CGFloat bottomX = bottomMiddlePoint.x + (controlPoint.x-bottomMiddlePoint.x)*rate;
       //下部點(diǎn)的y,隨比例變化
       CGFloat bottomY = bottomMiddlePoint.y + (controlPoint.y-bottomMiddlePoint.y)*rate;
       //拉伸時(shí),下部控制點(diǎn)
       CGPoint bottomControlPoint = CGPointMake(bottomX, bottomY);
       
       [path moveToPoint:originBottomPoint];
       
       //原始的圓,左半側(cè),順時(shí)針畫圓
       [path addArcWithCenter:originCenter radius:self.circleR startAngle:M_PI/2+angle endAngle:M_PI*3/2+angle clockwise:YES];
       
       //添加曲線到新圓的頂部
       [path addQuadCurveToPoint:newTopPoint controlPoint:topControlPoint];
       
       //新圓的右側(cè),順時(shí)針畫圓
       [path addArcWithCenter:newCenter radius:newRadius startAngle:M_PI*3/2+angle endAngle:M_PI*5/2+angle clockwise:YES];
       
       //添加曲線到新圓的底部點(diǎn)
       [path addQuadCurveToPoint:originBottomPoint controlPoint:bottomControlPoint];
       
       }
   
   
   //更新layer
   self.pointLayer.path = path.CGPath;

}

接下來(lái)就是要處理拖動(dòng)到一半,松手的處理了。
我們需要拉動(dòng)的那一部分按照拖動(dòng)出去的效果,反過(guò)來(lái)合并到初始圓中。

這里,我們已知這些信息:
初始圓心A,半徑R。
松手時(shí)圓心B,半徑R'。

接下來(lái)我們需要做這些操作:
1 計(jì)算出直線AB的關(guān)系式,然后按比率進(jìn)行縮小。
2 更新當(dāng)前的圖形。可使用拖動(dòng)時(shí)的函數(shù),只需要
把起始點(diǎn)和結(jié)束點(diǎn)調(diào)整一下即可。
3 會(huì)到初始位置后,反彈動(dòng)畫。

- (void)backCircleWithTotal:(NSInteger)total withCurrent:(NSInteger)current withTotalTime:(CGFloat)totalTime withOriginCenter:(CGPoint)originCenter withEndCenter:(CGPoint)endCenter
{
    NSTimeInterval duration = totalTime/total;
    
    __block NSInteger value = current;
    
    //采用遞歸處理幀
    if (current <= total) {
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            
            //比率
            CGFloat rate = value*1.f/total*1.f;
            
            //X,Y值的變化率
            CGFloat xChangeValue = endCenter.x-originCenter.x;
            CGFloat yChangeValue = endCenter.y-originCenter.y;
            
            //回彈時(shí),不同時(shí)刻的圓心位置
            CGPoint backCenter = CGPointMake(endCenter.x-xChangeValue*rate, endCenter.y-yChangeValue*rate);
            //繪制曲線
            [self updateCircleWithOriginCenter:originCenter withNewCenter:backCenter];
            
            //遞歸調(diào)用,繼續(xù)更新
            [self backCircleWithTotal:total withCurrent:value+1 withTotalTime:totalTime withOriginCenter:originCenter withEndCenter:endCenter];
            
            if (value == total) {
                //當(dāng)完成整個(gè)回彈時(shí),設(shè)置屬性,重置圓的位置
                self.isEndAnimation = YES;
            }
        });
    
    }else{
        //如果本次拖拽到最大值了,不需要回彈動(dòng)畫了
        if (self.isEnding) {
           
            return;
        }
        
        //未拉到最大值,放手的動(dòng)畫處理
        CGFloat rate = 0.1;
        
        //X,Y的變化值
        CGFloat xChangeValue = endCenter.x-originCenter.x;
        CGFloat yChangeValue = endCenter.y-originCenter.y;
        
        //初始位置的左上側(cè)
        CGPoint backCenter = CGPointMake(originCenter.x-xChangeValue*rate-self.originR, originCenter.y-yChangeValue*rate-self.originR);
        //初始位置的右下側(cè)
        CGPoint foreCenter = CGPointMake(originCenter.x+xChangeValue*rate/2-self.originR, originCenter.y+yChangeValue*rate/2-self.originR);
        //初始位置的左上側(cè),較靠近圓心
        CGPoint backTwoCenter = CGPointMake(originCenter.x-xChangeValue*rate/3-self.originR, originCenter.y-yChangeValue*rate/3-self.originR);

        //動(dòng)畫
        CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        animation.calculationMode = kCAAnimationLinear;
        
        CGMutablePathRef path = CGPathCreateMutable();
        //移動(dòng)到圓心
        CGPathMoveToPoint(path, NULL,originCenter.x-self.originR, originCenter.y-self.originR);
        //圓心左上側(cè)
        CGPathAddLineToPoint(path, NULL, backCenter.x, backCenter.y);
        //圓心的右下側(cè)
        CGPathAddLineToPoint(path, NULL, foreCenter.x, foreCenter.y);
        //圓心的左上側(cè),近圓心
        CGPathAddLineToPoint(path, NULL, backTwoCenter.x, backTwoCenter.y);
        //圓心的右下側(cè),近圓心
        CGPathAddLineToPoint(path, NULL, originCenter.x-self.originR, originCenter.y-self.originR);
        animation.path = path;
        animation.duration = 0.35;
        [self.pointLayer addAnimation:animation forKey:@"pointBackAnimation"];
        
        
        
    }
}

至此,我們就能實(shí)現(xiàn)動(dòng)感的小球了。

有什么意見(jiàn)或者建議請(qǐng)留言哈,共同進(jìn)步~

代碼在這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評(píng)論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,441評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,475評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,834評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,009評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,559評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,516評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,728評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,249評(píng)論 3 399
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,484評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容

  • --繪圖與濾鏡全面解析 概述 在iOS中可以很容易的開發(fā)出絢麗的界面效果,一方面得益于成功系統(tǒng)的設(shè)計(jì),另一方面得益...
    韓七夏閱讀 2,764評(píng)論 2 10
  • Core Animation Core Animation,中文翻譯為核心動(dòng)畫,它是一組非常強(qiáng)大的動(dòng)畫處理API,...
    45b645c5912e閱讀 3,047評(píng)論 0 21
  • Core Animation其實(shí)是一個(gè)令人誤解的命名。你可能認(rèn)為它只是用來(lái)做動(dòng)畫的,但實(shí)際上它是從一個(gè)叫做Laye...
    小貓仔閱讀 3,762評(píng)論 1 4
  • 整體思路: 手指移動(dòng),按鈕跟著移動(dòng).按鈕跟著手指移動(dòng).移動(dòng)時(shí)底部有一個(gè)圓, 根據(jù)上面的大圓按鈕拖動(dòng)的距離,小圓的半...
    翻滾的企鵝閱讀 704評(píng)論 0 0
  • 「PPT怎樣從圖片中取色,有什么技巧嗎?」 —— 關(guān)于圖片取色有幾個(gè)網(wǎng)站,在此我采用其中一個(gè)比較好用的網(wǎng)站進(jìn)行講解...
    湯帥同學(xué)閱讀 908評(píng)論 0 3