• iOS旋钮动画-CircleKnob


    • 欢迎相同喜欢动效的project师/UI设计师/产品添加我们
    • iOS动效特攻队–>QQ群:547897182
    • iOS动效特攻队–>熊熊:648070256

    前段时间和群里的一个设计师配合。依据网上的一个旋钮gif为原型,用代码做出来了。先来看下原型的效果
    原型效果

    在看下以下是我做完后成品的效果,加了一个通过移动光源来对阴影控制的效果。这样看起来更立体点。

    (那个太阳有点不协调。

    。)
    同一时候附上代码链接https://git.oschina.net/BearXR/CircleKnob
    还有单独的下载包http://download.csdn.net/detail/xiongbaoxr/9419655
    成品效果

    以下開始简单的解说下构思步骤和部分代码片段
    1,构建底盘
    首先我们要构造两个圆盘和旋钮上的红点
    这里写图片描写叙述
    用view画两个圆盘,和一个小的红色控制点。
    注意:红色的控制点我又单独新建了一个view,这样方便待会做手势控制。从而不会影响到后面的view。而且全部view的圆心都要设置好。后面和角度相关的处理会非常多的。最好能准确和self.view或者window能有直接的关联。

    2。底盘添加点击和拖动的手势
    在这里我们要添加手势,还有起始点,终点的角度。

    我画了辅助线,便于调试。

    手势和过渡效果都写好了。所以效果我就直接放上来了。
    旋钮动画

    3,添加外围的扇环
    因为后面要做阴影,所以除了扇环的底色和效果是用了一个view,其余的每一个小格子我都是新开了一个view,而且将旋钮的圆心位置设置为锚点进行旋转。
    带有扇环

    #pragma mark - 设置外围的扇环形
    - (void)initSetFanView
    {
        CGFloat delta_distance = 26;
    
        fanView = [[FanView alloc] initWithFrame:CGRectMake(0, 0, knob_width + delta_distance * 2, knob_width + delta_distance * 2)];
        fanView.center = knob.center;
        fanView.backgroundColor = [UIColor clearColor];
        fanView.userInteractionEnabled = NO;
        [self.view addSubview:fanView];
    
        fanView.knobValue = -startAngleValue;//设置起始点
        fanView.lightSource_InWindow = lightSource;
    }
    
    - (void)drawRect:(CGRect)rect
    {
        contextBack = UIGraphicsGetCurrentContext();
        contextFore = UIGraphicsGetCurrentContext();
    
        [self drawFan:contextBack
           bezierPath:bezierPathBack
            knobAngle:180 + endAngleValue
          strokeColor:[UIColor colorWithRed:202/255.0 green:207/255.0 blue:202/255.0 alpha:1.0f]];
    
        [self drawFan:contextFore
           bezierPath:bezierPathFore
            knobAngle:_knobValue
          strokeColor:[UIColor colorWithRed:174/255.0 green:0/255.0 blue:0/255.0 alpha:1.0f]];
    }
    
    //  绘制扇形
    - (void)drawFan:(CGContextRef)context bezierPath:(UIBezierPath *)bezierPath knobAngle:(CGFloat)knobAngle strokeColor:(UIColor *)strokeColor
    {
        CGRect          frame           = self.frame;
        CGFloat         radius          = (CGRectGetWidth(frame) - lineWidth) / 2;
        CGFloat         angleForOne     = M_PI / 180.0f;
        CGFloat         circleLength    = radius * 2 * M_PI;
        int             gapCount        = fanShowCount - 1;                         //间隙个数
        CGFloat         gapWidth        = 5;                                        //间隙距离
    
        //  计算须要绘制的角度(角度制)
        knobAngle = knobAngle < -startAngleValue ? -startAngleValue : knobAngle;
        knobAngle = knobAngle > 180 + endAngleValue ? 180 + endAngleValue : knobAngle;
    
        //  设置弧线路径
        bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetWidth(frame)/2, CGRectGetHeight(frame)/2) radius:(CGRectGetWidth(frame) - lineWidth)/2.0 startAngle:angleForOne * (180 - startAngleValue) endAngle:angleForOne * (180 + knobAngle) clockwise:YES];
        CGContextAddPath(context, bezierPath.CGPath);
    
        //  设置线的颜色。线宽,接头样式
        CGContextSetStrokeColorWithColor(context, strokeColor.CGColor);
        CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
        CGContextSetLineWidth(context, lineWidth);
        CGContextSetLineCap(context, kCGLineCapButt);
    
        //  绘制虚线
        CGFloat drawLineLength = circleLength * (1- (startAngleValue + endAngleValue)/fullAngleValue);
        CGFloat showLineLengthPer = (drawLineLength - gapWidth * gapCount)/(fanShowCount - 1);
        CGFloat lengths[2] = {showLineLengthPer,gapWidth};
        CGContextSetLineDash(context, 0, lengths, 2);
        CGContextDrawPath(context, kCGPathFillStroke);//最后一个參数是填充类型
    
    
        if (!self.blockViewArray) {
            self.blockViewArray = [[NSMutableArray alloc] init];
        }
    
        //  绘制小方格view(而且仅仅绘制一次)
        static BOOL drawBlock = NO;
        if (!drawBlock) {
            drawBlock = YES;
    
            for (int i = 1; i < fanShowCount; i++) {
                CGFloat blockWidth  = lineWidth + 8;
                CGFloat blockHeight = 5;
                CGFloat block_x     = CGRectGetWidth(frame) / 2 - radius - blockWidth/2;
                CGFloat block_y     = CGRectGetHeight(frame) / 2;
    
                //  角度修正
                if (blockHeight > gapWidth) {
                    block_y = block_y - blockHeight/2;
                }else{
                    block_y = block_y + (gapWidth - blockHeight)/2;
                }
    
                //  方格view 可辅助绘制垂直平分线
                ViewWithAutoShadow *viewBlock = [[ViewWithAutoShadow alloc] initWithFrame:CGRectMake(block_x, block_y, blockWidth, blockHeight)];
                viewBlock.showAssistPoint = NO;
                viewBlock.backgroundColor = [UIColor colorWithRed:248/255.0 green:238/255.0 blue:237/255.0 alpha:1.0f];
                [self addSubview:viewBlock];
    
                //  依据锚点旋转
                CGFloat blockAngle = (180 + startAngleValue + endAngleValue)/fanShowCount*i - startAngleValue;
                CGAffineTransform rotate = GetCGAffineTransformRotateAroundPoint1(viewBlock.center.x, viewBlock.center.y, CGRectGetWidth(frame)/2, CGRectGetHeight(frame)/2, blockAngle/180.0 * M_PI);
                [viewBlock setTransform:rotate];
    
                AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];
                [viewBlock drawShadowEffectWithSourcePoint:_lightSource_InWindow assistInView:myDelegate.window];
    
                [self.blockViewArray addObject:viewBlock];
            }
        }
    }

    4,添加动画效果
    给底盘和扇环都添加动效。里面用了定时器。关于定时器能够看參考这篇博客http://blog.csdn.net/xiongbaoxr/article/details/50580701

    旋钮动效
    动效的代码片段

    //  运行动画
    - (void)changeRadiusWithAnimation:(CGFloat)radius lastRadius:(CGFloat)lastRadius duration:(CGFloat)duration
    {
        CGFloat countAll = ABS(radius - lastRadius);
        double delaySeconds = 0.001f;
        CGFloat animateCount = duration/delaySeconds;
        __block int i = 0;
    
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, delaySeconds * NSEC_PER_SEC, 0);
        dispatch_source_set_event_handler(timer, ^{
    
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
                if (i >= animateCount) {
    
                    dispatch_source_cancel(timer);
                }else{
    
                    i ++;
                    dispatch_sync(dispatch_get_main_queue(), ^{
    
                        //  扇环进度条动画
                        CGFloat anglePer = countAll/animateCount * i;
                        int k = radius > lastRadius ?

    anglePer : -anglePer; CGFloat needValue = lastRadius + k; fanView.knobValue = needValue; // 该方法会又一次调用drawRect方法 [fanView setNeedsDisplay]; // 旋钮转动 knob.transform = CGAffineTransformMakeRotation((needValue/180.0 - 0) * M_PI); }); } }); }); dispatch_resume(timer); }

    5。最后一步是处理阴影效果,里面涉及的东西比較多。

    不详解代码,说一下思路。关于阴影处理的主要是这个类 ViewWithAutoShadow.h

    5.1,首先我们设置一个光源点,这个小太阳就是我们的光源所在的位置
    设置光源

    5.2,为了便于调试和理解,我对全部须要做投影效果的view画出了其与光源的连线,view的垂直平分线。而且进行了对应的延长。
    同一时候也把view的四个顶点和中心点也描出来了
    和光源连接的辅助线

    5.3。注意看,每一个夹脚处都有一个小的出头部分。这就是阴影的偏移量。

    依照这个偏移量来设置阴影就会有一种仿真的效果。
    连线细节

    5.4,我们把阴影效果设置上去看下终于的效果,是不是開始有点感觉了?
    阴影调试效果

    5.5最后把全部的辅助线都关掉。再看看效果
    终于效果

    6,先就这么多。花了好几个晚上做出来的。想弄清原理的话主要还是在代码里面。代码凝视写的也比較多。

    欢迎有兴趣做动效的私聊我啊。纯粹是兴趣,不为不论什么利益。

  • 相关阅读:
    类与继承
    闭包、原型链和继承
    ajax(下)和“承诺”
    ajax(上)
    Ubuntu电源键软关机设置
    金老师语录摘要(七)
    金老师语录摘要(六)
    金老师语录摘要(四)
    金老师语录摘要(三)
    金老师语录摘要(二)
  • 原文地址:https://www.cnblogs.com/zsychanpin/p/7227845.html
Copyright © 2020-2023  润新知