Sempal
|
Последняя активность: 09.04.2017 00:02
- Списки
Публичные сообщения
Отображение с 1 по 1 из 1 публичных сообщений
-
02.04.2017 17:00caseyryanДержи
/** * Copyright (c) 2008 Moses Gunesch * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.MovieClip; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; /** * Like magic, transparent parts of a PNG in this MovieClip are ignored during mouse interactions. * * <p>No special instructions are needed to use this class if the clip's artwork * does not change during play, simply use this class in place of MovieClip. If the * MovieClip animates or changes, you can call drawBitmapHitArea() at any time to * refresh the hit area.</p> * * @author Moses Gunesch * @version 1.0 */ public class InteractivePNG extends MovieClip { // -== Public Properties ==- /** * Whether this MovieClip is using InteractivePNG functionality. * * <p>Functionality is enabled by default from instantiation. Only returns * false if disableInteractivePNG() has been called. (Note that setting * <code>hitArea</code> or <code>mouseEnabled</code> may result in a disable * in certain cases, see documentation in the class file for more information * on this topic.)</p> * * @return False if disableInteractivePNG() has been called. * * @see #disableInteractivePNG * @see #enableInteractivePNG */ public function get interactivePngActive() : Boolean { return _interactivePngActive; } /** * Set to 0 to detect hit on any pixel that is not completely transparent, or 255 * detect only completely opaque pixels. * * @default 128 * @return An alpha threshold between 0 and 255. */ public function get alphaTolerance() : uint { return _threshold; } public function set alphaTolerance(value : uint) : void { _threshold = Math.min(255, value); } // Excluded from documentation for simplicity, a note is provided under disableInteractivePNG. /** * @private * Using a <code>hitArea</code> disables the functionality of this class, which cannot * be reenabled until the <code>hitArea</code> is removed. * * @see #interactivePngActive * @see #enableInteractivePNG * @see #disableInteractivePNG */ override public function set hitArea(value : Sprite) : void { if (value!=null && super.hitArea==null) { disableInteractivePNG(); } else if (value==null && super.hitArea!=null) { enableInteractivePNG(); } super.hitArea = value; } // Excluded from documentation for simplicity, a note is provided under disableInteractivePNG. /** * @private * This class uses <code>mouseEnabled</code> actively, so avoid setting it if possible; * setting it will disable InteractivePNG functionality if the mouse is within clip bounds. * If this happens, you must then call enableInteractivePNG() to restart functionality. * * @see #interactivePngActive * @see #enableInteractivePNG * @see #disableInteractivePNG */ override public function set mouseEnabled(enabled : Boolean) : void { if (isNaN(_buttonModeCache)==false) { // indicates that mouse has entered clip bounds. disableInteractivePNG(); } super.mouseEnabled = enabled; } // -== Private Properties ==- /** * @private */ protected var _threshold : uint = 128; /** * @private */ protected var _transparentMode : Boolean = false; /** * @private */ protected var _interactivePngActive : Boolean = false; /** * @private */ protected var _bitmapHit : Boolean = false; /** * @private */ protected var _basePoint : Point; /** * @private */ protected var _mousePoint : Point; /** * @private */ protected var _bitmapForHitDetection : Bitmap; /** * @private */ protected var _buttonModeCache : Number = NaN; // -== Public Methods ==- /** * InteractivePNG functionality is active from instantiation, but can * be turned off at any time by calling disableInteractivePNG(). */ public function InteractivePNG():void { super(); _basePoint = new Point(); _mousePoint = new Point(); enableInteractivePNG(); } /** * A hit-detection bitmap is generated on the first mouse interaction with the clip, * but you may need to call this method or use it as an ENTER_FRAME handler to refresh * the hit area if the clip's shape changes during play. * * @param event Provided so this method can be used as an event handler with no extra work. */ public function drawBitmapHitArea(event:Event=null) : void { var isRedraw:Boolean = (_bitmapForHitDetection!=null); if (isRedraw) { try { removeChild(_bitmapForHitDetection); }catch(e:Error) { } } var bounds:Rectangle = getBounds(this); var left:Number = bounds.left; var top:Number = bounds.top; var b:BitmapData = new BitmapData(bounds.width, bounds.height, true, 0); _bitmapForHitDetection = new Bitmap(b); _bitmapForHitDetection.name = "interactivePngHitMap"; // (So that it is not a mystery if the displaylist is being inspected!) _bitmapForHitDetection.visible = false; var mx:Matrix = new Matrix(); mx.translate(-left, -top); b.draw(this, mx); addChildAt(_bitmapForHitDetection, 0); _bitmapForHitDetection.x = left; _bitmapForHitDetection.y = top; } /** * Turns off the functionality of this class. Note that setting * <code>hitArea</code> or <code>mouseEnabled</code> may result in * a disable in certain cases (see documentation in the class file * for more information on this topic). * * @see #interactivePngActive * @see #enableInteractivePNG */ public function disableInteractivePNG(): void { deactivateMouseTrap(); removeEventListener(Event.ENTER_FRAME, trackMouseWhileInBounds); try { removeChild(_bitmapForHitDetection); } catch (e:Error) { } if (_bitmapForHitDetection) { _bitmapForHitDetection.bitmapData.dispose(); _bitmapForHitDetection.bitmapData = null; _bitmapForHitDetection = null; } super.mouseEnabled = true; _transparentMode = false; setButtonModeCache(true); _bitmapHit = false; _interactivePngActive = false; } /** * Restores functionality of this class if it was previously disabled. * This method will have no effect if <code>hitArea</code> has been set. * * @see #interactivePngActive * @see #disableInteractivePNG */ public function enableInteractivePNG(): void { disableInteractivePNG(); if (hitArea!=null) return; activateMouseTrap(); _interactivePngActive = true; } // -== Private Methods ==- /** * @private * Listens for hit to edges of this MovieClip, which then turns on bitmap hit tracking. */ protected function activateMouseTrap() : void { addEventListener(MouseEvent.ROLL_OVER, captureMouseEvent, false, 10000, true); //useCapture=true, priority=high, weakRef=true addEventListener(MouseEvent.MOUSE_OVER, captureMouseEvent, false, 10000, true); addEventListener(MouseEvent.ROLL_OUT, captureMouseEvent, false, 10000, true); addEventListener(MouseEvent.MOUSE_OUT, captureMouseEvent, false, 10000, true); addEventListener(MouseEvent.MOUSE_MOVE, captureMouseEvent, false, 10000, true); } /** * @private * Turns off listening for mouse events on this MovieClip. */ protected function deactivateMouseTrap() : void { removeEventListener(MouseEvent.ROLL_OVER, captureMouseEvent); removeEventListener(MouseEvent.MOUSE_OVER, captureMouseEvent); removeEventListener(MouseEvent.ROLL_OUT, captureMouseEvent); removeEventListener(MouseEvent.MOUSE_OUT, captureMouseEvent); removeEventListener(MouseEvent.MOUSE_MOVE, captureMouseEvent); } /** * @private * Captures and suppresses MouseEvents as the mouse enters this MovieClip's bounds. * Mouse is then tracked onEnterFrame by trackMouseWhileInBounds() until it leaves * clip bounds again. If the mouse hits a pixel this method is unsubscribed until * it falls off the bitmap again, otherwise it is left on to suppress all events. * * @param event Any event subscribed in activateBitmapHitDetection(). */ protected function captureMouseEvent(event : Event) : void { if (!_transparentMode) { if (event.type==MouseEvent.MOUSE_OVER || event.type==MouseEvent.ROLL_OVER) { // The buttonMode state is cached then disabled to avoid a cursor flicker // at the movieclip bounds. Reenabled when bitmap is hit. setButtonModeCache(); _transparentMode = true; super.mouseEnabled = false; addEventListener(Event.ENTER_FRAME, trackMouseWhileInBounds, false, 10000, true); // activates bitmap hit & exit tracking trackMouseWhileInBounds(); // important: Immediate response, and sets _bitmapHit to correct state for event suppression. } } if (!_bitmapHit) event.stopImmediatePropagation(); } /** * @private * Actively track the mouse for bitmap hit or exit of MovieClip bounds. * @param event This method is called on ENTER_FRAME when mouse is in bounds and mouseEnabled is turned off. */ protected function trackMouseWhileInBounds(event:Event=null):void { if (bitmapHitTest() != _bitmapHit) { _bitmapHit = !_bitmapHit; // Mouse is now on a nonclear pixel based on alphaTolerance. Reenable mouse events. if (_bitmapHit) { deactivateMouseTrap(); setButtonModeCache(true, true); _transparentMode = false; super.mouseEnabled = true; // This will trigger rollOver & mouseOver events } // Mouse is now on a clear pixel based on alphaTolerance. Disable mouse events but . else if (!_bitmapHit) { _transparentMode = true; super.mouseEnabled = false; // This will trigger rollOut & mouseOut events } } // When mouse exits this MovieClip's bounds, end tracking & restore all. var localMouse:Point = _bitmapForHitDetection.localToGlobal(_mousePoint); if (hitTestPoint( localMouse.x, localMouse.y)==false) { removeEventListener(Event.ENTER_FRAME, trackMouseWhileInBounds); _transparentMode = false; setButtonModeCache(true); super.mouseEnabled = true; activateMouseTrap(); } } /** * @private * @return Whether the mouse touches a pixel with an alpha value greater than the * <code>alphaTolerance</code> setting. */ protected function bitmapHitTest():Boolean { if (_bitmapForHitDetection==null) drawBitmapHitArea(); _mousePoint.x = _bitmapForHitDetection.mouseX; _mousePoint.y = _bitmapForHitDetection.mouseY; return _bitmapForHitDetection.bitmapData.hitTest(_basePoint, _threshold, _mousePoint); } /** * @private * Helper for avoiding cursor flicker at clip bounds. * @param restore Default false caches buttonMode state, true restores state from cache. * @param retain If flagged do not clear the cache during restore. */ protected function setButtonModeCache(restore:Boolean=false, retain:Boolean=false) : void { if (restore) { if (_buttonModeCache==1) buttonMode = true; if (!retain) _buttonModeCache = NaN; return; } _buttonModeCache = (buttonMode==true ? 1 : 0); buttonMode = false; } } }