一直想寫一個帶倒影效果的3D輪播圖锭部,最近有時間暂论,就研究了一下,下面是實現(xiàn)后的效果拌禾,可無限循環(huán)輪播:
倒影
首先是倒影效果取胎,要想實現(xiàn)倒影,需要了解CALayer復(fù)制層湃窍。這里要注意的是闻蛀,復(fù)制層CAReplicatorLayer
是針對父視圖來說的,比如要給一個UIImageView
添加倒影效果您市,則要針對UIImageView
的父視圖的根層進行設(shè)置復(fù)制層觉痛。
所以這里寫了一個繼承自UIView
類的HLImageView
,在其上面添加一個顯示圖片的UIImageView
,并在HLImageView
初始化方法中設(shè)置其復(fù)制層茵休,效果如下:
具體的代碼如下:
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
CGFloat w = frame.size.width;
CGFloat h = frame.size.height;
_posiP = self.layer.position;
_imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, w, h / 2.0)];
[self addSubview:_imgView];
CAReplicatorLayer *repL = (CAReplicatorLayer *)self.layer;
repL.instanceCount = 2;
//復(fù)制出來的子層,它都是繞著復(fù)制層錨點進行旋轉(zhuǎn).
repL.instanceTransform = CATransform3DMakeRotation(M_PI, 1, 0, 0);
repL.instanceRedOffset -= 0.5;
repL.instanceGreenOffset -= 0.5;
repL.instanceBlueOffset -= 0.5;
repL.instanceAlphaOffset -= 0.5;
}
return self;
}
這里注意的是復(fù)制出來的子層都是繞復(fù)制層的錨點進行旋轉(zhuǎn)的薪棒,CALayer默認的錨點是(0.5,0.5)榕莺,所以這里默認繞中心點延伸的軸旋轉(zhuǎn)俐芯,上面代碼是繞x軸旋轉(zhuǎn),即會產(chǎn)生倒影效果钉鸯。
3D效果旋轉(zhuǎn)
實現(xiàn)倒影效果后吧史,接下來就是實現(xiàn)圖片旋轉(zhuǎn)時遠小近大的3D效果。
在這里要用到CATransform3D
這個結(jié)構(gòu)體唠雕。API中我們可以看到CATransform3D
是一個4X4的矩陣贸营,如下:
struct CATransform3D
{
CGFloat m11, m12, m13, m14;
CGFloat m21, m22, m23, m24;
CGFloat m31, m32, m33, m34;
CGFloat m41, m42, m43, m44;
};
這里的矩陣乘法公式如下圖:
公式中我們可以看到,m34這個值影響了z軸的translation及塘,其默認值是0莽使,這里設(shè)置m34值,就可以實現(xiàn)遠小近大的效果:
這里需要給一個旋轉(zhuǎn)角度笙僚,否則無法看到3D效果芳肌。
- (void)viewDidLoad {
[super viewDidLoad];
CATransform3D transform0 = CATransform3DIdentity;
transform0.m34 = -1 / 550.0;
_img.layer.transform = CATransform3DRotate(transform0, M_PI_4, 0, 1, 0);
}
這里要注意的是,旋轉(zhuǎn)軸的原點是圖層的錨點anchorPoint
,默認是中心點(0.5亿笤,0.5),要想繞邊軸旋轉(zhuǎn)則要相應(yīng)設(shè)置設(shè)置錨點為(0.0翎迁,0.5)或(1.0,0.5)净薛。
實現(xiàn)倒影效果的3D旋轉(zhuǎn)
了解上面的兩種效果后汪榔,我們就可以實現(xiàn)帶倒影的3D輪播圖效果了。
具體思路就是用UIScrollView
寫一個輪播圖,加載圖片的是一個繼承自UIView
的自定義類肃拜,在其上加載圖片并實現(xiàn)倒影效果痴腌,對其進行輪播旋轉(zhuǎn)操作。
效果問首效果圖所示燃领。
這里主要是設(shè)置UIScrollView
的代理士聪,并在在代理方式- (void)scrollViewDidScroll:(UIScrollView *)scrollView
中實現(xiàn)3D旋轉(zhuǎn)效果。
在代理方法中分別獲得當(dāng)前展示的圖和即將展示的下一張圖猛蔽,在滑動手勢下不斷改變兩張圖的旋轉(zhuǎn)角度即可剥悟。原理不難,但實現(xiàn)起來還是有點復(fù)雜的曼库。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat c = ceilf(scrollView.contentOffset.x / _w);
if (c > 0 && c < _imgViews.count) {
CGFloat value = (scrollView.contentOffset.x - (c - 1) * _w) / _w; // 滑動時的值范圍0~1
// 當(dāng)前展示圖的實時旋轉(zhuǎn)角度
CGFloat angle0 = M_PI_2 * value;
// 當(dāng)前展示的圖
HLImageView *img0 = (HLImageView *)_imgViews[(int)c - 1];
img0.layer.anchorPoint = CGPointMake(0.0, 0.5);
img0.layer.position = CGPointMake(img0.posiP.x - img0.frame.size.width / 2.0 * (1 - value),img0.posiP.y);
// 設(shè)置3D旋轉(zhuǎn)效果
CATransform3D transform0 = CATransform3DIdentity;
transform0.m34 = -1 / 550.0;
img0.layer.transform = CATransform3DRotate(transform0, angle0, 0, 1, 0);
// 當(dāng)前展示圖后一張圖的實時旋轉(zhuǎn)角度
CGFloat angle1 = M_PI_2 * (1 - value);
// 當(dāng)前展示圖的后一張圖
HLImageView *img1 = (HLImageView *)_imgViews[(int)c];
img1.layer.anchorPoint = CGPointMake(1.0, 0.5);
img1.layer.position = CGPointMake(img1.posiP.x + img1.frame.size.width / 2.0 * value, img1.posiP.y);
//設(shè)置3D旋轉(zhuǎn)效果
CATransform3D transform1 = CATransform3DIdentity;
transform1.m34 = -1 / 550.0;
img1.layer.transform = CATransform3DRotate(transform1, -angle1, 0, 1, 0);
NSLog(@"===========:%f--------:%f=========:%f",scrollView.contentOffset.x,c,value);
}
if (scrollView.contentOffset.x == _w * (_imgViews.count - 1)) {
HLImageView *firstImg = (HLImageView *)_imgViews[1];
firstImg.layer.transform = CATransform3DIdentity;
scrollView.contentOffset = CGPointMake(_w, 0);
} else if (scrollView.contentOffset.x == 0) {
HLImageView *lastImg = (HLImageView *)_imgViews[imgCount];
lastImg.layer.transform = CATransform3DIdentity;
scrollView.contentOffset = CGPointMake(_w * imgCount, 0);
}
}
無限輪播
可能有人注意到了上面代理方法里最后部分的
if (scrollView.contentOffset.x == _w * (_imgViews.count - 1)) {
HLImageView *firstImg = (HLImageView *)_imgViews[1];
firstImg.layer.transform = CATransform3DIdentity;
scrollView.contentOffset = CGPointMake(_w, 0);
} else if (scrollView.contentOffset.x == 0) {
HLImageView *lastImg = (HLImageView *)_imgViews[imgCount];
lastImg.layer.transform = CATransform3DIdentity;
scrollView.contentOffset = CGPointMake(_w * imgCount, 0);
}
這里實現(xiàn)的就是無限輪播区岗。我的demo中有5張不同的圖,按順序編號從1到5毁枯,設(shè)置輪播圖如下:
圖號:5慈缔、1、2后众、3胀糜、4颅拦、5蒂誉、1
順序:1、2距帅、3右锨、4、5碌秸、6绍移、7
總共在UIScrollView
中添加了7張圖,其中首張為最后一張圖5
讥电,最后一張為第一張圖1
蹂窖。
這樣在輪播到第7張圖1
時立刻跳轉(zhuǎn)到第2張圖1
,輪播到第1張圖5
時立即跳到第6張圖5
恩敌。即產(chǎn)生了無限輪播的效果瞬测。
更新:自動輪播及點擊圖片回調(diào)。
源碼:GitHub地址