sevenson.com.au logo

Glowing Text Effect

The Lynx Fast Life campaign I worked on at Visual Jazz had a design style that was just screaming for more than just the usual blurs and fades you usually use on a flash website. Something that looked more like After Effects then flash.

After a bit of messing around with prototypes I finally came up with something I thought looked pretty cool and it ended up getting used throughout the entire site.

The effect was created by taking in a BitmapData of the text I wanted to reveal and then building around that.

First of all, I created two larger versions of the original BitmapData with extra transparency around them. Then I added a GlowFilter to thicken them up a bit before applying a two different levels of BlurFilter to the BitmapDatas.

Next, I lined these up with the original BitmapData and used them as masks for a gradient filled shape I generated (everything was set to cacheAsBitmap so I could do alpha masking). I animated the gradient through the blurred masks with a slight delay between them – this gave an effect of a tapering off type of brightness.


After that I created a gradient filled mask for the original BitmapData which I used to reveal the base image behind the two blurred glows. I had to play around with the timings a bit, but eventually came up with some values I thought worked pretty well.

Finally, just for a touch of class, I added in another small gradient to act as a shine to the text. It used an instance of the the original BitmapData as a mask and simple past over the length of the text a second or so behind the reveal.

You need flash and javascript to view this content.

And that was that – my pretty cool glowing text reveal effect thingy.

For the transition out of the text I did something a bit different. I wanted the text to decay away, but I didn’t really have a lot of time to get the effect right. What I came up with was a bit of a Frankenstein approach that generated an interesting result.

I started by creating a bit of a mask/guide in an off-stage Sprite. It consisted of a BitmapData generated from Perlin Noise as a base, and a gradient-filled shape that was big enough to cover the perlin noise base.

I then set up another BitmapData as a mask for the original text.

Here comes the fun part. I animated the gradient mask in my guide sprite to slowly slide across the perlin noise. As I was doing this, I did a BitmapData.draw() of the mask/guide into the BitmapData mask of the text, using BlendMode.MULTIPLY as the blend mode. This caused the the areas of the perlin noise that were revealed by the gradient to build up in the mask, hence slowly hiding chunks of the original text.

I hope that makes sense – I am pretty tired as I write this.

And there you have it – the glowing text effect with an added perlin noise dissolve thrown in for free. Hope you enjoyed it 🙂

Here is the code – it’s not pretty, but should show you how it was all done:

/**
 * GlowingText
 * 
 * @author Andrew Sevenson
 */

package com.sevenson.display.glowtext
{
	import com.greensock.easing.Linear;
	import com.greensock.easing.Quad;
	import com.greensock.TweenMax;
	import com.quasimondo.geom.ColorMatrix;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BitmapDataChannel;
	import flash.display.BlendMode;
	import flash.display.GradientType;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.filters.BlurFilter;
	import flash.filters.ColorMatrixFilter;
	import flash.filters.GlowFilter;
	import flash.geom.Matrix;
	import flash.geom.Point;
	
	public class GlowingText extends Sprite
	{
		private static const ZERO_PT:Point = new Point();
		
		private var _origBD:BitmapData, _origBMP:Bitmap
		
		private var _shineMask:Bitmap;
		private var _shineShape:Shape;
		
		private var _blurBD1:BitmapData, _blurBMP1:Bitmap;
		private var _blurBD2:BitmapData, _blurBMP2:Bitmap;
		
		private var _grad1:Shape, _grad2:Shape;
		private var _gradOffset:Number = -30;
		private var _commonGradLength:Number;
		
		private var _baseMask:Shape = new Shape();
		
		private var _noiseBD:BitmapData, _noiseBMP:Bitmap;
		private var _noiseGrad:Shape;
		private var _noiseContainer:Sprite;
		private var _hideBD:BitmapData, _hideBMP:Bitmap;
		private var _intermediateBD:BitmapData;
		
		
		/**
		 * Creates a new instance of the GlowingText class.
		 */
		public function GlowingText (bitmapData:BitmapData) : void
		{
			_origBD = bitmapData;
			_origBMP = new Bitmap(_origBD);
			addChild(_origBMP);
			
			
			
			var blurAmount:Number = 4;
			var glowAmount:Number = 16;
			var extra:Number = blurAmount + glowAmount
			
			_blurBD1 = new BitmapData(_origBD.width + extra, _origBD.height + extra, true, 0x00);
			_blurBD1.applyFilter(_origBD, _origBD.rect, new Point(extra * 0.5, extra * 0.5), new BlurFilter(blurAmount, blurAmount, 3) );			
			_blurBD1.applyFilter(_blurBD1, _blurBD1.rect, ZERO_PT, new GlowFilter(0xff0000, 1, glowAmount, glowAmount, 2, 3) );
			
			_blurBMP1 = new Bitmap(_blurBD1);
			_blurBMP1.x = (_origBMP.width - _blurBMP1.width) * 0.5;
			_blurBMP1.y = (_origBMP.width - _blurBMP1.width) * 0.5;
			//addChild(_blurBMP1);
			
			
			
			blurAmount = 10;
			glowAmount = 35;
			extra = blurAmount + glowAmount
			
			_blurBD2 = new BitmapData(_origBD.width + extra, _origBD.height + extra, true, 0x00);
			_blurBD2.applyFilter(_origBD, _origBD.rect, new Point((_blurBD2.width - _origBD.width)*0.5, (_blurBD2.height - _origBD.height)*0.5), new BlurFilter(blurAmount, blurAmount, 3) );			
			_blurBD2.applyFilter(_blurBD2, _blurBD2.rect, ZERO_PT, new GlowFilter(0xff0000, 1, glowAmount, glowAmount, 2, 3) );
			
			_blurBMP2 = new Bitmap(_blurBD2);
			_blurBMP2.x = (_origBMP.width - _blurBD2.width) * 0.5;
			_blurBMP2.y = (_origBMP.width - _blurBD2.width) * 0.5;
			//addChild(_blurBMP2);			
			
			
			_grad1 = generateRevealGradient(400, _blurBD2.height);
			_grad1.y = _blurBMP1.y;
			//addChild(_grad1);			
			
			_grad2 = generateRevealGradient(400, _blurBD2.height);
			_grad2.y = _blurBMP2.y;
			//addChild(_grad2);				
			
			
			
			_grad1.cacheAsBitmap = _grad2.cacheAsBitmap = _blurBMP1.cacheAsBitmap = _blurBMP2.cacheAsBitmap = true;
			_grad1.mask = _blurBMP1;
			_grad2.mask = _blurBMP2;
			
			
			_commonGradLength = _grad1.width * 0.5;
			
			var m:Matrix;
			m = new Matrix();
			m.createGradientBox( _commonGradLength, _origBD.height, 0, _origBD.width);
			
			_baseMask = new Shape();
			_baseMask.graphics.beginGradientFill(GradientType.LINEAR, [0x00, 0x00], [1, 0], [0, 255], m);
			_baseMask.graphics.drawRect(0, 0, _origBD.width + _commonGradLength, _origBD.height);
			//addChild(_baseMask);
			_baseMask.cacheAsBitmap = _origBMP.cacheAsBitmap = true;
			_origBMP.mask = _baseMask;
			
			
			
			_shineMask = new Bitmap(_origBD);
			//addChild(_shineMask);
			_shineShape = new Shape();
			
			var g:Graphics = _shineShape.graphics;
			m = new Matrix();
			m.createGradientBox(100, _origBD.height, 0 * (Math.PI/180));
			g.beginGradientFill( GradientType.LINEAR, [0xfcf47d, 0xfcf47d, 0xfcf47d], [0, 1, 0], [0, 128, 255], m );
			g.drawRect(0, 0, 100, _origBD.height);			
			//addChild(_shineShape);
			
			
			_shineShape.cacheAsBitmap = _shineMask.cacheAsBitmap = true;
			_shineShape.mask = _shineMask;
			
			
			
			
			_noiseContainer = new Sprite();
			//_noiseContainer.y = _origBD.height*3;
			//addChild(_noiseContainer);
			
			_noiseBD = _origBD.clone();
			_noiseBD.perlinNoise(4, 5, 3, 8, false, true, 7, true);
			//noiseBD.copyChannel(noiseBD, noiseBD.rect, ZERO_PT, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
			
			var cm:ColorMatrix = new ColorMatrix();
			cm.adjustBrightness( -2);
			_noiseBD.applyFilter(_noiseBD, _noiseBD.rect, ZERO_PT, cm.filter);
			_noiseBMP = new Bitmap(_noiseBD);
			_noiseContainer.addChild(_noiseBMP);
			
			_noiseGrad = new Shape();
			g = _noiseGrad.graphics;
			m = new Matrix();
			m.createGradientBox( _commonGradLength, _origBD.height);
			g.beginGradientFill(GradientType.LINEAR, [0xffffff, 0xffffff], [0.6, 1], [0, 255], m);
			g.drawRect(0, 0, _origBD.width + _commonGradLength, _origBD.height);
			_noiseContainer.addChild(_noiseGrad);
			
			
			_intermediateBD = new BitmapData(_origBD.width, _origBD.height, false, 0xffffffff); 
			
			_hideBD = _origBD.clone(); 
			_hideBMP = new Bitmap(_hideBD);
			//addChild(_hideBMP);
			_hideBMP.cacheAsBitmap = true;
			
		
		}
		

		
		
		// PUBLIC FUNCTIONS
		// ------------------------------------------------------------------------------------------
		/**
		 * A clean up routine that destroys all external references to prepare the class for garbage collection.
		 */
		public function destroy () : void
		{
			if (parent) parent.removeChild(this);
			
			TweenMax.killChildTweensOf(this);
			TweenMax.killTweensOf(_noiseGrad);
			
			_blurBD1.dispose();
			_blurBD2.dispose();
			
			_intermediateBD.dispose();
			_noiseBD.dispose();
			_hideBD.dispose();
			
		}
		
		
	
		public function transitionOut(time:Number = 3, delay:Number=0, callback:Function = null):void 
		{
			transitionInComplete()
			
			_noiseGrad.x = -_commonGradLength;
			_hideBD.fillRect(_hideBD.rect, 0xffffffff);
			_intermediateBD.fillRect(_hideBD.rect, 0xffffffff);
			
			addChild(_hideBMP)
			_origBMP.mask = _hideBMP;
			
			TweenMax.to(_noiseGrad, time, { delay:delay, x:_noiseBD.width, ease:Quad.easeOut, onUpdate:drawNoiseToMask, onComplete:transitionOutComplete, onCompleteParams:[callback]} );
			
			drawNoiseToMask();
		}
		
		public function transitionIn(time:Number = 5, delay:Number=0, callback:Function=null):void
		{
			
			if (_hideBMP.parent) _hideBMP.parent.removeChild(_hideBMP);
			
			// put in all the elements
			addChild(_blurBMP1)
			addChild(_blurBMP2);
			addChild(_grad1);
			addChild(_grad2);
			addChild(_baseMask)
			addChild(_shineMask);
			addChild(_shineShape);
			_origBMP.mask = _baseMask;
			
			
			// position for the start
			_grad2.x = _blurBMP2.x - _grad2.width - 60;
			_grad1.x = _grad2.x + _gradOffset;
			_baseMask.x = (_grad1.x - _baseMask.width) + (_grad1.width*0.5);
			
			_shineShape.x = -_shineShape.width;
			

			var ease:Function = Quad.easeOut;
			
			TweenMax.to(_grad2, time, { delay:delay, x: _blurBMP2.x + _blurBMP2.width - _gradOffset, ease:ease } );
			TweenMax.to(_grad1, time, { delay:delay, x: _blurBMP2.x + _blurBMP2.width, ease:ease } );
			TweenMax.to(_baseMask, time, { delay:delay, x: 0, ease:ease } );
			
			
			TweenMax.to(_shineShape, time*0.6, { delay:delay + time*0.35, x: _origBD.width, ease:ease, onComplete:transitionInComplete, onCompleteParams:[callback] } );
			
			
		}
		
		
		
		
		// PRIVATE FUNCTIONS
		// ------------------------------------------------------------------------------------------
		
		
		
		private function transitionOutComplete(callback:Function = null ):void 
		{
			if (callback != null) callback();
		}
		
		
		private function drawNoiseToMask():void 
		{
			_intermediateBD.draw(_noiseContainer, null, null, BlendMode.MULTIPLY, null, true);
			_hideBD.copyChannel(_intermediateBD, _hideBD.rect, ZERO_PT, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
		}		
		
		
		private function transitionInComplete(callback:Function=null):void
		{
			// clean up a little
			if(_blurBMP1.parent) removeChild(_blurBMP1)
			if(_blurBMP2.parent) removeChild(_blurBMP2);
			if(_grad1.parent) removeChild(_grad1);
			if(_grad2.parent) removeChild(_grad2);
			if(_baseMask.parent) removeChild(_baseMask)
			if(_shineMask.parent) removeChild(_shineMask);
			if (_shineShape.parent) removeChild(_shineShape);
			
			_origBMP.mask = null;
			
			if (callback != null) callback();
		}		
		
		private function generateRevealGradient(w:Number, h:Number):Shape
		{
			var g:Graphics;
			var m:Matrix;
			
			var s:Shape = new Shape();
			g = s.graphics;
			m = new Matrix();
			m.createGradientBox(w, h, 0);
			g.beginGradientFill( GradientType.LINEAR, [0xE3BE59, 0xfcf47d, 0xfcf47d, 0xfcf47d, 0xE3BE59], [0, 1, 1, 1, 0], [0, 190, 204, 216, 255], m );
			
			g.drawRect(0, 0, w, h);
			
			return s;
		}
				
		
		
		// EVENT HANDLERS
		// ------------------------------------------------------------------------------------------
		
		
		// GETTERS & SETTERS
		// ------------------------------------------------------------------------------------------
		
		public function get bitmapData():BitmapData { return _origBD; }
		
	}
}

You will need to download a couple of support classes – namely TweenMax from Greensock, and the ColorMatrix class from Quasimondo


Leave a Reply

Your email address will not be published. Required fields are marked *

Name *

This site uses Akismet to reduce spam. Learn how your comment data is processed.


sevenson.com.au