package LibCore.ui.core { import LibCore.utils.ClassUtils; import LibCore.utils.DisplayObjectUtils; import LibCore.utils.NumberUtils; import flash.display.Bitmap; import flash.display.FrameLabel; import flash.display.MovieClip; import flash.events.Event; import flash.geom.Point; import flash.utils.Dictionary; /** * 采用位图缓存处理的影片剪辑 * <p> * <li>对于内容复杂(尤其是使用了很多滤镜)但固定的影片剪辑,非常耗CPU,而Flash自带的位图缓存机制又无法加以优化,因此采用手动方式实现</li> * <li>将原影片剪辑的每帧缓存为一张位图,并始终保存,实际使用时用位图代替原影片剪辑显示,且支持多个实例间共享</li> * </p> * @author Fictiony * @version 2017/3/21 */ public class BmpCacheMovieClip extends MovieClip { static private var _bmpCacheMap:Dictionary = new Dictionary(); //位图缓存表:{原影片剪辑类或实例: [帧缓存信息]} static private var _mcCacheMap:Dictionary = new Dictionary(); //影片剪辑实例缓存表:{原影片剪辑类: 影片剪辑实例} static private var _tempOffset:Point = new Point(); //临时偏移坐标记录 private var _sourceClass:Class; //原影片剪辑类(null表示构造时指定的是实例) private var _sourceMC:MovieClip; //原影片剪辑实例 private var _content:Bitmap; //内容位图(用于显示每帧的缓存位图) private var _totalFrames:int = 0; //总帧数 private var _currentFrame:int = 0; //当前帧序号 private var _isPlaying:Boolean = false; //是否正在播放 private var _labels:Array; //原影片剪辑的所有帧标签列表(不考虑场景) private var _labelMap:Dictionary; //帧标签表:{帧标签: 帧序号} private var _originSize:Point; //指定的原始大小 /** * 构造函数 * @param sourceMC 原影片剪辑类或实例 * @throws ArgumentError 若sourceMC不为影片剪辑类或实例,则抛出异常 */ public function BmpCacheMovieClip( sourceMC:Object ) { super(); if (sourceMC is MovieClip) { _sourceMC = sourceMC as MovieClip; } else if (sourceMC is Class && ClassUtils.extendsClass(sourceMC as Class, MovieClip)) { _sourceClass = sourceMC as Class; } else { throw new ArgumentError("Parameter sourceMC should be a subclass or instance of MovieClip: " + sourceMC); } init(); } /** * 总帧数 */ override public function get totalFrames():int { return _totalFrames; } /** * 当前帧序号 */ override public function get currentFrame():int { return _currentFrame; } /** * 当前是否正在播放 */ override public function get isPlaying():Boolean { return _isPlaying; } /** * 所有帧标签列表 */ override public function get currentLabels():Array { return _labels; } /** * 指定的原始大小(null表示不指定) */ public function get originSize():Point { return _originSize ? _originSize.clone() : null; } public function set originSize( val:Point ):void { _originSize = val ? new Point(Math.max(1, val.x || 0), Math.max(1, val.y || 0)) : null; } /** * 宽度 */ override public function get width():Number { return _originSize ? _originSize.x * this.scaleX : super.width; } override public function set width( val:Number ):void { if (_originSize) { this.scaleX = (val || 0) / this.originSize.x; } else { super.width = val; } } /** * 高度 */ override public function get height():Number { return _originSize ? _originSize.y * this.scaleY : super.height; } override public function set height( val:Number ):void { if (_originSize) { this.scaleY = (val || 0) / this.originSize.y; } else { super.height = val; } } /** * 初始化 */ private function init():void { if (_sourceClass) //指定类 { if (_sourceClass in _bmpCacheMap) { _sourceMC = _mcCacheMap[_sourceClass] as MovieClip; } else { _sourceMC = new _sourceClass(); _bmpCacheMap[_sourceClass] = new Vector.<FrameCacheInfo>(_sourceMC.totalFrames, true); _mcCacheMap[_sourceClass] = _sourceMC; } } else //指定实例 { if (!(_sourceMC in _bmpCacheMap)) { _bmpCacheMap[_sourceMC] = new Vector.<FrameCacheInfo>(_sourceMC.totalFrames, true); } } //初始化影片剪辑参数 _totalFrames = _sourceMC.totalFrames; _labels = _sourceMC.currentLabels; if (_labels && _labels.length > 0) { _labelMap = new Dictionary(); for each (var label:FrameLabel in _labels) { _labelMap[label.name] = label.frame; } } //创建内容,并显示第一帧 _content = new Bitmap(); _content.smoothing = true; addChild(_content); gotoFrame(1); } /** * 开始播放 */ override public function play():void { if (_isPlaying || this.totalFrames < 2) return; addEventListener(Event.ENTER_FRAME, onFrame); _isPlaying = true; } /** * 停止播放 */ override public function stop():void { if (!_isPlaying) return; removeEventListener(Event.ENTER_FRAME, onFrame); _isPlaying = false; } /** * 跳转到指定帧并播放 * @param frame 帧序号或帧标签 */ override public function gotoAndPlay( frame:Object, scene:String=null ):void { gotoFrame(frame); play(); } /** * 跳转到指定帧并停止 * @param frame 帧序号或帧标签 */ override public function gotoAndStop( frame:Object, scene:String=null ):void { gotoFrame(frame); stop(); } /** * 跳转到上一帧并停止 */ override public function prevFrame():void { gotoFrame(this.currentFrame > 1 ? this.currentFrame - 1 : this.totalFrames); stop(); } /** * 跳转到下一帧并停止 */ override public function nextFrame():void { gotoFrame(this.currentFrame < this.totalFrames ? this.currentFrame + 1 : 1); stop(); } /** * 跳转到指定帧 * @param frame 帧序号或帧标签 */ protected function gotoFrame( frame:* ):void { if (_labelMap && frame in _labelMap) { frame = _labelMap[frame]; } frame = NumberUtils.limit(int(frame), 1, this.totalFrames); if (frame == _currentFrame) return; _currentFrame = frame; //若该帧缓存位图尚未创建,则先创建 var cache:Vector.<FrameCacheInfo> = _bmpCacheMap[_sourceClass || _sourceMC]; if (!cache) return; var info:FrameCacheInfo = cache[_currentFrame - 1]; if (!info) { _sourceMC.gotoAndStop(_currentFrame); info = new FrameCacheInfo(); info.bitmap = DisplayObjectUtils.drawToBitmap(_sourceMC, 0, _tempOffset); info.offsetX = _tempOffset.x; info.offsetY = _tempOffset.y; cache[_currentFrame - 1] = info; } //刷新内容 _content.bitmapData = info.bitmap; _content.x = info.offsetX; _content.y = info.offsetY; } /** * 每帧处理 */ private function onFrame( e:Event ):void { gotoFrame(this.currentFrame < this.totalFrames ? this.currentFrame + 1 : 1); } /** * 清除位图缓存(清除后所有已存在的相关影片剪辑都将不再刷新内容,但新创建的不受影响) * @param sourceMC 原影片剪辑类或实例 */ public function clearBmpCache():void { BmpCacheMovieClip.clearBmpCache(_bmpCacheMap); } static public function clearBmpCache( sourceMC:Object ):void { var cache:Vector.<FrameCacheInfo> = _bmpCacheMap[sourceMC]; if (cache) { for each (var info:FrameCacheInfo in cache) { info.bitmap.dispose(); } delete _bmpCacheMap[sourceMC]; delete _mcCacheMap[sourceMC]; } } } } import flash.display.BitmapData; /** * 帧缓存信息 */ class FrameCacheInfo { public var bitmap:BitmapData; //该帧的缓存位图 public var offsetX:int; //偏移X坐标 public var offsetY:int; //偏移Y坐标 }