鼠标指针什么的最讨厌了啦啦啦~什么自定义的鼠标指针都是一个骗局啊老湿!听我说完鼠标自定义指针的实现原理之后你就会知道为什么我要这么说了。
就拿当前最火的农场应用来说吧,在里面我们的鼠标可以变成水壶、铲子或者手型什么的,那是怎么做的呢?不告诉你,啦啦啦~你打我呀,打我呀~哎呦!呜~妈妈……
<ignore_js_op>
不要阻止我先看结果:http://www.iamsevent.com/upload/CustomCursor.swf
咳咳,先让我们用CS工具来快速看一下素材吧,这些素材都是网上下来的,身为一名程序员的我不打算在制作素材上花太多时间,况且我也不在行折腾那些玩意儿(呸!借口!)
<ignore_js_op>
<ignore_js_op>
在库里我们看到了4个鼠标素材,分别代表着普通状态、洒水状态、挖坑状态以及除草状态的鼠标,要做这样一个动画其实不难,我们就拿一个铲土动作来看,就是分了三个图层,分别放置鼠标,铲子以及泥土的动作。在库中我们还放了用作背景的图片以及可用于点击的地块。
看完了素材我们就把fla文件编译生成swf后放到flash builder的工程目录下吧,在FB中加载素材相比对于列位道友来说已经是不动脑经就能写出来了,这里我们为了方便起见写一个资源管理工具类AssetsManager。代码如下(还是一如既往地省略一系列import语句):
AssetsManager.as
- public class AssetsManager extends EventDispatcher
- {
- private var contentAD:ApplicationDomain = null;
- public function AssetsManager(){
- }
- public function load( url:String ):void{
- var loader:Loader = new Loader();
- loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
- loader.load( new URLRequest( url ) );
- }
- public function getResource( id:String ):*{
- var definition:Class = contentAD.getDefinition( id ) as Class;
- return new definition();
- }
- private function onComplete(event:Event):void{
- contentAD = (event.currentTarget as LoaderInfo).applicationDomain;
- dispatchEvent( new Event(Event.COMPLETE) );
- }
- }
当我们调用load方法加载一个资源后,AssetsManager 会开始加载外部swf资源,加载完成后会派发COMPLETE事件,在外部监听到此事件后就可以取用swf中的库资源了。
之后我们要写一个CursorLayer类来管理鼠标外观:
- public class CursorLayer extends Sprite
- {
- private var _crtCursor : MovieClip = new MovieClip();
- public function CursorLayer()
- {
- super( );
- init( );
- }
- public function play():void{
- _crtCursor.play();
- }
- public function set crtCursor(mc : MovieClip ) : void {
- _crtCursor = mc;
- _crtCursor.stop();
- reposCursor( );
- addChild(_crtCursor);
- }
- public function get crtCursor( ) : MovieClip {
- return _crtCursor;
- }
- private function init( ) : void {
- addEventListener(Event.ENTER_FRAME, efHandler);
- }
- private function reposCursor( ) : void {
- _crtCursor.x = mouseX-10;
- _crtCursor.y = mouseY-10;
- }
- private function efHandler(event : Event) : void {
- reposCursor( );
- }
- }
在init函数中监听了enterFrame,并在事件处理函数中做了我们很熟悉的“鼠标跟随”工作。类中申明的私有变量_crtCursor 指示了当前应用的鼠标外观素材,类型为movieClip,因为此素材是在CS工具中的fla中制作的,且其具有时间轴,有动画。至于get和set函数的用法,网上一搜一大把,它们可以提供给我们的,是在对一个变量进行读写时能够做一系列的幕后工作。在这里我们crtCursor的set方法中除了赋值外还需要做额外三步:
1.由于鼠标外观素材_crtCursor内部具有动画,为了避免其在添加到舞台上后自动反复播放其内部动画,我们需要先调用stop()方法
2.定位_crtCursor到鼠标所在位置
3.添加到显示列表
当我们使用
- crtCursor = mc;
这样的赋值语句时就会自动在幕后执行上述几步。
最后来看看主应用文件:
- public function CustomCursor()
- {
- assetsManager = new AssetsManager();
- assetsManager.addEventListener( Event.COMPLETE, onAssetsComplete );
- assetsManager.load( "assets/happyFarmCursor.swf" );
- }
- private function onAssetsComplete(event:Event):void{
- initBackground();
- initCursor();
- }
- private function initBackground():void{
- //布置背景
- var bg:Bitmap = new Bitmap( assetsManager.getResource( "DefaultBg" ) as BitmapData );
- this.addChild( bg );
- //布置地块
- for(var i:int=0;i<numLands;i++){
- var land:MovieClip = assetsManager.getResource( "FarmlandS" ) as MovieClip;
- land.x = 240 + int( i / 3 ) * TILE_WIDTH / 2 - i % 3 * TILE_WIDTH / 2;
- land.y = 280 + int( i / 3 ) * TILE_HEIGHT / 2 + i % 3 * TILE_HEIGHT / 2;
- this.addChild( land );
- land.addEventListener( MouseEvent.ROLL_OVER, onRollOver );
- land.addEventListener( MouseEvent.ROLL_OUT, onRollOut );
- }
- }
- private function initCursor():void{
- cursorLayer = new CursorLayer();
- cursorLayer.crtCursor = assetsManager.getResource( "CursorArrow" ) as MovieClip;
- addChild( cursorLayer );
- }
- //鼠标移至地块上时高亮显示地块
- private function onRollOver( event:MouseEvent ):void{
- var land:MovieClip = event.currentTarget as MovieClip;
- land.filters = [new ColorMatrixFilter(
- [1,0,0,0,50, //为RGB三个颜色通道增值50,或者你想让它更亮就取比50大的值
- 0,1,0,0,50,
- 0,0,1,0,50,
- 0,0,0,1, 0 ]
- )];
- }
- //鼠标从地块上移开,取消高亮显示
- private function onRollOut( event:MouseEvent ):void{
- (event.currentTarget as MovieClip).filters = null;
- }
这段代码应该不难理解,但是要你写你能写的出来吗?注意事项2点:
1.必须监听到AssetsManager发出的COMPLETE事件后才能获取加载的swf中的资源;
2.cursorLayer必须最后addChild, 它必须处于最上层才能保证其不被背景遮挡
编译运行主应用文件,会看到自定义的光标跟着鼠标跑,但是鼠标移到地块上时地块没有高亮显示,这是为虾米呢?广告之后继续回来……
<ignore_js_op>
“我们的丑陋,海昌隐形眼镜……”
“干咱们这行的,整天在外风吹日晒的,用大便,嘿,还真对得起咱这张脸……”
咦?我广告看得好好的咋插播起了AS3节目了捏?啥?广大人民群众等着看?好吧,让我们继续讲……
刚才说到当鼠标使用了自定义鼠标外观后移到我们添加了MouseOver事件侦听的地块上不会触发事件侦听函数,即地块不会高亮显示,这是因为我们的自定义鼠标层CursorLayer它的位置始终跟随着鼠标跑,就会始终盖在鼠标的下面,如果把鼠标比作一只脚,那么我们的CursorLayer层就是鞋子,不管我们怎么点击鼠标,接收到鼠标事件的只有CursorLayer而已。所以为了让我们改造了外观的鼠标能够正常地触发与其他对象的交互,我们需要让CursorLayer不接受鼠标事件,在CursorLayer的代码中找到init()初始化函数,并在函数末尾增加两句:
- private function init( ) : void {
- addEventListener(Event.ENTER_FRAME, efHandler);
- this.mouseChildren = false;
- this.mouseEnabled = false;
- }
通过将mouseChildren 以及mouseEnabled 属性设为false可以有效地阻止CursorLayer挡住鼠标的事件。
现在鼠标跟随是做好了,不过对于带动画的元件来说可没那么简单了,在CS工具中制作的影片剪辑内部有时间轴,我们需要在点击时让元件内部的时间轴走起来,播放动画,就像在玩农场应用时你选中了铲土工具,此时不仅鼠标变成了一把铲子,而且点一下鼠标铲子还会播放铲土的动画。为了播放自定义鼠标元件的动画,我们在CursorLayer中再写一个共有函数:
- public function play():void{
- _crtCursor.play();
- }
外部可以通过这个借口来使CursorLayer内部的光标对象播放起动画。回到我们的主应用中,在initCursor函数中添加一个鼠标点击侦听:
- private function initCursor():void{
- ……
- stage.addEventListener(MouseEvent.CLICK, onClick);
- }
在事件侦听函数中我们需要做的事情格外简单,只需要调用一下CursorLayer中的play函数即可:
- private function onClick( event:MouseEvent ):void{
- cursorLayer.play();
- }
最后,为了方便测试,我们使用Mouse.hide();方法隐藏我们系统默认的光标,且把鼠标外观素材改成喷水的动作,最终主应用中的全部代码如下:
- public function CustomCursor()
- {
- assetsManager = new AssetsManager();
- assetsManager.addEventListener( Event.COMPLETE, onAssetsComplete );
- assetsManager.load( "assets/happyFarmCursor.swf" );
- }
- private function onAssetsComplete(event:Event):void{
- initBackground();
- initCursor();
- }
- private function initBackground():void{
- var bg:Bitmap = new Bitmap( assetsManager.getResource( "DefaultBg" ) as BitmapData );
- this.addChild( bg );
- for(var i:int=0;i<numLands;i++){
- var land:MovieClip = assetsManager.getResource( "FarmlandS" ) as MovieClip;
- land.x = 240 + int( i / 3 ) * TILE_WIDTH / 2 - i % 3 * TILE_WIDTH / 2;
- land.y = 280 + int( i / 3 ) * TILE_HEIGHT / 2 + i % 3 * TILE_HEIGHT / 2;
- this.addChild( land );
- land.addEventListener( MouseEvent.ROLL_OVER, onRollOver );
- land.addEventListener( MouseEvent.ROLL_OUT, onRollOut );
- }
- }
- private function initCursor():void{
- cursorLayer = new CursorLayer();
- cursorLayer.crtCursor = assetsManager.getResource( "CursorArrow" ) as MovieClip;
- Mouse.hide();
- addChild( cursorLayer );
- stage.addEventListener(MouseEvent.CLICK, onClick);
- }
- private function onRollOver( event:MouseEvent ):void{
- var land:MovieClip = event.currentTarget as MovieClip;
- land.filters = [new ColorMatrixFilter(
- [1,0,0,0,50,
- 0,1,0,0,50,
- 0,0,1,0,50,
- 0,0,0,1, 0 ]
- )];
- }
- private function onRollOut( event:MouseEvent ):void{
- (event.currentTarget as MovieClip).filters = null;
- }
- private function onClick( event:MouseEvent ):void{
- cursorLayer.play();
- }
好了,编译并运行一下看看吧,把鼠标移到地块上,地块的确亮起来了,点一下鼠标,动作动画也开始播放了,不过……这TMD的动画咋一直在反复播放呢?不行,我得让他播放一遍就停下来!打开我们制作素材的fla文件,找到库中用来作鼠标外观的元件,依次打开它们并在时间轴最后一个关键帧处插入以下脚本代码:
- gotoAndStop(1);
<ignore_js_op>
这样就能时动画播放到最后一帧时自动跳转到第一帧并停止住,嗯嗯,这样就可以满足我们的要求了……
有朋友问,若是我想让鼠标按下去后动画就一直反复播放,直到松开鼠标按键才停止咋办呢?哦,别急,我的孩子,My son,这样就不需要去fla文件里给元件时间轴上写脚本代码实现了,直接在Flash builder中先打开CursorLayer.as文件然后加一个接口函数:
- public function stop():void{
- _crtCursor.gotoAndStop(1);
- }
再在主应用文件中把MouseEvent.Cliclk事件侦听拆分为两个:
- stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
- stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
- private function onDown(event:MouseEvent):void{
- cursorLayer.play();
- }
- private function onUp(event:MouseEvent):void{
- cursorLayer.stop();
- }
仔细想想就会发现其中之奥秘了我的孩子……
最后终结一下吧,所谓的自定义鼠标外观,其实就是为主应用最上层添加一个实现了“鼠标跟随”的显示对象,并使其不响应鼠标事件而已,若要播放点击动作动画,可以随时使用play()方法来做到。所以当初的我们还懵懂地以为真的是默认的系统鼠标被改变了,殊不知这一切都只是个骗局,真是讨厌死了啦啦啦~~
双手双脚奉上我最不值钱的源码,其中的fla文件请使用flash CS4以上版本打开: <ignore_js_op>src.rar (84.33 KB, 下载次数: 576)
定义系统鼠标指针获取更佳的鼠标指针体验
在flash player10.2中提供了新的一系列用于定义鼠标指针的API,可以让我们不使用上述这种“元件跟随”方式就能实现自定义鼠标指针,而且移动起来更佳流畅,基本与系统指针效率相当。我们称之为“硬鼠标”,即使用硬件驱动的鼠标,不同于之前提到的“软鼠标”(软件驱动)。和“硬鼠标”比起来,软鼠标就是一个不折不扣的骗子!
英文好的童鞋可以直接阅读官方原文获知新API使用方式:http://blogs.adobe.com/flashplatform/2010/12/working-with-native-custom-cursors-in-flash.html
在这个文章里面不仅有代码介绍还有实例demo可供把玩。
英文不好的同学可以看看这里有一个中文的文章:http://www.freeliver.net/?p=167
我最后做个总结,你可以使用MouseCursorData这个新类来指定自定义的指针外观(传入一个BitmapData类型的Vector向量,若此向量内只有一个BitmapData对象,则为静态指针,传入多个则为动画指针)