Category Archives: UI

Fill in the Blank

Actionscript:
  1. var story:String = "Fill in the _____.";
  2.  
  3.  
  4. var txt:TextField = new TextField();
  5. txt.defaultTextFormat = new TextFormat("Georgia", 20);
  6. txt.width = stage.stageWidth;
  7. txt.multiline = true;
  8. txt.wordWrap = true;
  9. txt.text = story;
  10. addChild(txt);
  11.  
  12. var alph:Array = "abcdefghijklmnopqrstuvwxyz".split("");
  13. var keys:Object = {};
  14. for (var i:int = 0; i<alph.length; i++){
  15.     keys[65 + i] = alph[i];
  16. }
  17. stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyPressed);
  18. function onKeyPressed(evt:KeyboardEvent):void{
  19.      
  20.     if (evt.keyCode == Keyboard.ENTER){
  21.         story = "Fill in the _____.";
  22.         txt.text = story;
  23.     }
  24.    
  25.     for (var i:int = 0; i<story.length; i++){
  26.         if (story.charAt(i) == "_"){
  27.             var head:String = story.substr(0, i);
  28.             var tail:String = story.substr(i + 1);
  29.             var letter:String = keys[evt.keyCode];
  30.             if (!letter) return;
  31.             story = head + letter + tail;
  32.            
  33.             txt.text = story;
  34.            
  35.             break;
  36.         }
  37.     }
  38. }

I needed to do a fill in the blank for a personal project that I'm working on and this is what I came up with. Have a look at the swf here:

(you need to click first so you can type with the keyboard):
Fill in the blank

Also posted in TextField | Tagged , , | 3 Comments

3D Ring

Actionscript:
  1. [SWF(width = 500, height=500)]
  2. var ring:MovieClip = createRing();
  3. ring.x = stage.stageWidth / 2;
  4. ring.y = stage.stageHeight / 2;
  5. addChild(ring);
  6.  
  7. function createRing(sectionNum:int = 30):MovieClip{
  8.     var container:MovieClip = new MovieClip();
  9.     container.circles = [];
  10.     container.theta = 0;
  11.     container.thetaDest = 0;
  12.     var step:Number = (Math.PI * 2) / sectionNum;
  13.     for (var i:int = 0; i<sectionNum; i++){
  14.         var c:MovieClip = new MovieClip();
  15.         with (c.graphics){
  16.             lineStyle(0,0x000000);
  17.             beginFill(0xCCCCCC);
  18.             drawCircle(0,0,20);
  19.         }
  20.         c.thetaOffset = step * i;
  21.         container.addChild(c);
  22.         container.circles.push(c);
  23.     }
  24.     container.addEventListener(Event.ENTER_FRAME, onRun);
  25.     return container;
  26. }
  27. function onRun(evt:Event):void {
  28.     var container:MovieClip = MovieClip(evt.currentTarget);
  29.     var num:int = container.circles.length;
  30.     for (var i:int = 0; i<num; i++){
  31.         var c:MovieClip = container.circles[i];
  32.         var angle:Number = container.theta + c.thetaOffset;
  33.         c.x = 200 * Math.cos(angle);
  34.         c.y = 100 * Math.sin(angle);
  35.         c.scaleX = (100 + c.y) / 120 + 0.2;
  36.         c.scaleY = c.scaleX;
  37.     }
  38.     container.circles.sortOn("y", Array.NUMERIC);
  39.     for (i = 0; i<num; i++){
  40.         container.addChild(container.circles[i]);
  41.     }
  42.     if (container.mouseX <-100){
  43.         container.thetaDest -= 0.05;
  44.     }
  45.     if (container.mouseX> 100){
  46.         container.thetaDest += 0.05;
  47.     }
  48.     container.theta += (container.thetaDest  - container.theta) / 12;
  49.    
  50. }

This snippet shows how to create a 3D ring navigation using sine and cosine. Have a look:

Also posted in 3D, Graphics, MovieClip, arrays, motion, sortOn | Tagged , , | 3 Comments

Who Has the Best Button Class?

We've all done it... we've wasted way too much time writing an overly complex class just to create a simple square/roundrect button. Who has the nicest one? Post a link to yours (googlecode, wonderfl, etc...).

I've always avoided wasting too much time writing one of these but just spent 2 hours writing an overly complex one... looking forward to the way that people have done it....

For instance, did you make your class dynamic? Did you use scale9? did you use CSS? etc... Just curious how complex have gotten with it.

Also posted in OOP | Tagged , , , | 11 Comments

~20,000 Rollovers

Actionscript:
  1. [SWF(width = 500, height = 500, frameRate = 30)]
  2.  
  3. var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
  4.  
  5. var indexCanvas:BitmapData = new BitmapData(stage.stage.stageWidth, stage.stageHeight, false,
  6.                                             0xFFFFFF);
  7. addChild(new Bitmap(canvas));
  8.  
  9. var s:Shape = new Shape();
  10.  
  11. var lineData:Array = [];
  12. var dataIndex:int = 0;
  13.  
  14. trace(0xFFFFFF - 1)
  15. var totalLines:int = 20000;
  16. var iterations:int = 9;
  17. var linesPerIter:int = totalLines / iterations;
  18.  
  19. var xp:int = stage.stageWidth / 2;
  20. var yp:int = stage.stageHeight / 2;
  21.  
  22. var stepAmt:Number = 60;
  23. var halfStepAmt:Number = stepAmt / 2;
  24.  
  25. addEventListener(Event.ENTER_FRAME, onDraw);
  26. function onDraw(evt:Event):void {
  27.      if (lineData.length <totalLines){
  28.         generateData(linesPerIter);
  29.      }else{
  30.         stage.quality = "high";
  31.         addChild(s);
  32.         s.x = 0;
  33.         s.y = 0;
  34.          
  35.         removeEventListener(Event.ENTER_FRAME, onDraw);
  36.         addEventListener(Event.ENTER_FRAME, onRun);
  37.      }
  38. }
  39.  
  40. function onRun(evt:Event):void {
  41.    var currentIndex:int = indexCanvas.getPixel(mouseX, mouseY);
  42.    var currentLine:Array = lineData[currentIndex];
  43.    
  44.    s.graphics.clear();
  45.    if (currentIndex != 0xFFFFFF){
  46.           s.graphics.lineStyle(3, 0xFF0000);
  47.           s.graphics.moveTo(currentLine[0], currentLine[1]);
  48.           s.graphics.lineTo(currentLine[2], currentLine[3]);  
  49.    }
  50. }
  51.  
  52. function generateData(num:int):void{
  53.     var rxA:int, rxB:int, ryA:int, ryB:int;
  54.     var g:Graphics = s.graphics;
  55.     for (var i:int = 0; i<num; i++){
  56.         rxA = xp;
  57.         ryA = yp;
  58.        
  59.         xp += Math.round(Math.random() * stepAmt) - halfStepAmt;
  60.         yp += Math.round(Math.random() * stepAmt) - halfStepAmt;
  61.        
  62.         if (xp> stage.stageWidth){
  63.             xp = stage.stageWidth - halfStepAmt;
  64.         }else
  65.         if (xp <0){
  66.             xp = halfStepAmt;
  67.         }
  68.         if (yp> stage.stageHeight){
  69.             yp = stage.stageHeight - halfStepAmt;
  70.         }else
  71.         if (yp <0){
  72.             yp = halfStepAmt;
  73.         }
  74.        
  75.         rxB = xp;
  76.         ryB = yp;
  77.          
  78.         lineData[dataIndex] = [rxA, ryA, rxB, ryB];            
  79.         s.x = rxA;
  80.         s.y = ryA;
  81.         var endX:Number = rxB - rxA;
  82.         var endY:Number = ryB - ryA;
  83.         var m:Matrix = s.transform.matrix;
  84.         g.clear();
  85.         g.lineStyle(1, 0x000000, 0.3);
  86.  
  87.         g.lineTo(endX, endY);
  88.         stage.quality = "high";
  89.         canvas.draw(s, m);
  90.        
  91.         g.clear();
  92.         g.lineStyle(3, dataIndex);
  93.        
  94.         g.lineTo(endX, endY);
  95.         stage.quality = "low";
  96.         indexCanvas.draw(s, m);
  97.        
  98.         dataIndex++
  99.     }
  100. }

I'm working on a data visualization that contains a long path made up of approximately one million points. There is some information associated with every two sets of coordinates that needs to be displayed when the user rolls their mouse over any part of the line.

I took a little time to think about the best way to do this and came up with a few techniques. The first one I tried seems to work nicely - this snippet is the proof of concept for that first technique. I tested this snippet with 1,000,000 xy coordinates and it works nicely. It takes a little while to draw though, so for the purposes of this demo I've just included 20,000 coordinates.

Have a look at the swf over at wonderfl.net

The way this works is by drawing lines to two different BitmapData instances. I draw anti-aliased slightly transparent lines to a BitmapData instance called "canvas" (this is added to the display list) - I then draw aliased lines to a BitmapData called "indexCanvas" (this is never added to the display list) - each aliased line uses an incremental value for its color - this incremental value is also the index for a two dimensional array containing the coordinate information for the aliased line. I use getPixel() on the "indexCanvas" and use the return value as the index for the 2D array. The data from the 2D array is used to draw a red line with the graphics class. This technique enables you to have many many rollovers and all you ever have to do is call getPixel() and use the returned color value to look up info about what you're mouse is touching.

There are a few cool ways this could be repurposed and this is really only one solution to the problem of having many many things that you need to be able to rollover... there are others that don't use BitmapData at all... I may write those up in the next couple of days.

Also posted in BitmapData, Data Structures, arrays, display list, graphics algorithms, matrix, misc, pixel manipulation, return values | Tagged , , | 2 Comments

Obfuscated Slider

Actionscript:
  1. var slider:MovieClip = makeSlider();
  2. slider.addEventListener(Event.CHANGE, function(evt:Event):void{
  3.     trace(evt.currentTarget.percent);                                  
  4. });
  5.  
  6. function makeSlider():MovieClip{
  7.     var slider:MovieClip = MovieClip(addChild(new MovieClip()));
  8.     var circle:Sprite = Sprite(slider.addChild(new Sprite()));
  9.     with (circle.graphics) beginFill(0x000000), drawCircle(0,0,10);
  10.     var line:Shape = Shape(slider.addChild(new Shape()));
  11.     with (line.graphics) lineStyle(0,0x000000), lineTo(0, 100);
  12.     slider.x = slider.y = 100;
  13.     circle.addEventListener(MouseEvent.MOUSE_DOWN, function(evt:Event):void{ evt.currentTarget.startDrag(false, new Rectangle(0,0,0,100)), slider.addEventListener(Event.ENTER_FRAME, onChange) });
  14.     var stopIt:Function = function(){ stopDrag(), slider.removeEventListener(Event.ENTER_FRAME, onChange) };
  15.     stage.addEventListener(Event.MOUSE_LEAVE, stopIt);
  16.     stage.addEventListener(MouseEvent.MOUSE_UP, stopIt);
  17.     return slider;
  18. }
  19. function onChange(evt:Event):void { evt.currentTarget.percent = evt.currentTarget.getChildAt(0).y / 100, evt.currentTarget.dispatchEvent(new Event(Event.CHANGE)) }

This is a pretty nasty implementation of a basic slider. Just felt like writing some obfuscated code today... It could probably be made even more confusing with some additional tweaks...

Have a look at the swf over at wonderfl...

Posted in UI | Tagged , , | Comments closed

Hide Browser Scrollbars

Actionscript:
  1. /*
  2. This snippet is by Mels le Noble
  3. www.melslenoble.nl
  4. It will hide the browser scrollbars.
  5. */
  6.  
  7. // see if we are testing locally
  8. if (stage.loaderInfo.url.split("/")[2])
  9. {
  10.  ExternalInterface.call("function(){document.body.style.overflow='hidden';document.html.style.overflow = 'hidden';}");
  11. }

This snippet by Mels le Noble will hide the browser scrollbars. The bulk of the snippet is the javascript inside the ExternalInterface.call() method. I like the trick that Mels uses to check if the swf is local.... snippet-worthy in itself.

Posted in UI | Tagged , , , , | Leave a comment

HSV Color Type

Actionscript:
  1. [SWF(width=560,height=300,backgroundColor=0x000000,frameRate=30)]
  2.  
  3. var key:Object = new Object();
  4. var alphabet:Array = "abcdefghijklmnopqrstuvwxyz".split("");
  5.  
  6. var num:Number = alphabet.length;
  7. var step:Number = 360 / num;
  8.  
  9. var colors:Object = new Object();
  10. for (var i:int  = 0; i<num; i++){
  11.     var index:String = alphabet[i];
  12.      key[index] = 65 + i;
  13.      var c:Array = hsv(i * step, 1, 1);
  14.      colors[index] = c[0] <<16 | c[1] <<8 | c[2];
  15. }
  16. alphabet.push("32");
  17. num++;
  18. key["32"] = 32;
  19. colors["32"]  = 0x333333;
  20. x = y = 10;
  21. var count:int = 0;
  22. var size:int = 20;
  23. stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyPressed);
  24. function onKeyPressed(evt:KeyboardEvent):void{
  25.     for (var i:int= 0; i<num; i++){
  26.         var index:String = alphabet[i];
  27.          if (index == "32"){
  28.             trace("hi", evt.keyCode, key[index]);
  29.          }
  30.         if (evt.keyCode == key[index]){
  31.             graphics.beginFill(colors[index]);
  32.             var xp:int = count % num * size;
  33.             var yp:int = int(count / num) * size;
  34.             graphics.drawRect(xp, yp, size, size);
  35.             count++;
  36.         }
  37.     }
  38. }
  39. // ported from here:
  40. //http://www.cs.rit.edu/~ncs/color/t_convert.html
  41. function hsv(h:Number, s:Number, v:Number):Array{
  42.     var r:Number, g:Number, b:Number;
  43.     var i:int;
  44.     var f:Number, p:Number, q:Number, t:Number;
  45.     if (s == 0){
  46.         r = g = b = v;
  47.         return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
  48.     }
  49.     h /= 60;
  50.     i  = Math.floor(h);
  51.     f = h - i;
  52.     p = v *  (1 - s);
  53.     q = v * (1 - s * f);
  54.     t = v * (1 - s * (1 - f));
  55.     switch( i ) {
  56.         case 0:
  57.             r = v, g = t, b = p;
  58.             break;
  59.         case 1:
  60.             r = q, g = v, b = p;
  61.             break;
  62.         case 2:
  63.             r = p, g = v, b = t;
  64.             break;
  65.         case 3:
  66.             r = p, g = q, b = v;
  67.             break;
  68.         case 4:
  69.             r = t, g = p, b = v;
  70.             break;
  71.         default:        // case 5:
  72.             r = v, g = p, b = q;
  73.             break;
  74.     }
  75.     return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
  76. }

This snippet is a typing experiment - for every letter, you type a box filled with a specific color is drawn to the stage. The color associated with each letter is determined by moving through hsv color space - so typing an alphabet will end up with something resembling a spectrum.

Also posted in Graphics, color, keys | Tagged , , | Leave a comment

Mouse.cursor

Actionscript:
  1. var count:int = 0;
  2. var cursors:Array = [MouseCursor.ARROW, MouseCursor.BUTTON, MouseCursor.HAND, MouseCursor.IBEAM];
  3. setInterval(changeMouse, 100);
  4. function changeMouse():void{
  5.     Mouse.cursor = cursors[count % cursors.length];
  6.     count++;
  7. }

This is a small snippet that shows the cursor property of the Mouse class. This is fp10 only...

Posted in UI | Tagged , | Leave a comment

Quick UI Creation (Brainstorming)

Actionscript:
  1. var ui:QuickUI = new QuickUI();
  2. ui.x = 20;
  3. ui.y = 260;
  4. ui.addEventListener(Event.CHANGE, onChange);
  5. addChild(ui);
  6.  
  7. var spriteA:Sprite = makeSprite(150,150,0xFF0000);
  8. spriteA.addEventListener(MouseEvent.CLICK, onShowAddProps);
  9.  
  10. var spriteB:Sprite = makeSprite(350,150, 0xCC6600);
  11. spriteB.addEventListener(MouseEvent.CLICK, onShowExcludeProps);
  12.  
  13. var spriteC:Sprite = makeSprite(550,150, 0x550066);
  14. spriteC.addEventListener(MouseEvent.CLICK, onShowExcludeProps);
  15.  
  16. function onChange(evt:Event):void{
  17.     // updates the property on the current target object
  18.     ui.updateObject(evt.target);
  19. }
  20. function onShowAddProps(evt:MouseEvent):void{
  21.     ui.rows = 3;
  22.     // onle show the following properties
  23.     //  add(property, label)
  24.     ui.add("name", "name:");
  25.     ui.add("x", "x location:");
  26.     ui.add("y", "y location:");
  27.     ui.add("scaleX", "x scale:");
  28.     ui.add("buttonMode", "show hand cursor");
  29.     ui.create(evt.currentTarget);
  30.     ui.window({title:"Select Properties: "+ evt.currentTarget.name});
  31. }
  32.  
  33. function onShowExcludeProps(evt:MouseEvent):void {
  34.     ui.rows = 5;
  35.     // don't show a few select properties - if add() is not called
  36.     // all properties will be shown
  37.     ui.exclude("doubleClickEnabled");
  38.     ui.exclude("useHandCursor");
  39.     // build the UI, give two custom labels to x and y properties
  40.     ui.create(evt.currentTarget, {x:"x loc:", y:"y loc:"});
  41.     // optionally render a window behind the UI elements
  42.     ui.window({title:"All Properties: " + evt.currentTarget.name});
  43. }
  44.  
  45.  
  46. function makeSprite(xp:Number, yp:Number, col:uint):Sprite{
  47.     var s:Sprite = Sprite(addChild(new Sprite()));
  48.     s.x = xp;
  49.     s.y = yp;
  50.     s.buttonMode = true;
  51.     with (s.graphics) beginFill(col), drawRect(-40, -40, 80, 80);
  52.     return s;
  53. }

This snippet is my first stab at creating a library that makes certain types of UI creation very easy. It works by automatically creating input text fields and check boxes for all public properties that make sense with that type of UI. It has a long way to go, but this is a good start

Take a look at the swf to get an idea of what this snippet does.

Download the source for the library and the fla for the above demo.

I hope to get this library to the point that it will at least be useful for internal UI - that is, for level editors and mini-cms systems.

Also posted in dynamic | Tagged , | 4 Comments

static BitmapData References

Actionscript:
  1. package{
  2.    
  3.     import flash.display.*;
  4.     import flash.events.*;
  5.     import flash.geom.*;
  6.    
  7.     public class QuickCheckBox extends Sprite{
  8.        
  9.         private var _checked:Boolean;
  10.         private var _bitmap:Bitmap;
  11.         private var _canvas:BitmapData;
  12.        
  13.         private static var checked:BitmapData;
  14.         private static var unchecked:BitmapData;
  15.         private static var bg:Shape;
  16.         private static var ex:Shape;
  17.         private static var pnt:Point;
  18.        
  19.         // static init for BitmapData and drawing
  20.         {
  21.             trace("only render your graphics once");
  22.             pnt = new Point(0,0);
  23.             checked = new BitmapData(10,10, true, 0x00000000);
  24.             unchecked = new BitmapData(10, 10, true, 0x00000000);
  25.             bg = new Shape();
  26.             with(bg.graphics) lineStyle(0,0x000000), beginFill(0xEFEFEF), drawRect(0,0,9,9);
  27.             unchecked.draw(bg);
  28.             ex = new Shape();
  29.             with(ex.graphics) {
  30.                 lineStyle(2,0x333333), moveTo(3, 3),
  31.                 lineTo(7, 7), moveTo(7, 3), lineTo(3, 7);
  32.             }
  33.             checked.draw(bg);
  34.             checked.draw(ex);
  35.         }
  36.        
  37.         public function QuickCheckBox(value:Boolean = false):void{
  38.             _checked = value;
  39.             _canvas =  new BitmapData(10,10, true, 0x00000000);
  40.             _bitmap = new Bitmap(_canvas);
  41.             addChild(_bitmap);
  42.             buttonMode = true;
  43.             render();
  44.             addEventListener(MouseEvent.CLICK, onClick);
  45.         }
  46.        
  47.         public function render():void{
  48.              if (_checked){
  49.                  _canvas.copyPixels(QuickCheckBox.checked, _canvas.rect, pnt);
  50.              }else{
  51.                  _canvas.copyPixels(unchecked, _canvas.rect, pnt);
  52.             }
  53.         }
  54.        
  55.         private function onClick(evt:Event):void{
  56.             _checked = !_checked;
  57.             render();
  58.             this.dispatchEvent(new Event(Event.CHANGE, true));
  59.         }
  60.        
  61.         public function get checked():Boolean{
  62.             return _checked;
  63.         }
  64.        
  65.         public function set checked(val:Boolean):void{
  66.             _checked = val;
  67.             render();
  68.         }
  69.     }
  70. }

This checkbox class uses a static initializer. Static initializers can come in handy to initialize some static variables for all class instances to make use of. In this case I'm using the static initializer to create checked and unchecked BitmapData objects - all instances of QuickCheckBox use these two static BitmapData objects rather than create their own unique ones.

Here is some client code if you want to test this class out:

Actionscript:
  1. for (var i:int = 0; i<100; i++){
  2.  var checkBox:QuickCheckBox = new QuickCheckBox(true);
  3.  checkBox.x =100 +  i % 10 * 12;
  4.  checkBox.y = 100 + int(i / 10) * 12;
  5.  // uncheck a few checkboxes
  6.  if (checkBox.x == checkBox.y){
  7.      checkBox.checked = false;
  8.  }
  9.  addChild( checkBox);
  10.  // checkBox dispatches a change event
  11.  checkBox.addEventListener(Event.CHANGE, onChange);
  12. }
  13. function onChange(evt:Event):void{
  14.     trace(evt.currentTarget.checked);
  15. }
  16. /*outputs
  17. only render your graphics once
  18. */

This draws 100 checkboxes, - you'll also notice in the output window that the trace statement from QuickCheckBox only runs once.

Here are 100 instances of QuickCheckBox:

Also posted in BitmapData, OOP | Tagged , | 4 Comments