Source code for custom TextFields with cool effects

by Josh Tynjala

Recently, I decided to throw an interesting little Flash experiment up on one of my websites. Nothing fancy or special. A simple SWF displays the recent postings from the various blogs to which I contribute. Part of the project’s goal was to display the posts in an interesting way, so I created a couple of custom TextField subclasses that manipulate the text as they display it.

Effect #1: TypingTextField

This one is obvious. When the text property changes, the TextField clears and adds one letter at a time to simulate typing. A simple blinking cursor may be displayed to give it a command line sort of appearance.

Please install Flash Player.

Source Code

Effect #2: DecryptingTextField

Hollywood doesn’t understand computers. That said, it’s always cool to see the combination for a safe with an electronic lock get cracked character by character. The DecryptingTextField displays a set of random letters that cover the full length of the text property. One by one, the proper characters are displayed.

Please install Flash Player.

Source Code

A couple things to note on this one. If you’re going turn on word-wrapping for a DecryptingTextField, you should set the keepSpaces property to true. This will minimize ugly changing line breaks by always displaying spaces when the randomizer encounters a space in the original text. Additionally, to further improve the aesthetics, you might want to consider using a mono-spaced font like Courier or Consolas so that the characters stay in the same location every time a new random character is displayed. It doesn’t look bad on a single line, but it’s more noticeable when you have multiple lines.

Source code is linked above under each demo. They’re under the terms of an MIT-style license so you may include them in both open source and commercial projects.

About the Author

Josh Tynjala is an indie game developer, entrepreneur, Flash and Flex mercenary, and bowler hat enthusiast.

Discussion
  1. I quite like the new homepage, text layout and colours are great.

    posted by C4RL05 on 01.18.2008
  2. Thanks, C4RL05.

    posted by Josh Tynjala on 01.18.2008
  3. Sorry, stupid question,
    i importend the class in my flash project.
    but don’t really know how tell the class to start working on my textfield.

    could you pls explain this to a noob :D

    best

    posted by skuk on 01.19.2008
  4. skuk, these classes ARE TextFields, so you just use them as you would any regular TextField. Example:

    var tf:TypingTextField = new TypingTextField();
    tf.autoSize = TextFieldAutoSize.LEFT;
    tf.selectable = false;
    tf.text = "I am the very model of a modern major general.";
    this.addChild(tf);
    posted by Josh Tynjala on 01.19.2008
  5. Trying to run this in a Flex project. I created the .as class, then instantiated it as:

    This doesn’t work. Nor does it work when I set the text using a creationComplete function:

    private function initApp() : void
    {
    typingText.text = “this is my beautiful text”;
    }

    What am I doing wrong?

    posted by Tom24569 on 01.29.2008
  6. Tom, these are not designed for Flex.

    posted by Josh Tynjala on 01.29.2008
  7. Hi Josh,

    I played with the typing textfield class and Flex 2.

    I derived the class from TextArea ( mx.controls.TextArea) instead of Textfield and just added one line into

    function timerUpdateHandler(event:TimerEvent):void
    {
        ...
        textField.scrollV += 1;
    }

    to make the scrolling working.

    If I set width/height for my instance it works pretty well with Flex ;)

    best regards,
    kcell

    posted by kcell on 02.05.2008
  8. hey, i really dont get this… its not working

    posted by warren on 03.19.2008
  9. warren, can you share some source code, maybe? I can’t help if I have no idea what the problem might be.

    posted by Josh Tynjala on 03.19.2008
  10. Thanks Josh!

    Best Regards!!

    posted by kurt on 05.05.2008
  11. Hi,

    i don’t understand why i can’t add a textformat, with set setTextFormat, pls help.

    greetz

    posted by tokarma on 09.06.2008
  12. Tokarma, there’s nothing that should stop you from setting the TextFormat. Since these TextFields add text over time after you set their value, maybe you should try setting the defaultTextFormat property instead of calling setTextFormat().

    posted by Josh Tynjala on 09.06.2008
  13. THX,

    defaultTextFormat works, i wonder that setTextFormat don’t work.
    And of course thx for sharing this classes.

    posted by tokarma on 09.06.2008
  14. When you call setTextFormat(), the new TextFormat only applies to existing text in a TextField. The defaultTextFormat property applies to any text added after you set the property. Since these classes add characters one by one over a period of time, only defaultTextFormat will work.

    posted by Josh Tynjala on 09.06.2008
  15. I guess .fla sample file will be better. If somebody interested in letter cycling codes too, so can chech the blog. The sample there is AS as document class, no .fla again.

    posted by Ico on 10.11.2008
  16. Hi Josh, congrats on yr website, nice and clean. I’ve been looking for an ‘aiport departures-type’ effect for a while and your Effect #2: DecryptingTextField is like the closest I’ve found so far :) So here are a few questions:

    I’d like to keep only the 26 normal alphabet characters – easy enough?

    Also, I’d like to start from a word and have it morph into another, letter by letter, from left. Would you know how to tweak yr code to achieve this?
    If you could help that would be much appreciated! Thanks, Y

    posted by Yann on 04.08.2009
  17. Yann, your requirement sounded interesting, so I quickly wrote a class to approximate the effect you requested:

    ////////////////////////////////////////////////////////////////////////////////
    //
    //  Copyright (c) 2009 Josh Tynjala
    //
    //  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 com.joshtynjala.text
    {
    	import flash.events.Event;
    	import flash.events.TimerEvent;
    	import flash.text.TextField;
    	import flash.utils.Timer;
    
    	import mx.utils.StringUtil;
    
    	//--------------------------------------
    	//  Events
    	//--------------------------------------
    
    	/**
    	 * Event that is fired when the text effect is complete.
    	 *
    	 * @eventType flash.events.Event.COMPLETE
    	 */
    	[Event(name="complete",type="flash.events.Event")]
    
    	/**
    	 * A TextField that looks a bit like a classic airport departures board.
    	 *
    	 * @author Josh Tynjala
    	 */
    	public class AirportDepartures extends TextField
    	{
    
    	//--------------------------------------
    	//  Static Properties
    	//--------------------------------------
    
    		private static const TARGET_COUNT:int = 5;
    
    	//--------------------------------------
    	//  Constructor
    	//--------------------------------------
    
    		/**
    		 * Constructor.
    		 */
    		public function AirportDepartures()
    		{
    			super();
    			this._typingTimer = new Timer(this.delay);
    			this._typingTimer.addEventListener(TimerEvent.TIMER, timerUpdateHandler);
    		}
    
    	//--------------------------------------
    	//  Properties
    	//--------------------------------------
    
    		/**
    		 * @private
    		 */
    		private var _typingTimer:Timer;
    
    		/**
    		 * @private
    		 */
    		private var _position:int;
    
    		/**
    		 * @private
    		 */
    		private var _count:int;
    
    		/**
    		 * @private
    		 * Storage for the delay property.
    		 */
    		private var _delay:Number = 30;
    
    		/**
    		 * The number of milliseconds before a new character is decrypted.
    		 */
    		public function get delay():Number
    		{
    			return this._delay;
    		}
    
    		/**
    		 * @private
    		 */
    		public function set delay(value:Number):void
    		{
    			this._delay = value;
    		}
    
    		/**
    		 * @private
    		 * Storage for the text property.
    		 */
    		private var _text:String = "";
    
    		/**
    		 * @copy flash.text.TextField#text
    		 */
    		override public function get text():String
    		{
    			return this._text;
    		}
    
    		/**
    		 * @private
    		 */
    		override public function set text(value:String):void
    		{
    			this._text = value;
    
    			super.text = StringUtil.trim(super.text);
    			this._text = StringUtil.trim(this._text);
    
    			//it's easiest if we make the strings the same length. we can trim
    			//the whitespace again later.
    			var maxLength:Number = Math.max(super.text.length, this._text.length);
    			while(this._text.length < maxLength)
    			{
    				this._text += " ";
    			}
    			while(super.text.length < maxLength)
    			{
    				super.text = super.text + " ";
    			}
    
    			this._position = 0;
    			this._count = 0;
    
    			if(value.length > 0)
    			{
    				this._typingTimer.delay = this.delay;
    				this._typingTimer.start();
    			}
    		}
    
    		/**
    		 * @private
    		 * Storage for the keepSpaces property.
    		 */
    		private var _keepSpaces:Boolean = false;
    
    		/**
    		 * If true, the TextField will always choose a space
    		 * during randomization when it encounters a space in the original
    		 * text. This can help the aesthetics when displaying multiple lines.
    		 */
    		public function get keepSpaces():Boolean
    		{
    			return this._keepSpaces;
    		}
    
    		/**
    		 * @private
    		 */
    		public function set keepSpaces(value:Boolean):void
    		{
    			this._keepSpaces = value;
    		}
    
    	//--------------------------------------
    	//  Private Methods
    	//--------------------------------------
    
    		/**
    		 * @private
    		 */
    		private function timerUpdateHandler(event:TimerEvent):void
    		{
    			//it might change during the typing
    			this._typingTimer.delay = this.delay;
    
    			this._count++;
    			if(this._count == TARGET_COUNT)
    			{
    				this._count = 0;
    				this._position++;
    			}
    
    			if(this._position > this._text.length)
    			{
    				//if we've typed the full text, we can stop
    				this._typingTimer.stop();
    
    				//get rid of the extra whitespace used to make them the same
    				//length
    				super.text = StringUtil.trim(super.text);
    				this._text = StringUtil.trim(this._text);
    
    				this.dispatchEvent(new Event(Event.COMPLETE));
    				return;
    			}
    
    			var oldText:String = super.text;
    			super.text = this._text.substr(0, this._position);
    			if(this._position < this._text.length)
    			{
    				super.text = super.text + this.randomChars("x") + (oldText.length > this._position ? oldText.substr(this._position + 1) : "");
    			}
    		}
    
    		/**
    		 * @private
    		 *
    		 * Generates a string of the specified length containing random
    		 * characters in the character code range 33 to 126.
    		 */
    		private function randomChars(value:String):String
    		{
    			var result:String = "";
    			var count:int = value.length;
    			for(var i:int = 0; i < count; i++)
    			{
    				//for best results on word wrapped textfields,
    				//spaces should be kept in the same locations.
    				//a monospaced font makes it even better.
    				if(this.keepSpaces && value.charAt(i) == " ")
    				{
    					result += " ";
    				}
    				else
    				{
    					result += String.fromCharCode(int(65 + Math.random() * 25));
    				}
    			}
    			return result;
    		}
    	}
    }

    Note: Rather than flipping through each character in order, it displays five random characters before choosing the correct one. Not exactly, but it doesn't look bad, in my opinion. For best results, use a fixed-width font like Courier New.

    posted by Josh Tynjala on 04.08.2009
  18. Wow! Just found out yr reply: will try it out whenever I can, but thanks a lot for such a quick response! :) Y

    posted by Yann on 04.10.2009
  19. good stuff,
    where I can download your com.joshtynjala class?
    thanks!

    posted by gabriele on 06.03.2009
  20. John what do I have to change in your class to get decrypting effect on a textfield that is already in the stage?

    posted by sek on 09.07.2009
  21. sek, you’d probably have to change it from extending TextField to passing in a TextField reference. Instead of using this to refer to the TextField, you’d have some variable of type TextField that you’d use instead.

    posted by Josh Tynjala on 09.07.2009
  22. Hi,
    I’m having problems setting a textFormat for this class. Saw some people had the same problem and that they’ve made it work using defaultTextFormat. That doesn’t seam to help in my case.
    The text just disappears when I apply it.

    My method for setting up the textField Looks like this:

    public function initText():void
    {
    	var tx:TextFormat = new TextFormat();
    	tx.size = 50;
    
    	ct.defaultTextFormat = tx;
    	ct = new DecryptingTextField();
    	ct.text = "Hello, this is some text";
    	ct.y = 50;
    
    	addChild(ct);
    	stage.addEventListener(MouseEvent.CLICK, hej);
    }
    

    Any help would be great!

    Thanks
    /John

    posted by John on 03.28.2010
  23. John, you seem to be setting the defaultTextFormat and then replacing the TextField completely with a new one.

    posted by Josh Tynjala on 03.28.2010
Share Your Thoughts

To display code in comments: <pre>Code here. May be multiline. Format XML with &gt; and &lt; entities.</pre>

Some HTML allowed in comments: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>