iPad上的3D旋转木马效果

iPad上的3D旋转木马效果,ipad,ios,opengl-es,quartz-graphics,ios-4.2,Ipad,Ios,Opengl Es,Quartz Graphics,Ios 4.2,我正试图在iPad上实现一个3D旋转木马,它由UIView组成,就像上面显示的那样 我已经问了很多类似的问题,但是没有找到任何厂里的答案或者根本没有答案 我试图通过修改coverflow动画来实现这个效果,但它并没有提供平滑的效果 是否有人实现了这一点?(通过quartz和openGL均可获得建议)假设您不介意前面提到的模糊,则无需深入研究quartz或openGL。你链接到的页面的透视图错误(这就是为什么背景中的图像比前景中的图像移动得更快的原因),因此数学计算可能有点冒烟 底部有完整的示例代

我正试图在iPad上实现一个3D旋转木马,它由UIView组成,就像上面显示的那样

我已经问了很多类似的问题,但是没有找到任何厂里的答案或者根本没有答案

我试图通过修改coverflow动画来实现这个效果,但它并没有提供平滑的效果


是否有人实现了这一点?(通过quartz和openGL均可获得建议)

假设您不介意前面提到的模糊,则无需深入研究quartz或openGL。你链接到的页面的透视图错误(这就是为什么背景中的图像比前景中的图像移动得更快的原因),因此数学计算可能有点冒烟

底部有完整的示例代码。我所做的是使用正弦和余弦来移动一些视图。其背后的基本理论是,位于原点的半径为r的圆的外侧,角度为a的点位于(a*sin(r),a*cos(r))。这是一个简单的从极坐标到笛卡尔坐标的转换,应该从大多数国家教给青少年的三角学中清晰可见;考虑一个直角三角形和一个斜边的长度a-其他长度是什么长度?

然后可以减小y部分的半径,将圆转换为椭圆。椭圆看起来有点像从一个角度看的圆。这忽略了透视的可能性,但还是跟着它走

然后我通过使尺寸与y坐标成比例来伪造透视图。我正在调整alpha,就像你链接到的站点一样模糊,希望这对你的应用程序来说足够好

我通过调整要操纵的UIView的仿射变换来影响位置和比例。我直接在UIView上设置alpha。我还调整了视图图层上的Z位置(这就是为什么要导入QuartzCore)。z位置类似于CSS z位置;它不影响比例,只影响绘图顺序。因此,通过将它设置为我计算的比例,它只会说“在较小的事物之上绘制较大的事物”,从而给出正确的绘制顺序

通过触摸开始/触摸移动/触摸结束循环一次跟随一个UITouch来完成手指跟踪。如果没有手指被跟踪,并且一些触摸开始,其中一个手指将成为被跟踪的手指。如果它移动,则旋转转盘旋转

为了产生惯性,我有一个附加在计时器上的小方法,它可以跟踪当前角度与一个刻度之前的角度。这种差异就像速度一样被使用,同时向下缩放以产生惯性

计时器是在手指向上的时候启动的,因为旋转木马应该在这个时候根据自己的意愿开始旋转。如果转盘停止或放下新手指,则停止

我的代码是:

#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
#导入
@实现testCarouselViewController
-(无效)旋转木马:(浮动)角度
{
//我们想绕着一个圆圈的外侧走一圈
//线性步数;计算出一步的距离
//到下一个
浮动角度TOADD=360.0f/[旋转视图计数];
//将位置应用于所有旋转木马视图
用于(UIView*旋转视图中的视图)
{
浮动角度半径=角度*M_PI/180.0f;
//根据角度获取位置
浮点X位置=(self.view.bounds.size.width*0.5f)+100.0f*sinf(角度半径);
浮动Y位置=(self.view.bounds.size.height*0.5f)+30.0f*cosf(角度半径);
//还要一个秤;实际上我们有:
//
//0.75f最小刻度
//0.25f刻度在半圈内变化的量
//
//因此,这将给出介于0.75和1.25之间的刻度。调整以适应!
浮动刻度=0.75f+0.25f*(余弦角半径)+1.0);
//应用位置和比例
view.transform=CGAffineTransformScale(CGAffineTransformMakeTransform(xPosition,yPosition),scale,scale);
//这次,使用与缩放相同的系统调整alpha
//最小值为0.3,半圆范围为0.5
view.alpha=0.3f+0.5f*(余弦角半径)+1.0);
//在图层上设置z位置的效果是设置
//绘制顺序,无需重新排列子视图列表
view.layer.zPosition=比例;
//算出下一个角度是什么
角度+=角度添加;
}
}
-(无效)动画角度
{
//计算出当前角度与当前角度之间的差值
//最后一个,再加上一个,但是要小一点。
//这给了我们惯性滚动。
浮动角度现在=当前角度;
currentAngle+=(currentAngle-lastAngle)*0.97f;
lastAngle=angleNow;
//将新角度推入旋转木马
[自定旋转木马:当前角度];
//如果最后一个角度和当前角度现在相同
//非常相似,然后取消动画计时器
if(fabsf(最后角度-当前角度)<0.001)
{
[动画计时器失效];
animationTimer=nil;
}
}
//实现viewDidLoad以在加载视图(通常从nib)后执行附加设置。
-(无效)viewDidLoad
{
[超级视图下载];
//创建以(0,0)为中心的80x80矩形视图
CGRect frameForViews=CGRectMake(-40,-40,80,80);
//创建六个视图,每个视图具有不同的颜色。
carouselview=[[NSMutableArray alloc]initWithCapacity:6];
int c=6;
而(c--)
{
UIView*view=[[UIView alloc]initWithFrame:frameForViews];
//我们不在乎颜色是什么,只要颜色不同,
//所以什么都可以做
view.backgroundColor=[UIColor COLOR WITHRED:(c&4)?1.0:0.0绿色:(c&2)?1.0:0.0蓝色:(c&1)?1.0:0.0阿尔法:1.0];
//使视图可见,并将其添加到我们的汽车阵列中