One By One Design

Liquid Text Effect

Haven’t been too active around here lately due to a major on going project at work lately as well as the fact that my wife and I are in the slow process of moving homes. Really, the only free time it seems I have these days is during the lunch hour. So, over the past two days, I came up with this fun little toy during lunches.

Having recently re-watched Terminator 2 or 3 or 4 or whichever one had that liquid metal dude, I wanted to do something liquid metally. And below is the text effect result. You can change what it says by typing something new into the text box and clicking the “change” button. You can also try different colors using the color picker in the top left (a deep red blood color may be cool for halloween, a blue for nice looking water or black can create a nice new oily BP logo). You can also mouse over the text to swish it up a bit.

[kml_flashembed publishmethod=”static” fversion=”10.0.0″ movie=”http://blog.onebyonedesign.com/wp-content/uploads/2010/07/liquitext.swf” width=”640″ height=”400″ targetclass=”flashmovie”]

Get Adobe Flash player

[/kml_flashembed]

The code is by no means optimized or even cleaned up, but if you’d like to play around, it’s all below. To use as is, you’ll need the Arial Black font and the great minimalcomps from Keith (Bit-101) Peters.

package  {
	
	import com.bit101.components.ColorChooser;
	import com.bit101.components.InputText;
	import com.bit101.components.PushButton;
	import com.bit101.components.Style;
	import flash.utils.setTimeout;
	
	import com.greensock.easing.Quad;
	import com.greensock.TweenLite;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;	
	import flash.events.Event;
	import flash.events.MouseEvent;	
	import flash.filters.BevelFilter;
	import flash.filters.BlurFilter;
	import flash.filters.DropShadowFilter;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;

	/**
	 * some liquidy metal madness
	 * @author Devon O.
	 */
	
	 [SWF(width='640', height='400', backgroundColor='#444444', frameRate='40')]
	public class Main extends Sprite {
		
		[Embed(source = "../assets/ariblk.ttf", fontFamily = "ariblk", mimeType = "application/x-font", embedAsCFF = "false")] public static const EFONT:Class;
		public static const NUM_BLOBS:int = 500;
		public static const PROXIMITY:int = 35;
		
		private var _holder:Sprite = new Sprite();
		
		private var _display:BitmapData;
		
		private var _textData:BitmapData;
		private var _text:TextField;
		private var _textHolder:Sprite;
		
		private var _textColor:uint = 0xFF333333;
		
		private var _blobs:Vector. = new Vector.(NUM_BLOBS, true);
		private var _toPoints:Vector. = new Vector.(NUM_BLOBS, true);
		
		private var _layout:BitmapLayout;
		
		private var _blur:BlurFilter = new BlurFilter(8, 8, 5);
		private var _bevel:BevelFilter = new BevelFilter(4, 80, 0xFFFFFF, 1, 0x111111, 1, 8, 8, 2, 1);
		private var _shadow:DropShadowFilter = new DropShadowFilter(4, 80, 0x000000, 1, 4, 4, 1, 1, false, false);
		private var _pt:Point = new Point();
		
		private var _colorPicker:ColorChooser;
		private var _input:InputText;
		private var _enterButton:PushButton;
		
		public function Main() {
			init();
		}
		
		private function init():void {
			initText();
			setText("LIQUID");
			
			initDisplay();
			initBalls();
			
			initUI();
			
			addEventListener(Event.ENTER_FRAME, drawDisplay);
		}
		
		private function initDisplay():void {
			_display = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0x00000000);
			var view:Bitmap = new Bitmap(_display);
			view.filters = [_bevel, _shadow];
			addChild(view);
		}
		
		private function initUI():void {
			Style.BUTTON_FACE = 0x333333;
			
			_colorPicker = new ColorChooser(this, 10, 10, 0x333333, colorHandler);
			_colorPicker.usePopup = true;
			
			_input = new InputText(this, _colorPicker.x + _colorPicker.width + 10, 10, "LIQUID");
			
			_enterButton = new PushButton(this, _input.x + _input.width + 10, 10, "CHANGE", swapText);
		}
		
		private function colorHandler(event:Event):void {	
			var r:uint = _colorPicker.value >> 16;
			var g:uint = _colorPicker.value >> 8 & 0xFF;
			var b:uint = _colorPicker.value & 0xFF;
			_textColor = 0xFF << 24 | r << 16 | g << 8 | b;
		}
		
		private function initBalls():void {
			for (var i:int = 0; i < NUM_BLOBS; i++) {
				var b:Circle = new Circle();
				var pt:SimplePoint = _layout.getPoint();
				b.cx = b.tx = b.x = pt.x;
				b.cy = b.ty = b.y = pt.y;
				b.range = randRange(2, 1, 2);
				b.angle = randRange(360, 0);
				b.speed = randRange(.13, .05, 2);
				b.scaleX = b.scaleY = randRange(1, .5, 2);
				_blobs[i] = b;
				_toPoints[i] = new SimplePoint(b.cx + randRange(150, -150), b.cy + randRange(150, -150));
				_holder.addChild(b);
			}
		}
		
		private function initText():void {
			_text = new TextField();
			_text.defaultTextFormat = new TextFormat(new EFONT().fontName, 150, 0x000000);
			_text.autoSize = TextFieldAutoSize.LEFT;
			_text.embedFonts = true;
			_textHolder = new Sprite();
			_textHolder.addChild(_text);
			_textData = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0x00000000);
		}
		
		private function swapText(event:MouseEvent):void {	
			var i:int = NUM_BLOBS;
			while (i--) {
				if (i == 0) {
					TweenLite.to(_blobs[i], .60, { x:_toPoints[i].x, y:_toPoints[i].y, ease:Quad.easeOut, onComplete:completeText } );
				} else {
					TweenLite.to(_blobs[i], .60, { x:_toPoints[i].x, y:_toPoints[i].y, ease:Quad.easeOut } );
				}
			}
		}
		
		private function completeText():void {
			setText(_input.text);
			
			setTimeout(tweenBack, 200);
		}
		
		private function tweenBack():void {
			var i:int = NUM_BLOBS;
			while (i--) {
				var b:Circle = _blobs[i];
				var pt:SimplePoint = _layout.getPoint();
				b.cx = pt.x;
				b.cy = pt.y;
				b.range = randRange(2, 1, 2);
				b.angle = randRange(360, 0);
				b.speed = randRange(.13, .05, 2);
				TweenLite.to(b, .80, { x:b.cx, y:b.cy, ease:Quad.easeOut } );
			}
		}
		
		private function setText(words:String):void {
			_text.text = words;
			_text.x = (stage.stageWidth - _text.width) >> 1;
			_text.y = (stage.stageHeight - _text.height) >> 1;
			
			_textData.fillRect(_textData.rect, 0x00000000);
			_textData.draw(_textHolder);
			
			if (_layout == null) _layout = new BitmapLayout(_textData);
			else _layout.data = _textData;
		}
		
		private function shimmy():void {
			var i:int = NUM_BLOBS;
			while (i--) {
				var b:Circle = _blobs[i];
			
				b.inner.x =  Math.sin(b.angle) * (b.range);
				b.inner.y =  Math.cos(b.angle) * (b.range * 6);
			
				b.angle += b.speed;
				
				var p1:SimplePoint = new SimplePoint(b.cx, b.cy);
				var p2:SimplePoint = new SimplePoint(stage.mouseX, stage.mouseY);
				
				var d:Number = getDist(p1, p2);
				if ( d < (PROXIMITY * PROXIMITY) ) {
					b.tx = stage.mouseX;
					b.ty = stage.mouseY
				} else {
					b.tx = b.cx;
					b.ty = b.cy;
				}
				b.x += (b.tx - b.x) * .25;
                b.y += (b.ty - b.y) * .25;
			}
		}
		
		private function getDist(p1:SimplePoint, p2:SimplePoint):Number {
			return (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y);
		}
		
		private function drawDisplay(event:Event):void {
			shimmy();
			
			_display.fillRect(_display.rect, 0x000000);
			_display.draw(_holder);
			_display.applyFilter(_display, _display.rect, _pt, _blur);
			_display.threshold(_display, _display.rect, _pt, ">",  0x001F0000, _textColor, 0x00FF0000, false);
		}
		
		private function randRange(max:Number, min:Number = 0, decimals:int = 0):Number {
			if (min > max) return NaN;
			var rand:Number = Math.random() * (max-min + Math.pow(10, -decimals)) + min;
			return int(rand * Math.pow(10, decimals)) / Math.pow(10, decimals);
		}
	}
}
package  {
	
	import flash.display.Shape;
	import flash.display.Sprite;
	
	public class Circle extends Sprite {
		
		private var _cx:Number;
		private var _cy:Number;
		private var _tx:Number;
		private var _ty:Number;
		private var _range:Number;
		private var _speed:Number;
		private var _angle:Number;
		private var _inner:Shape = new Shape();
		
		public function Circle() {
			_inner.graphics.beginFill(0xFFFFFF);
			_inner.graphics.drawCircle(0, 0, 6);
			_inner.graphics.endFill();
			addChild(_inner);
		}
		
		public function get cx():Number { return _cx; }
		
		public function set cx(value:Number):void {
			_cx = value;
		}
		
		public function get cy():Number { return _cy; }
		
		public function set cy(value:Number):void {
			_cy = value;
		}
		
		public function get range():Number { return _range; }
		
		public function set range(value:Number):void {
			_range = value;
		}
		
		public function get speed():Number { return _speed; }
		
		public function set speed(value:Number):void {
			_speed = value;
		}
		
		public function get angle():Number { return _angle; }
		
		public function set angle(value:Number):void {
			_angle = value;
		}
		
		public function get inner():Shape { return _inner; }
		
		public function get tx():Number { return _tx; }
		
		public function set tx(value:Number):void {
			_tx = value;
		}
		
		public function get ty():Number { return _ty; }
		
		public function set ty(value:Number):void {
			_ty = value;
		}
	}
}
package  {
	
	import flash.display.BitmapData;

	public class BitmapLayout {
		
		private var _data:BitmapData;
		
		public function BitmapLayout(data:BitmapData) {
			_data = data;
		}
		
		// tip of the hat to the HYPE framework cats for this bit
		public function getPoint():SimplePoint {
			var pt : SimplePoint;
			
			do {
				pt = new SimplePoint();
				pt.x = (Math.random() * _data.width);
				pt.y = (Math.random() * _data.height);
				
				var c:uint = _data.getPixel32(pt.x, pt.y);
				
				var alpha:uint = c >> 24;
				
				if (alpha == 0) pt = null;

				
			} while (pt == null);
			
			return pt;
		}
		
		public function get data():BitmapData { return _data; }
		
		public function set data(value:BitmapData):void {
			_data = value;
		}
	}

}
package  {

	public class SimplePoint {
		
		private var _x:Number;
		private var _y:Number;
		
		public function SimplePoint(x:Number = 0, y:Number = 0 ) {
			_x = x;
			_y = y;
		}
		
		public function get x():Number { return _x; }
		
		public function set x(value:Number):void {
			_x = value;
		}
		
		public function get y():Number { return _y; }
		
		public function set y(value:Number):void {
			_y = value;
		}
	}
}
Posted by

Post a comment

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