美文网首页
如何实现一个不占内存的帧动画

如何实现一个不占内存的帧动画

作者: 费宇超 | 来源:发表于2017-09-09 19:53 被阅读279次

我分到另一个项目里的时候,发现首页有一个比较炫酷的动画,比较好奇是怎么实现,当时同事和我说用的是airbnb的一个库:Lottile,iOS和安卓都有,甚至RN版本都有。
大致就是ui生成一个json文件,客户端只要导入就能用了,该库自己会完成json文件渲染成动画的过程。

后来有一期,因为需要实现组件化,资源只能自己打包成bundle,导致图怎么都渲染不出来。只好临时弃用,换成了帧动画实现。一共600多帧的动画,不仅图片资源占的空间很大,而且运行时内存可以到290M,不用想就异常可怕了。并且安卓每个app分到的资源就只有120M。

思考能否自己生成一个GIF文件播放GIF是否可行。
做过视频的朋友应该都知道视频有关键帧和补偿帧的概念,而GIF的原理和视频一样,有关键帧和补偿帧,简单说只有第一张图是完整的,后面的只保存与前图不同的部分。所以GIF体积很小,效果却很好。

直接导入GIF肯定不可行,不同屏幕下的表现并不理想。

我就想到YYkit里有播放GIF的代码,那是否有生成GIF的办法。一看还真有。
YYFrameImage,其实也不是生成了gif。它是将图片组成一个数组而已。
性能优化的亮点还是在YYAnimationview里。
下面看了YYimage 再来说一说为什么YYimage播放帧动画会不占内存。

主要是代码里三点。

    for (int i = 0; i < max; i++, idx++) {
        @autoreleasepool {
            if (idx >= total) idx = 0;
            if ([self isCancelled]) break;
            __strong YYAnimatedImageView *view = _view;
            if (!view) break;
            LOCK_VIEW(BOOL miss = (view->_buffer[@(idx)] == nil));
            
            if (miss) {
                UIImage *img = [_curImage animatedImageFrameAtIndex:idx];
                img = img.yy_imageByDecoded;
                if ([self isCancelled]) break;
                LOCK_VIEW(view->_buffer[@(idx)] = img ? img : [NSNull null]);
                view = nil;
            }
        }
    }

自动释放池就不说了,他每一次播放一张,就释放一张,这个安卓的同事后来也做了,内存降到了30m左右。

// dynamically adjust buffer size for current memory.
- (void)calcMaxBufferCount {
    int64_t bytes = (int64_t)_curAnimatedImage.animatedImageBytesPerFrame;
    if (bytes == 0) bytes = 1024;
    
    int64_t total = _YYDeviceMemoryTotal();
    int64_t free = _YYDeviceMemoryFree();
    int64_t max = MIN(total * 0.2, free * 0.6);
    max = MAX(max, BUFFER_SIZE);
    if (_maxBufferSize) max = max > _maxBufferSize ? _maxBufferSize : max;
    double maxBufferCount = (double)max / (double)bytes;
    if (maxBufferCount < 1) maxBufferCount = 1;
    else if (maxBufferCount > 512) maxBufferCount = 512;
    _maxBufferCount = maxBufferCount;
}

动态管理了内存,避免了峰值。

- (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
    if (_imagePaths) {
        if (index >= _imagePaths.count) return nil;
        NSString *path = _imagePaths[index];
        CGFloat scale = _NSStringPathScale(path);
        NSData *data = [NSData dataWithContentsOfFile:path];
        return [[UIImage imageWithData:data scale:scale] yy_imageByDecoded];
    } else if (_imageDatas) {
        if (index >= _imageDatas.count) return nil;
        NSData *data = _imageDatas[index];
        return [[UIImage imageWithData:data scale:[UIScreen mainScreen].scale] yy_imageByDecoded];
    } else {
        return index == 0 ? self : nil;
    }
}

这里也是关键,有人可能会问为什么不直接用uiimage直接转。uiimage生成图片并不是马上生成,都会到GPU上统一渲染,会造成大量的内存占用。

/**
 A proxy used to hold a weak object.
 It can be used to avoid retain cycles, such as the target in NSTimer or CADisplayLink.
 */
@interface _YYImageWeakProxy : NSProxy
@property (nonatomic, weak, readonly) id target;
- (instancetype)initWithTarget:(id)target;
+ (instancetype)proxyWithTarget:(id)target;
@end

这里的nsproxy 避免了循环引用。

最后播放时占有的内存1M都不到,但由于占的资源太多,依然不推荐帧动画的做法,之后会介绍Lottie是如何将Json文件渲染成动画的。

相关文章

  • 如何实现一个不占内存的帧动画

    我分到另一个项目里的时候,发现首页有一个比较炫酷的动画,比较好奇是怎么实现,当时同事和我说用的是airbnb的一个...

  • 每帧1MB的图片做帧动画会卡?不存在的!—— SurfaceVi

    继上篇用“SurfaceView逐帧解析 & 帧复用”优化了帧动画内存性能后,一个更复杂的问题付出了水面:帧动画时...

  • Android帧动画的实现

    帧动画就是图片的循环播放 实现帧动画有两种方式: 1.通过代码实现: //帧动画对象AnimationDrawab...

  • 动画

    帧:是动画里的最小单位,它是静态的图像帧频:是一秒钟播放的帧的数量一个完整的动画是如何实现的呢?首先它是由很多帧组...

  • Days18 H5+Css3

    1.使用动画 (js实现动画,css3实现动画) 一个是帧动画 一个是补间动画 什么是帧动画:使用定时器 每隔一段...

  • UIImageView 序列帧动画的实现以及内存的优化

    UIImageView 序列帧动画的实现以及内存的优化 最近笔者项目中又一次遇到了UIImageView的序列帧动...

  • Android 动画使用详解(一) 帧动画

    动画在Android开发中经常会被用到,好的动画效果可以达到事半功倍的效果,本文主要是帧动画的实现。帧动画主要实现...

  • 浅谈swift动画(二)

    逐帧动画 逐帧动画的实现就是将图片一帧一帧的渲染,然后在短时间内依次展示出来,即可实现动画效果。 基于NSTime...

  • UIImage 加载简易动画效果

    序列帧动画概念和序列帧动画实现 方式1: 方式2:

  • Android 动画锦集

    Android 动画可分为逐帧动画、补间动画、属性动画。使用传统的逐帧动画、补间动画可以实现 Android 基本...

网友评论

      本文标题:如何实现一个不占内存的帧动画

      本文链接:https://www.haomeiwen.com/subject/eyjajxtx.html