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坐标
}