【问题标题】:Displaying Animation by Frame?按帧显示动画?
【发布时间】:2012-02-20 19:04:37
【问题描述】:

我有一个包含 100 个图像的动画。我希望能够通过一系列帧显示动画。例如,我宁愿使用第 0 帧到第 32 帧,而不是显示从 0:00 秒到 0:32 秒的动画。我需要这样做,因为我需要对动画进行很多控制(即使是一个框架会产生错误的结果)。每一帧都是我亲手绘制的,所以我知道每一帧的图像。

无论如何我可以在 Cocoa 中使用 Objective-C 做到这一点吗?我该怎么做?我只有 100 张图片,我不知道如何从这里开始。而且,与按时间指定相比,这会“滞后”吗?

我希望得到一个详细的答案,因为我在网上找不到任何资源并且对此完全陌生。

请注意,我使用的是 OS X,而不是 iOS,因此请不要提供与 iOS 相关的答案。我在 Snow Leopard 上使用 Xcode 3.2.6。

【问题讨论】:

    标签: objective-c macos cocoa animation


    【解决方案1】:

    获得最大控制权的方法是将所有图像存储在一个数组中。如果您将图像命名为“frame.png”(即“frame1.png”),那么您可以像这样快速加载所有图像:

    frames = [[NSMutableArray alloc] init]; // In header: NSMutableArray *frames;
    for (int i = 1; i < 100; i++) // 100 can be replaced for any number of frames.
    {
        NSString *filename = [NSString stringWithFormat:@"frame%i.png", i];
        [frames addObject:[NSImage imageNamed:filename]];
    }
    

    它的作用是创建一个数组并用您的文件命名的NSImages 填充它。 %i 在字符串中被替换为i 的当前值。

    现在,您需要另一个变量来跟踪当前帧。像unsigned int currentFrame; 这样的东西。这可以在每次更新循环时递增,也可以设置为特定值,让您完全控制显示的帧。

    然后,在您的渲染循环中,您可以像这样渲染当前存储的任何帧:

    [(NSImage *)[frames objectAtIndex:currentFrame - 1] drawAtPoint:animPos fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0f];
    

    animPos 是一个 NSPoint,包含你想要绘制的位置。 - 1 在索引中,因此您可以参考从 1 而不是 0 开始的帧。此代码将图像拉到帧 currentFrame 并将其渲染到屏幕上。

    由于您使用的是数组,请务必检查 currentFrame 永远不会等于大于最大帧数的值,例如:if (currentFrame &gt; 100) currentFrame = 1;。这将导致动画循环,因为当它到达最后一帧时,currentFrame 将被设置回数组的开始索引。

    有关绘图的更多信息,您应该查看:Cocoa Drawing GuideNSImage Class Reference。 此外,如果您需要有关如何在每次增量时更新帧的更多信息,请查看NSTimer Class Reference

    【讨论】:

    • 渲染循环代码会去哪里?我的意思是什么课?会是 NSImageView 吗?
    • drawAtPoint: 代码可以放在任何你想要的类中。它不需要在 NSImageView 内部执行,它可以在窗口中的任何位置渲染,即使没有视图。如果您没有很多类,则将其留在应用程序委托中可能是最简单的。但是,如果你想使用一个NSImageView,你可以使用[NSImageView setImage:(NSImage *)[frames objectAtIndex:currentFrame - 1]]而不是drawAtPoint,让视图处理渲染、定位等。
    【解决方案2】:

    您可以将此代码用于逐帧动画。希望这会对你有所帮助。

     // create the view that will execute our animation
     UIImageView* imageListView = [[UIImageView alloc] initWithFrame:self.view.frame];
    
     // load all the frames of our animation
     imageListView.animationImages = [NSArray arrayWithObjects:    
                                 [UIImage imageNamed:@"campFire01.gif"],
                                 [UIImage imageNamed:@"campFire02.gif"],
                                 [UIImage imageNamed:@"campFire03.gif"],
                                 [UIImage imageNamed:@"campFire04.gif"],
                                 [UIImage imageNamed:@"campFire05.gif"],
                                 [UIImage imageNamed:@"campFire06.gif"],
                                 [UIImage imageNamed:@"campFire07.gif"],
                                 [UIImage imageNamed:@"campFire08.gif"],
                                 [UIImage imageNamed:@"campFire09.gif"],
                                 [UIImage imageNamed:@"campFire10.gif"],
                                 [UIImage imageNamed:@"campFire11.gif"],
                                 [UIImage imageNamed:@"campFire12.gif"],
                                 [UIImage imageNamed:@"campFire13.gif"],
                                 [UIImage imageNamed:@"campFire14.gif"],
                                 [UIImage imageNamed:@"campFire15.gif"],
                                 [UIImage imageNamed:@"campFire16.gif"],
                                 [UIImage imageNamed:@"campFire17.gif"], 
                                  //all your 100 images// , nil];
    
     // all frames will execute in 1.75 seconds
     imageListView.animationDuration = 1.75;
     // repeat the annimation forever
     imageListView.animationRepeatCount = 0;
     // start animating
     [imageListView startAnimating];
     // add the animation view to the main window 
     [self.view addSubview:imageListView];
    

    【讨论】:

      【解决方案3】:

      您可以考虑使用CADisplayLink。这是一个如何使用它的示例:

      在你的初始化方法中,设置显示链接:

      CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(playAnimation:)];
      [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];    
      

      然后添加方法:

      - (void)playAnimation:(CADisplayLink*)displayLink
      {
          // Set the right image to display
          self.imageToDisplay = TheRightImageBasedOnYourAnimationSequence
      
          [self setNeedsDisplay];
      }
      

      最后,在 drawRect: 方法中,显示图像。 CADisplayLink 类具有控制帧持续时间等的属性...

      【讨论】:

        【解决方案4】:

        重申@wquist 的回复:-

        frames = [[NSMutableArray alloc] init]; // In header: NSMutableArray *frames;
        for (int i = 1; i < 100; i++) // 100 can be replaced for any number of frames.
        {
            NSString *filename = [NSString stringWithFormat:@"frame%i.png", i];
            [frames addObject:[NSImage imageNamed:filename]];
        }
        

        在过去,我有一些动画文件名,例如 image_00000.png、image_00001.png,为了照顾它们,我使用了这样的东西:-

        NSString *filename = [NSString stringWithFormat:@"frame%05d.png", i];
        

        【讨论】:

          【解决方案5】:

          【讨论】:

            【解决方案6】:

            您是否尝试过子类化 NSAnimation,并让它改变每个刻度显示的图像?

            编辑:

            首先,您创建一个 NSAnimation 的子类。然后,通过调用其超类实现覆盖setCurrentProgress:,并让它根据currentValue 更改视图。有关更深入的说明,请参阅以下指南:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/AnimationGuide/Articles/TimingAnimations.html#//apple_ref/doc/uid/TP40003581-SW1

            【讨论】:

            • 你能扩展一下吗?我该怎么做?我以前从未使用过 NSAnimation。
            • 这是一个相当简单的过程,在 NSAnimation 的文档中解释得很清楚。有关更多详细信息,请参阅我的编辑。
            【解决方案7】:

            我做了一个你或许可以使用的简单项目:you can find the project here

            我希望这是你需要的

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-12-02
              • 2012-08-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-06-24
              • 2014-09-02
              相关资源
              最近更新 更多