jjzjj

ipad - iPad 上的 3D 轮播效果

coder 2023-07-27 原文

我正在尝试在 iPad 上实现一个 3D 旋转木马,由 UIView 组成,效果类似于 here 中显示的效果.

我在 SO 上经历过许多类似的问题,但没有找到任何令人满意的答案或根本没有答案。

我试图通过修改 coverflow 动画来实现这种效果,但它并没有提供那种流畅的效果。

有没有人实现过这个?(通过 quartz 和 openGL 开放征求建议)

最佳答案

无需深入研究 Quartz 或 OpenGL,假设您不介意前面的模糊。您链接到的页面透视错误(这就是背景中的图像看起来比前景中的图像移动得更快的原因),因此数学可能有点虚幻。

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

然后您可以做的是减小 y 部分的半径,将圆转换为椭圆。椭圆看起来有点像你从一个角度看的圆。这忽略了透视的可能性,但顺其自然。

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

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

手指跟踪是通过在 touchesBegan/touchesMoved/touchesEnded 循环中一次跟随一个 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

所以相关的成员变量是一个可变数组“carouselViews”、一个计时器“animationTimer”、两个 float “currentAngle”和“lastAngle”,以及一个 UITouch“trackingTouch”。显然,您可能想要使用不仅仅是彩色方 block 的 View ,并且您可能想要调整我凭空提取的用于定位的数字。否则,它应该可以正常工作。

编辑:我应该说,我在 Xcode 中使用 iPhone“基于 View 的应用程序”模板编写并测试了这段代码。创建该模板,将我的东西转储到创建的 View Controller 中,并添加必要的成员变量进行测试。但是,我已经意识到触摸跟踪假定 180 度是您 View 的整个宽度,但是 setCarouselAngle: 方法强制旋转木马始终跨 280 个点(这是 xPosition 上的 100 乘数乘以二,加上看法)。因此,如果您在 iPad 上运行手指跟踪,它会显得太慢。解决方案显然不是假设 View 宽度为 180 度,但这留作练习!

关于ipad - iPad 上的 3D 轮播效果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5243614/

有关ipad - iPad 上的 3D 轮播效果的更多相关文章

  1. ruby-on-rails - date_field_tag,如何设置默认日期? [ rails 上的 ruby ] - 2

    我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问

  2. ruby-on-rails - openshift 上的 rails 控制台 - 2

    我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新ruby​​gems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems

  3. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  4. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  5. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  6. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  7. [Vuforia]二.3D物体识别 - 2

    之前说过10之后的版本没有3dScan了,所以还是9.8的版本或者之前更早的版本。 3d物体扫描需要先下载扫描的APK进行扫面。首先要在手机上装一个扫描程序,扫描现实中的三维物体,然后上传高通官网,在下载成UnityPackage类型让Unity能够使用这个扫描程序可以从高通官网上进行下载,是一个安卓程序。点到Tools往下滑,找到VuforiaObjectScanner下载后解压数据线连接手机,将apk文件拷入手机安装然后刚才解压文件中的Media文件夹打开,两个PDF图打印第一张A4-ObjectScanningTarget.pdf,主要是用来辅助扫描的。好了,接下来就是扫描三维物体。将瓶

  8. ruby-on-rails - Ruby - 如何从 ruby​​ 上的 .pfx 文件中提取公钥、rsa 私钥和 CA key - 2

    我有一个.pfx格式的证书,我需要使用ruby​​提取公共(public)、私有(private)和CA证书。使用shell我可以这样做:#ExtractPublicKey(askforpassword)opensslpkcs12-infile.pfx-outfile_public.pem-clcerts-nokeys#ExtractCertificateAuthorityKey(askforpassword)opensslpkcs12-infile.pfx-outfile_ca.pem-cacerts-nokeys#ExtractPrivateKey(askforpassword)o

  9. 带有 attr_accessor 的类上的 Ruby instance_eval - 2

    我了解instance_eval和class_eval之间的基本区别。我在玩弄时发现的是一些涉及attr_accessor的奇怪东西。这是一个例子:A=Class.newA.class_eval{attr_accessor:x}a=A.newa.x="x"a.x=>"x"#...expectedA.instance_eval{attr_accessor:y}A.y="y"=>NoMethodError:undefinedmethod`y='forA:Classa.y="y"=>"y"#WHATTT?这是怎么回事:instance_eval没有访问我们的A类(对象)然后它实际上将它添加到

  10. ruby-on-rails - rails 上的 ruby : radio buttons for collection select - 2

    我有一个集合选择:此方法的单选按钮是什么?谢谢 最佳答案 Rails3中没有这样的助手。在Rails4中,它是collection_radio_buttons. 关于ruby-on-rails-rails上的ruby:radiobuttonsforcollectionselect,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/18525986/

随机推荐