在iPad上的3D旋转木马效果

我正试图在iPad上实现一个3D旋转木马,包含UIViews,类似于这里显示的效果。

我也经历了许多类似的问题,但没有find任何满意的答案,也没有find答案。

我试图通过修改coverflowanimation来实现这个效果,但是它并没有给出那种光滑的效果。

有没有人实现了这个?(通过石英和OpenGL开放的build议)

不需要深入Quartz或OpenGL,假设你不介意模糊。 你链接到的页面会导致错误的视angular(这就是为什么背景中的图像比前景中的图像移动得更快),所以math可能会有点烟雾和镜像。

底部有完整的示例代码。 我所做的是用正弦和余弦来移动一些观点。 其背后的基本理论是位于原点的半径为r的圆的外侧的angular度α处于(a * sin(r),a * cos(r))处。 这是笛卡尔转换的一个简单的极性,应该从大多数国家教给他们的十几岁的三angular学中清楚; 考虑一个长度为a的斜边的直angular三angular形 – 其他两边的长度是多less?

然后你可以做的是减lessy部分的半径,将圆转换成椭圆。 而椭圆看起来有点像一个圆形,你从一个angular度看。 这忽视了透视的可能性,但随之而去。

然后我通过使尺寸与y坐标成比例来伪造视angular。 而且我正在调整阿尔法,就像你链接的网站模糊不清,希望对你的应用程序来说足够好。

我通过调整我想操作的UIViews的仿射变换来影响位置和缩放比例。 我直接在UIView上设置alpha。 我也调整视图层上的zPosition(这就是导入QuartzCore的原因)。 zPosition就像CSS z的位置; 它不影响比例尺,只能绘制顺序。 所以通过设定它等于我计算的比例,它只是说“在较小的东西上画更大的东西”,给我们正确的画图顺序。

手指跟踪是通过在touchesBegan / touchesMoved / touchesEnded周期中每次跟随一个UITouch完成的。 如果没有手指正在被追踪并且一些触摸开始,其中一个手指正被追踪。 如果它移动,则传送带旋转。

为了产生惯性,我有一个附加到定时器的小方法来跟踪当前angular度与之前的angular度。 这种差异就像速度一样使用,同时向下缩放产生惯性。

计时器是用手指开始的,因为那时旋转木马应该开始旋转自己的意志。 如果传送带静止或新手指放下,则停止。

留下你填写空白,我的代码是:

#import <QuartzCore/QuartzCore.h> @implementation testCarouselViewController - (void)setCarouselAngle:(float)angle { // we want to step around the outside of a circle in // linear steps; work out the distance from one step // to the next float angleToAdd = 360.0f / [carouselViews count]; // apply positions to all carousel views for(UIView *view in carouselViews) { float angleInRadians = angle * M_PI / 180.0f; // get a location based on the angle float xPosition = (self.view.bounds.size.width * 0.5f) + 100.0f * sinf(angleInRadians); float yPosition = (self.view.bounds.size.height * 0.5f) + 30.0f * cosf(angleInRadians); // get a scale too; effectively we have: // // 0.75f the minimum scale // 0.25f the amount by which the scale varies over half a circle // // so this will give scales between 0.75 and 1.25. Adjust to suit! float scale = 0.75f + 0.25f * (cosf(angleInRadians) + 1.0); // apply location and scale view.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(xPosition, yPosition), scale, scale); // tweak alpha using the same system as applied for scale, this time // with 0.3 the minimum and a semicircle range of 0.5 view.alpha = 0.3f + 0.5f * (cosf(angleInRadians) + 1.0); // setting the z position on the layer has the effect of setting the // draw order, without having to reorder our list of subviews view.layer.zPosition = scale; // work out what the next angle is going to be angle += angleToAdd; } } - (void)animateAngle { // work out the difference between the current angle and // the last one, and add that again but made a bit smaller. // This gives us inertial scrolling. float angleNow = currentAngle; currentAngle += (currentAngle - lastAngle) * 0.97f; lastAngle = angleNow; // push the new angle into the carousel [self setCarouselAngle:currentAngle]; // if the last angle and the current one are now // really similar then cancel the animation timer if(fabsf(lastAngle - currentAngle) < 0.001) { [animationTimer invalidate]; animationTimer = nil; } } // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - (void)viewDidLoad { [super viewDidLoad]; // create views that are an 80x80 rect, centred on (0, 0) CGRect frameForViews = CGRectMake(-40, -40, 80, 80); // create six views, each with a different colour. carouselViews = [[NSMutableArray alloc] initWithCapacity:6]; int c = 6; while(c--) { UIView *view = [[UIView alloc] initWithFrame:frameForViews]; // We don't really care what the colours are as long as they're different, // so just do anything view.backgroundColor = [UIColor colorWithRed:(c&4) ? 1.0 : 0.0 green:(c&2) ? 1.0 : 0.0 blue:(c&1) ? 1.0 : 0.0 alpha:1.0]; // make the view visible, also add it to our array of carousel views [carouselViews addObject:view]; [self.view addSubview:view]; } currentAngle = lastAngle = 0.0f; [self setCarouselAngle:currentAngle]; /* Note: I've omitted viewDidUnload for brevity; remember to implement one and clean up after all the objects created here */ } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // if we're not already tracking a touch then... if(!trackingTouch) { // ... track any of the new touches, we don't care which ... trackingTouch = [touches anyObject]; // ... and cancel any animation that may be ongoing [animationTimer invalidate]; animationTimer = nil; lastAngle = currentAngle; } } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { // if our touch moved then... if([touches containsObject:trackingTouch]) { // use the movement of the touch to decide // how much to rotate the carousel CGPoint locationNow = [trackingTouch locationInView:self.view]; CGPoint locationThen = [trackingTouch previousLocationInView:self.view]; lastAngle = currentAngle; currentAngle += (locationNow.x - locationThen.x) * 180.0f / self.view.bounds.size.width; // the 180.0f / self.view.bounds.size.width just says "let a full width of my view // be a 180 degree rotation" // and update the view positions [self setCarouselAngle:currentAngle]; } } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { // if our touch ended then... if([touches containsObject:trackingTouch]) { // make sure we're no longer tracking it trackingTouch = nil; // and kick off the inertial animation animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(animateAngle) userInfo:nil repeats:YES]; } } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { // treat cancelled touches exactly like ones that end naturally [self touchesEnded:touches withEvent:event]; } @end 

所以相关的成员variables是一个可变数组,'carouselViews',一个计时器,'animationTimer',两个浮点数,'currentAngle'和'lastAngle',以及一个UITouch,'trackingTouch'。 显然你可能想要使用不仅仅是彩色方块的视图,而且你可能想要调整我为了定位而抽出的数字。 否则,它应该只是工作。

编辑:我应该说,我用Xcode中的iPhone“基于视图的应用程序”模板编写和testing了这个代码。 创build该模板,将我的东西转储到创build的视图控制器中,并添加必要的成员variables进行testing。 不过,我已经意识到,触摸跟踪假定180度是你的视图的全部宽度,但是setCarouselAngle:方法强制转盘总是280点(这是xPosition上的100乘数乘以2,再加上a的宽度视图)。 所以如果你在iPad上运行,手指跟踪会显得太慢。 解决办法显然不是假设视图宽度是180度,但是这只是一个练习!

一个伟大的开源代码的不同types的封面,包括通告 – https://github.com/demosthenese/iCarousel

编辑:

资源库的新path – https://github.com/nicklockwood/iCarousel