2010-03-07 34 views
13

Tôi đang cố gắng triển khai hoạt ảnh lật được sử dụng trong trò chơi trên bảng như ứng dụng trên iPhone. Các hình ảnh động được cho là trông giống như một mảnh trò chơi mà quay và thay đổi màu sắc của mặt sau của nó (loại giống như một Reversi piece). Tôi đã quản lý để tạo ra một hình ảnh động lật các mảnh xung quanh trục trực giao của nó, nhưng khi tôi cố gắng lật nó xung quanh một trục chéo bằng cách thay đổi luân phiên xung quanh trục z hình ảnh thực tế cũng được xoay (không ngạc nhiên). Thay vào đó, tôi muốn xoay hình ảnh "nguyên trạng" quanh trục chéo.Làm thế nào để tôi xoay một CALayer xung quanh một đường chéo?

Tôi đã cố thay đổi layer.sublayerTransform nhưng không thành công.

Đây là triển khai hiện tại của tôi. Nó hoạt động bằng cách thực hiện một mẹo để giải quyết vấn đề nhận được một hình ảnh được nhân đôi ở cuối hoạt ảnh. Giải pháp là không thực sự xoay lớp 180 độ, thay vào đó nó xoay nó 90 độ, thay đổi hình ảnh và sau đó xoay nó trở lại.

Phiên bản cuối cùng: Dựa trên đề xuất Lorenzos để tạo hoạt ảnh có khóa rời rạc và tính ma trận chuyển đổi cho mỗi khung. Phiên bản này thay vì cố gắng ước tính số lượng khung "hướng dẫn" cần thiết dựa trên kích thước lớp và sau đó sử dụng hoạt ảnh có khóa tuyến tính. Phiên bản này quay với một góc tùy ý để xoay quanh đường chéo sử dụng góc 45 độ.

sử dụng Ví dụ:

[someclass flipLayer:layer image:image angle:M_PI/4] 

Thực hiện:

- (void)animationDidStop:(CAAnimationGroup *)animation 
       finished:(BOOL)finished { 
    CALayer *layer = [animation valueForKey:@"layer"]; 

    if([[animation valueForKey:@"name"] isEqual:@"fadeAnimation"]) { 
    /* code for another animation */ 
    } else if([[animation valueForKey:@"name"] isEqual:@"flipAnimation"]) { 
    layer.contents = [animation valueForKey:@"image"]; 
    } 

    [layer removeAllAnimations]; 
} 

- (void)flipLayer:(CALayer *)layer 
      image:(CGImageRef)image 
      angle:(float)angle { 
    const float duration = 0.5f; 

    CAKeyframeAnimation *rotate = [CAKeyframeAnimation 
           animationWithKeyPath:@"transform"]; 
    NSMutableArray *values = [[[NSMutableArray alloc] init] autorelease]; 
    NSMutableArray *times = [[[NSMutableArray alloc] init] autorelease]; 
    /* bigger layers need more "guiding" values */ 
    int frames = MAX(layer.bounds.size.width, layer.bounds.size.height)/2; 
    int i; 
    for (i = 0; i < frames; i++) { 
    /* create a scale value going from 1.0 to 0.1 to 1.0 */ 
    float scale = MAX(fabs((float)(frames-i*2)/(frames - 1)), 0.1); 

    CGAffineTransform t1, t2, t3; 
    t1 = CGAffineTransformMakeRotation(angle); 
    t2 = CGAffineTransformScale(t1, scale, 1.0f); 
    t3 = CGAffineTransformRotate(t2, -angle); 
    CATransform3D trans = CATransform3DMakeAffineTransform(t3); 

    [values addObject:[NSValue valueWithCATransform3D:trans]]; 
    [times addObject:[NSNumber numberWithFloat:(float)i/(frames - 1)]]; 
    } 
    rotate.values = values; 
    rotate.keyTimes = times; 
    rotate.duration = duration; 
    rotate.calculationMode = kCAAnimationLinear; 

    CAKeyframeAnimation *replace = [CAKeyframeAnimation 
            animationWithKeyPath:@"contents"]; 
    replace.duration = duration/2; 
    replace.beginTime = duration/2; 
    replace.values = [NSArray arrayWithObjects:(id)image, nil]; 
    replace.keyTimes = [NSArray arrayWithObjects: 
         [NSNumber numberWithDouble:0.0f], nil]; 
    replace.calculationMode = kCAAnimationDiscrete; 

    CAAnimationGroup *group = [CAAnimationGroup animation]; 
    group.duration = duration; 
    group.timingFunction = [CAMediaTimingFunction 
          functionWithName:kCAMediaTimingFunctionLinear]; 
    group.animations = [NSArray arrayWithObjects:rotate, replace, nil]; 
    group.delegate = self; 
    group.removedOnCompletion = NO; 
    group.fillMode = kCAFillModeForwards; 
    [group setValue:@"flipAnimation" forKey:@"name"]; 
    [group setValue:layer forKey:@"layer"]; 
    [group setValue:(id)image forKey:@"image"]; 

    [layer addAnimation:group forKey:nil]; 
} 

đang Original:

+ (void)flipLayer:(CALayer *)layer 
      toImage:(CGImageRef)image 
     withAngle:(double)angle { 
    const float duration = 0.5f; 

    CAKeyframeAnimation *diag = [CAKeyframeAnimation 
           animationWithKeyPath:@"transform.rotation.z"]; 
    diag.duration = duration; 
    diag.values = [NSArray arrayWithObjects: 
       [NSNumber numberWithDouble:angle], 
       [NSNumber numberWithDouble:0.0f], 
       nil]; 
    diag.keyTimes = [NSArray arrayWithObjects: 
        [NSNumber numberWithDouble:0.0f], 
        [NSNumber numberWithDouble:1.0f], 
        nil]; 
    diag.calculationMode = kCAAnimationDiscrete; 

    CAKeyframeAnimation *flip = [CAKeyframeAnimation 
           animationWithKeyPath:@"transform.rotation.y"]; 
    flip.duration = duration; 
    flip.values = [NSArray arrayWithObjects: 
       [NSNumber numberWithDouble:0.0f], 
       [NSNumber numberWithDouble:M_PI/2], 
       [NSNumber numberWithDouble:0.0f], 
       nil]; 
    flip.keyTimes = [NSArray arrayWithObjects: 
        [NSNumber numberWithDouble:0.0f], 
        [NSNumber numberWithDouble:0.5f], 
        [NSNumber numberWithDouble:1.0f], 
        nil]; 
    flip.calculationMode = kCAAnimationLinear; 

    CAKeyframeAnimation *replace = [CAKeyframeAnimation 
            animationWithKeyPath:@"contents"]; 
    replace.duration = duration/2; 
    replace.beginTime = duration/2; 
    replace.values = [NSArray arrayWithObjects:(id)image, nil]; 
    replace.keyTimes = [NSArray arrayWithObjects: 
         [NSNumber numberWithDouble:0.0f], nil]; 
    replace.calculationMode = kCAAnimationDiscrete; 

    CAAnimationGroup *group = [CAAnimationGroup animation]; 
    group.removedOnCompletion = NO; 
    group.duration = duration; 
    group.timingFunction = [CAMediaTimingFunction 
          functionWithName:kCAMediaTimingFunctionLinear]; 
    group.animations = [NSArray arrayWithObjects:diag, flip, replace, nil]; 
    group.fillMode = kCAFillModeForwards; 

    [layer addAnimation:group forKey:nil]; 
} 
+0

một là gì trục chéo? – kennytm

+0

Bằng trục chéo, tôi có nghĩa là vòng xoay phải nằm quanh đường chéo của lớp. Giống như nếu bạn nắm lấy hai góc chéo của lớp và xoay nó xung quanh. –

+2

Giống như, xoay về đường thẳng 'y = x'? – kennytm

Trả lời

6

bạn có thể giả mạo nó theo cách này: tạo một affine biến mà sụp đổ lớp cùng nó đường chéo:

A-----B   B 
|  |  /
|  | -> A&D 
|  |  /
C-----D  C 

thay đổi hình ảnh và trasform lại CALayer trong một hoạt ảnh khác. Điều này sẽ tạo ảo ảnh của lớp xoay quanh đường chéo của nó.

ma trận cho rằng nên nếu tôi nhớ toán một cách chính xác:

0.5 0.5 0 
0.5 0.5 0 
0 0 1 

Cập nhật: ok, CA doen't thực sự thích sử dụng biến đổi thoái hóa, nhưng bạn có thể xấp xỉ nó theo cách này:

CGAffineTransform t1 = CGAffineTransformMakeRotation(M_PI/4.0f); 
CGAffineTransform t2 = CGAffineTransformScale(t1, 0.001f, 1.0f); 
CGAffineTransform t3 = CGAffineTransformRotate(t2,-M_PI/4.0f); 

trong các thử nghiệm của tôi trên giả lập vẫn còn là một vấn đề bởi vì quay xảy ra nhanh hơn so với dịch te vì vậy với một hình vuông màu đen rắn hiệu quả là một chút lạ. Tôi cho rằng nếu bạn có một sprite trung tâm với vùng trong suốt xung quanh nó thì hiệu ứng sẽ gần với những gì mong đợi. Sau đó, bạn có thể tinh chỉnh giá trị của ma trận t3 để xem liệu bạn có nhận được kết quả hấp dẫn hơn hay không.

sau khi nghiên cứu thêm, có vẻ như người ta nên tạo hiệu ứng chuyển tiếp của chính nó thông qua khung hình chính để tránh sự kiểm soát tối đa của quá trình chuyển đổi.nói rằng bạn đã hiển thị hoạt ảnh này trong một giây, bạn nên tạo mười ma trận được hiển thị ở mỗi phần mười của một giây với nội suy không sử dụng kCAAnimationDiscrete; những ma trận có thể được tạo ra thông qua các mã bên dưới:

CGAffineTransform t1 = CGAffineTransformMakeRotation(M_PI/4.0f); 
CGAffineTransform t2 = CGAffineTransformScale(t1, animationStepValue, 1.0f); 
CGAffineTransform t3 = CGAffineTransformRotate(t2,-M_PI/4.0f); 

nơi animationStepValue cho ech của keyframe được lấy từ sự tiến triển này:

{1 0.7 0.5 0.3 0.1 0.3 0.5 0.7 1} 

đó là: bạn đang tạo mười ma trận biến đổi khác nhau (trên thực tế 9), đẩy chúng thành khung hình chính được hiển thị ở mỗi phần mười giây và sau đó sử dụng tham số "không nội suy". bạn có thể tinh chỉnh số hoạt ảnh để cân bằng độ mượt và hiệu suất *

* xin lỗi vì các lỗi có thể xảy ra, phần cuối này được viết mà không có trình kiểm tra chính tả.

+0

Cảm ơn câu trả lời của bạn. Giả định ASCII có phải là sự chuyển đổi cắt không? nếu tôi cố gắng làm động từ danh tính, sau đó đến ma trận của bạn và chúng trở lại danh tính tôi không có hình ảnh nào cả: ( Tôi đã tìm cách tạo một ma trận cắt và trông như sau: {{1, sheary, 0} , {shearx, 1, 0}, {0, 0, 1}} Nếu tôi sử dụng điều đó trong hoạt ảnh, đường nối sẽ hoạt động một chút nhưng tôi cần cắt khá nhiều và sau đó hình ảnh sẽ được thu nhỏ lại một chút. –

+0

không cắt sẽ làm cho một hình bình hành, ở đây chúng tôi đang cố gắng để có được một hình thoi thoái hóa, với A & D góc di chuyển về phía trung tâm và không phải là góc A & B di chuyển về phía trái tôi sẽ phải cố gắng này trên thiết bị ngày mai để có được Các thông số ma trận thực tế –

+0

Ok, tôi đã đọc các biến đổi affine thực sự hoạt động như thế nào và sự biến đổi bạn mô tả với sơ đồ như bạn nói là affine, bảo toàn các điểm trên một đường thẳng và chuột ios (miễn là A và D là "đối lập" tôi đoán?). Nhưng tôi havent thực sự figured làm thế nào để suy nghĩ về những con số trong ma trận. –

4

Tôi đã giải quyết nó. Bạn có thể đã có một giải pháp là tốt, nhưng đây là những gì tôi đã tìm thấy. Nó là khá đơn giản thực sự ...

Bạn có thể sử dụng CABasicAnimation để thực hiện phép xoay chéo, nhưng nó cần phải ghép nối hai ma trận, cụ thể là ma trận hiện có của lớp, cộng với CATransform3DRotate. Các "lừa" là, trong 3DRotate bạn cần phải xác định các tọa độ để xoay xung quanh.

Mã này trông giống như sau:


CATransform3DConcat(theLayer.transform, CATransform3DRotate(CATransform3DIdentity, M_PI/2, -1, 1, 0)); 

này sẽ làm cho một vòng quay xuất hiện như thể góc trên bên trái của hình vuông được quay xung quanh trục Y = X, và đi du lịch đến thấp hơn lại góc phải.

Mã để animate trông như sau:


CABasicAnimation *ani1 = [CABasicAnimation animationWithKeyPath:@"transform"]; 

// set self as the delegate so you can implement (void)animationDidStop:finished: to handle anything you might want to do upon completion 
[ani1 setDelegate:self]; 

// set the duration of the animation - a float 
[ani1 setDuration:dur]; 

// set the animation's "toValue" which MUST be wrapped in an NSValue instance (except special cases such as colors) 
ani1.toValue = [NSValue valueWithCATransform3D:CATransform3DConcat(theLayer.transform, CATransform3DRotate(CATransform3DIdentity, M_PI/2, -1, 1, 0))]; 

// give the animation a name so you can check it in the animationDidStop:finished: method 
[ani1 setValue:@"shrink" forKey:@"name"]; 

// finally, apply the animation 
[theLayer addAnimation:ani1 [email protected]"arbitraryKey"]; 

Vậy là xong! Mã đó sẽ xoay hình vuông (theLayer) thành tàng hình khi nó di chuyển 90 độ và trình bày chính nó trực giao với màn hình. Sau đó, bạn có thể thay đổi màu sắc và thực hiện chính xác cùng một hoạt ảnh để đưa nó trở lại. Hoạt ảnh tương tự hoạt động vì chúng tôi đang ghép các ma trận, vì vậy mỗi lần bạn muốn xoay, chỉ cần thực hiện hai lần hoặc thay đổi M_PI/2 thành M_PI.

Cuối cùng, cần lưu ý, và điều này đã thúc đẩy tôi, sau khi hoàn thành, lớp sẽ quay trở lại trạng thái ban đầu trừ khi bạn đặt nó thành trạng thái hoạt ảnh kết thúc một cách rõ ràng. Điều này có nghĩa là ngay trước dòng [theLayer addAnimation:ani1 [email protected]"arbitraryKey"]; bạn sẽ muốn thêm


theLayer.transform = CATransform3DConcat(v.theSquare.transform, CATransform3DRotate(CATransform3DIdentity, M_PI/2, -1, 1, 0)); 

để đặt giá trị sau khi hoạt ảnh hoàn tất. Điều này sẽ ngăn chặn việc chụp lại trạng thái ban đầu.

Hy vọng điều này sẽ hữu ích. Nếu không thì bạn có lẽ ai đó đang đập đầu vào tường như chúng tôi!:)

Chúc mừng,

Chris

+0

Tôi nghĩ rằng tôi đã thử một giải pháp tương tự nhưng với vấn đề xoay chuyển đổi quay nội dung hình ảnh thực tế. Nhưng nếu lớp là một hình ảnh đối xứng, bạn sẽ không nhận thấy nó. –

+0

bạn có thể giải thích những gì 'v.theSquare' là ở đây? – aBikis

1

Dưới đây là một ví dụ Xamarin iOS tôi sử dụng để vỗ góc của một nút vuông, giống như một tai chó (dễ dàng được chuyển đến obj-C):

enter image description here

Phương pháp 1: sử dụng một hình ảnh động xoay với 1 cho cả xy trục (ví dụ trong Xamarin.iOS, bu t dễ dàng cầm tay để obj-C):

// add to the UIView subclass you wish to rotate, where necessary 
AnimateNotify(0.10, 0, UIViewAnimationOptions.CurveEaseOut | UIViewAnimationOptions.AllowUserInteraction | UIViewAnimationOptions.BeginFromCurrentState,() => 
{ 
    // note the final 3 params indicate "rotate around x&y axes, but not z" 
    var transf = CATransform3D.MakeRotation(-1 * (nfloat)Math.PI/4, 1, 1, 0); 
    transf.m34 = 1.0f/-500; 
    Layer.Transform = transf; 
}, null); 

Cách 2: chỉ cần thêm một x-axis luân chuyển, và y-axis luân chuyển đến một CAAnimationGroup để họ chạy cùng một lúc:

// add to the UIView subclass you wish to rotate, where necessary 
AnimateNotify(1.0, 0, UIViewAnimationOptions.CurveEaseOut | UIViewAnimationOptions.AllowUserInteraction | UIViewAnimationOptions.BeginFromCurrentState,() => 
{ 
    nfloat angleTo = -1 * (nfloat)Math.PI/4; 
    nfloat angleFrom = 0.0f ; 
    string animKey = "rotate"; 

    // y-axis rotation 
    var anim = new CABasicAnimation(); 
    anim.KeyPath = "transform.rotation.y"; 
    anim.AutoReverses = false; 
    anim.Duration = 0.1f; 
    anim.From = new NSNumber(angleFrom); 
    anim.To = new NSNumber(angleTo); 

    // x-axis rotation 
    var animX = new CABasicAnimation(); 
    animX.KeyPath = "transform.rotation.x"; 
    animX.AutoReverses = false; 
    animX.Duration = 0.1f; 
    animX.From = new NSNumber(angleFrom); 
    animX.To = new NSNumber(angleTo); 

    // add both rotations to a group, to run simultaneously 
    var animGroup = new CAAnimationGroup(); 
    animGroup.Duration = 0.1f; 
    animGroup.AutoReverses = false; 
    animGroup.Animations = new CAAnimation[] {anim, animX}; 
    Layer.AddAnimation(animGroup, animKey); 

    // add perspective 
    var transf = CATransform3D.Identity; 
    transf.m34 = 1.0f/500; 
    Layer.Transform = transf; 

}, null); 
+0

Không chắc chắn tại sao điều này lại phản đối một cuộc bỏ phiếu xuống. Đây là vòng xoay CALayer về đường chéo. "Hiển thị công việc của bạn", downvoter. – bunkerdive

+0

Điều này thực sự tốt đẹp! chính xác những gì tôi đang tìm kiếm. Tôi không nhìn thấy nơi bạn đã chỉ định khu vực góc, Trong góc ví dụ của bạn là lớn đang di chuyển như tai chó Tôi muốn tai nhỏ – nikhilgohil11

+0

@ nikhilgohil11, khu vực "+" lớn là một nút riêng biệt (UIView). Các nút góc thực sự là một hình vuông (một nửa bao phủ bởi nút lớn) đang được luân chuyển. Tóm lại, lấy được 2 Button sub-classes: BigButton và CornerButton. Đặt một cá thể CornerButton bên dưới một BigButton (một nửa được bảo vệ), và khi CornerButton được nhấn xuống, hãy gọi mã xoay. – bunkerdive

Các vấn đề liên quan