After taking another look at this post, I came up with a way of making multiple windows that will all blur the same image. I have to admit, though, I’m not particularly fond nor proud of the way I did it. The trouble is this: a masked display object can only have a single mask – so all items which will be used as a mask must be added to a single encompassing parent. But you would also like each mask item to be draggable along with the window it “rides behind”. Therefore, ideally, you’d like each mask item to be a child of the window instance (as I did it in the previous post). But there lies the rub. A display object can have only a single parent, hence you can’t add the child to both the mask and the window.
The solution I came up with was this: add the masking item to the mask instance and move the masking item with the window using an ENTER_FRAME event (rather than a parent-child relationship). If anyone has a more elegant solution, please post a comment. I’m probably overlooking something simple, but brilliant…
The MWindowBlur (Multiple Window Blur) class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
package com.onebyonedesign.extras { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.DisplayObjectContainer; import flash.display.Sprite; import flash.events.Event; import flash.filters.BlurFilter; import flash.geom.Point; import flash.utils.Dictionary; /** * Allows creation of multiple windows that blur the same background image * @author Devon O. */ public class MWindowBlur { private var _background:DisplayObjectContainer; private var _blurData:BitmapData; private var _blurImage:Bitmap; private var _blurAmount:int; private var _blur:BlurFilter; private var _mask:Sprite = new Sprite(); private var _point:Point = new Point(); private var _windows:Array = []; private var _maskDictionary:Dictionary = new Dictionary(true); public function MWindowBlur(background:DisplayObjectContainer, blurAmount:int = 8) { _background = background; _blurAmount = (blurAmount >= 0 && blurAmount <= 255) ? blurAmount : 8; _blur = new BlurFilter(_blurAmount, _blurAmount, 3); createBlur(); } public function addWindow(window:DisplayObjectContainer):Boolean { if (_windows.indexOf(window) > -1) return false; var WindowClass:Class = Object(window).constructor; var m:DisplayObjectContainer = new WindowClass(); m.transform = window.transform; m.filters = window.filters; if (window.scale9Grid) { m.scale9Grid = window.scale9Grid; } _maskDictionary[window] = m; _windows.push(window); if (!_mask.willTrigger(Event.ENTER_FRAME)) _mask.addEventListener(Event.ENTER_FRAME, moveMask); _mask.addChild(m); if (!_background.contains(_mask)) _background.addChild(_mask); return true; } public function removeWindow(window:DisplayObjectContainer = null):Boolean { if (!_windows.length) return false; if (!window) return removeWindow(_windows[_windows.length - 1]); var index:int = _windows.indexOf(window); if (index < 0) return false; _windows.splice(index, 1); _mask.removeChild(_maskDictionary[window]); if (!_windows.length) _mask.removeEventListener(Event.ENTER_FRAME, moveMask); return true; } public function kill():void { if (_mask.willTrigger(Event.ENTER_FRAME)) _mask.removeEventListener(Event.ENTER_FRAME, moveMask); if (_background.contains(_mask)) _background.removeChild(_mask); if (_background.contains(_blurImage)) _background.removeChild(_blurImage); _blurData.dispose(); _blurImage = null; _blurData = null; _maskDictionary = null; _background = null; _blur = null; } public function get blurAmount():int { return _blurAmount; } public function set blurAmount(value:int):void { _blurAmount = value; createBlur(); } private function createBlur():void { if (_blurData) _blurData.dispose(); _blur.blurX = _blur.blurY = _blurAmount; _blurData = new BitmapData(_background.width, _background.height, false); _blurData.draw(_background); _blurData.applyFilter(_blurData, _blurData.rect, _point, _blur); _blurImage = new Bitmap(_blurData); _blurImage.mask = _mask; _background.addChild(_blurImage); } private function moveMask(event:Event):void { var i:int = _windows.length; while (i--) { var w:DisplayObjectContainer = _windows[i]; var m:DisplayObjectContainer = _maskDictionary[w]; m.transform = w.transform; } } } } |
The Flash document class for a test ride:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
package { import com.onebyonedesign.extras.MWindowBlur; import com.onebyonedesign.ui.OBO_ValueSlider; import com.onebyonedesign.ui.events.ValueSliderEvent; import fl.controls.Button; import flash.display.MovieClip; import flash.display.Sprite; import flash.events.MouseEvent; import flash.filters.DropShadowFilter; import flash.text.AntiAliasType; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; /** * Demonstrates MWindowBlur (for multiple windows) Class * @author Devon O. */ public class Main extends Sprite { // GraphicWindow is MovieClip in .fla library private var _activeWindow:GraphicWindow; // Background is MovieClip in .fla library private var _bg:Background; private var _windowBlur:MWindowBlur; private var _shadow:DropShadowFilter = new DropShadowFilter(2, 90, 0x000000, 1, 2, 2, 1, 3); private var _windowArray:Array = []; public function Main():void { // Background is MovieClip in .fla library _bg = new Background(); addChild(_bg); init(); } private function init():void { initBlur(); initControlPanel(); addWindow(); } private function initBlur():void { _windowBlur = new MWindowBlur(_bg); } private function initControlPanel():void { var cp:Sprite = new Sprite(); cp.graphics.beginFill(0xEAEAEA); cp.graphics.drawRoundRect(0, 0, 222, 70, 10, 10); cp.graphics.endFill(); cp.filters = [_shadow]; cp.x = 10; cp.y = stage.stageHeight - cp.height - 10; var tf:TextField = new TextField(); var fmt:TextFormat = new TextFormat("_sans", 11); tf.defaultTextFormat = fmt; tf.autoSize = TextFieldAutoSize.LEFT; tf.selectable = false; tf.mouseEnabled = false; tf.antiAliasType = AntiAliasType.ADVANCED; tf.text = "Blur amount:"; tf.x = 5; tf.y = 5; cp.addChild(tf); var blurSlider:OBO_ValueSlider = new OBO_ValueSlider(139, 0, 32, _windowBlur.blurAmount); blurSlider.x = tf.x + tf.textWidth + 8; blurSlider.y = 13; blurSlider.addEventListener(ValueSliderEvent.DRAG, blurChangeHandler); cp.addChild(blurSlider); var b1:Button = new Button(); b1.label = "Add Window"; b1.x = 5; b1.y = 40; b1.addEventListener(MouseEvent.CLICK, addWindow); cp.addChild(b1); var b2:Button = new Button(); b2.label = "Remove Window"; b2.x = b1.x + b1.width + 10; b2.y = 40; b2.addEventListener(MouseEvent.CLICK, removeWindow); cp.addChild(b2); addChild(cp); } private function addWindow(event:MouseEvent = null):void { // GraphicWindow is MovieClip in library var window:GraphicWindow = new GraphicWindow(); window.width = randRange(50, 300); window.height = randRange(50, 200); window.x = randRange(0, 500 - window.width); window.y = randRange(0, 500 - window.height); window.filters = [_shadow]; _windowArray.push(window); addChild(window); _windowBlur.addWindow(window); window.addEventListener(MouseEvent.MOUSE_DOWN, pressHandler); } private function removeWindow(event:MouseEvent):void { if (_windowArray.length) { _windowBlur.removeWindow(); removeChild(_windowArray.pop()); } } private function blurChangeHandler(event:ValueSliderEvent):void { _windowBlur.blurAmount = int(event.value); } private function pressHandler(event:MouseEvent):void { _activeWindow = event.currentTarget as GraphicWindow; addChild(_activeWindow); stage.addEventListener(MouseEvent.MOUSE_UP, releaseHandler); _activeWindow.startDrag(false); } private function releaseHandler(event:MouseEvent):void { stage.removeEventListener(MouseEvent.MOUSE_UP, releaseHandler); _activeWindow.stopDrag(); } private function randRange(min:Number, max:Number):Number { return Math.floor(Math.random() * (max - min + 1)) + min; } } } |
And a quick example:
Very nice. A couple suggestions:
1) When two windows are layered, it would be nice if the front window blurred the one behind it.
2) Presently, the blur amount seems to be a global value. Can it be set for each window individually?
Awesome job.
I appreciate you taking the time to create this example. I am trying to build this, but, as a newbie to AS, I am having some trouble. I can’t seem to resolve the Background or GraphicWindow data type. Any suggestions?
Hey Steve,
Both items are MovieClips inside the .fla library. Once you have an object in the library, you can right click it and pick “linkage” from the context menu. Check the “Export for actionscript” box, leave the other boxes alone and in the Class textbox just put “Background” (for the image you want to be blurred) or “GraphicWindow” (for the window you want to drag around). Hope that helps out..
Devon,
Could you please post a .ZIP with the ActiveBlur class, FLA, and its corresponding document class?
Cheers!
Well, there’s really no more than what’s posted above, but since so many are asking, you can get the .fla that created the above .swf here. You’ll also need the ui package mentioned in this post to create the value slider control above – but the package isn’t necessary for general usage of the MWindowBlur class.
Hi, I’ve put together a really simple timeline example to try and get this working (before trying it on a more complicated project), but it doesn’t seem to be working… can you see any reason why?
////////////////////////////////////////////////////////////////////////////////
import com.onebyonedesign.extras.*;
var bg:Sprite = new Sprite();
var matr:Matrix = new Matrix();
matr.createGradientBox(200, 50, 20, -50, 200);
bg.graphics.beginGradientFill(“linear”, [0xff0000, 0x00ff00], [1,1], [150,255], matr);
bg.graphics.drawRoundRect(100,20,200,250,20);
bg.graphics.endFill();
addChild(bg);
var blur:MWindowBlur=new MWindowBlur(bg,100);
var box:Sprite = new Sprite();
box.graphics.beginFill(0x0000ff, 0.5);
box.graphics.drawRect(0,0,100,100);
box.graphics.endFill();
addChild(box);
box.addEventListener(MouseEvent.MOUSE_DOWN, drag);
blur.addWindow(box);
function drag(e:Event) {
box.removeEventListener(MouseEvent.MOUSE_DOWN, drag);
box.addEventListener(MouseEvent.MOUSE_UP, drop);
box.startDrag();
}//drag
function drop(e:Event) {
box.addEventListener(MouseEvent.MOUSE_DOWN, drag);
box.removeEventListener(MouseEvent.MOUSE_UP, drop);
box.stopDrag();
}//drag
Hey Chris. There’s a couple things causing the problem. For some reason, the blur windows cannot be created programmatically – they have to be exported from the library or on the stage. So, if you make your box a class in the library, that will take care of the biggest problem. The other thing is that the background object should be aligned to the origin (0,0). Do those two things and it should work fine.
Hey Devon’! This is awesome, but I don’t understand how I can use this, in my document? Should I’ve two movieclips, one background and one window-route in the .fla project? And how do I use the actions you have posted? Should I paste it in a normal keyframe in “actions” (by pressing F9)?
Would you please make a short video how you do this? :)
I need same thing to be done in AS3
Pankaj Kumar really nice comment! Maybe you need someone to read this post for you. =P