Scale items with iCarousel

后端 未结 3 1502
我在风中等你
我在风中等你 2020-12-13 11:17

I was trying to use iCarousel for one my solutions, I need to achieve something like the image below \"enter

相关标签:
3条回答
  • 2020-12-13 11:41

    You can do this via the iCarousel's iCarouselTypeCustom type in the delegate method

    - (CATransform3D)carousel:(iCarousel *)carousel itemTransformForOffset:(CGFloat)offset baseTransform:(CATransform3D)transform
    

    Just set the type of the carousel (e.g. in viewDidLoad of the carousel's view controller):

    self.carousel.type = iCarouselTypeCustom;
    

    And calculate the transform as you like. I've laid the objects on a hyperbola, and shrink them in addition a bit as they move away from the center. That quite resembles your image, I think:

    - (CATransform3D)carousel:(iCarousel *)carousel itemTransformForOffset:(CGFloat)offset baseTransform:(CATransform3D)transform
    {
        const CGFloat offsetFactor = [self carousel:carousel valueForOption:iCarouselOptionSpacing withDefault:1.0f]*carousel.itemWidth;
    
        //The larger these values, as the items move away from the center ...
    
        //... the faster they move to the back
        const CGFloat zFactor = 150.0f;
    
        //... the faster they move to the bottom of the screen
        const CGFloat normalFactor = 50.0f;
    
        //... the faster they shrink
        const CGFloat shrinkFactor = 3.0f;
    
        //hyperbola
        CGFloat f = sqrtf(offset*offset+1)-1;
    
        transform = CATransform3DTranslate(transform, offset*offsetFactor, f*normalFactor, f*(-zFactor));
        transform = CATransform3DScale(transform, 1/(f/shrinkFactor+1.0f), 1/(f/shrinkFactor+1.0f), 1.0);
        return transform;
    }
    

    and the result: result

    you can adjust the float constants to your liking.

    For moving items around a circle while scaling them just use goniometric functions for translation, then rotate and scale:

    - (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value
    {
        if (option == iCarouselOptionSpacing)
        {
            return value * 2.0f;
        }
        if(option == iCarouselOptionVisibleItems)
        {
            return 11;
        }
        if(option == iCarouselOptionWrap) return YES;
        return value;
    }
    
    - (CATransform3D)carousel:(iCarousel *)carousel itemTransformForOffset:(CGFloat)offset baseTransform:(CATransform3D)transform
    {
        const CGFloat radius = [self carousel:carousel valueForOption:iCarouselOptionRadius withDefault:200.0];
        const CGFloat offsetFactor = [self carousel:carousel valueForOption:iCarouselOptionSpacing withDefault:1.0f]*carousel.itemWidth;
        const CGFloat angle = offset*offsetFactor/radius;
    
        //... the faster they shrink
        const CGFloat shrinkFactor = 2.0f;
        //hyperbola (now only for shrinking purposes)
        CGFloat f = sqrtf(offset*offset+1)-1;
    
    
        transform = CATransform3DTranslate(transform, radius*sinf(angle), radius*(1-cosf(angle)), 0.0);
        transform = CATransform3DRotate(transform, angle, 0, 0, 1);
        transform = CATransform3DScale(transform, 1/(f*shrinkFactor+1.0f), 1/(f*shrinkFactor+1.0f), 1.0);
        return transform;
    } 
    

    and again, the result: result2

    you can adjust the spacing and the radius in the carousel:valueForOption:withDefault: method.

    Enjoy! :)

    0 讨论(0)
  • 2020-12-13 11:46

    I do not have enough reputation to comment so i have to ask a further question as a reply :(

    @burax is it possible to layout items on linear line instead of a hyperbola but keep the resizing?

    Regards, and sorry for asking like this

    Edit : with random tries i achieved with this :

    - (CATransform3D)carousel:(iCarousel *)carousel itemTransformForOffset:(CGFloat)offset baseTransform:(CATransform3D)transform
    {
    const CGFloat radius = [self carousel:carousel valueForOption:iCarouselOptionRadius withDefault:5050.0];
    const CGFloat offsetFactor = [self carousel:carousel valueForOption:iCarouselOptionSpacing withDefault:0.8f]*carousel.itemWidth;
    const CGFloat angle = offset*offsetFactor/radius;
    
    //... the faster they shrink
    const CGFloat shrinkFactor = 2.0f;
    //hyperbola (now only for shrinking purposes)
    CGFloat f = sqrtf(offset*offset+1)-1;
    
    
    transform = CATransform3DTranslate(transform, radius*sinf(angle), radius*(1-cosf(angle)), 0.0);
    transform = CATransform3DRotate(transform, angle, 0, 0, 1);
    transform = CATransform3DScale(transform, 1/(f*shrinkFactor+1.0f), 1/(f*shrinkFactor+1.0f), 1.0);
    return transform;
    }
    

    there is probably a better way but i am new to transformations :)

    0 讨论(0)
  • 2020-12-13 11:54

    A little modified and in SWIFT to copy paste ;) - works perfekt for me

    func carousel(carousel: iCarousel, valueForOption option: iCarouselOption, withDefault value: CGFloat) -> CGFloat {
        if option == iCarouselOption.Spacing {
            return value * 1.8
        }
        return value
    }
    
    func carousel(carousel: iCarousel, itemTransformForOffset offset: CGFloat, baseTransform transform: CATransform3D) -> CATransform3D {
        let offsetFactor = self.carousel(carousel, valueForOption: iCarouselOption.Spacing, withDefault: 1) * carousel.itemWidth
    
        let zFactor: CGFloat = 150
        let normalFactor: CGFloat = 0
        let shrinkFactor: CGFloat = 1
        let f = sqrt(offset*offset+1)-1
    
        var transform = CATransform3DTranslate(transform, offset*offsetFactor, f*normalFactor, f*(-zFactor));
        transform = CATransform3DScale(transform, 1/(f/shrinkFactor+1), 1/(f/shrinkFactor+1), 1);
        return transform;
    }
    
    0 讨论(0)
提交回复
热议问题