author Jesse Freeman aka @theFlashBum | http://jessefreeman.com
This is the Doc Class to run my SketchArea. This class adds keyboard support
to help control features of the SketchArea. Here is a list of all the keys
I have set up.
Keyboard Short Cuts
Space Bar - flips between src a and src b. Source A is always the bottom
layer and Source B is the paint layer.
r - Redraw last animation.
c - Clear SketchArea
s - Quick save. The SketchArea will take the last set of recorded points
along with what mode it is in and save it to a number key. For example if
nothing has been saved and you hit save it will register at key 1. Next
save will be key 2, etc.
1 - 0 Plays back a saved animation.
d - Activates the draw tool. Once in draw mode you will see a representation
of the brush that follows the mouse. When you are done you can hit d to
exit draw mode. Hitting replay automatically exits you from draw mode.
+ - Increases the size of the brush.
- - Decreases the size of the brush.
The
/**
* Copyright FlashBum ( http://wonderfl.net/user/FlashBum )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/gBuu
*/
package {
import flash.text.TextFieldAutoSize;
import flash.text.TextField;
import flash.display.Shape;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.net.URLRequest;
import flash.system.LoaderContext;
/**
* author Jesse Freeman aka @theFlashBum | http://jessefreeman.com
*
* This is the Doc Class to run my SketchArea. This class adds keyboard support
* to help control features of the SketchArea. Here is a list of all the keys
* I have set up.
*
* Keyboard Short Cuts
*
* Space Bar - flips between src a and src b. Source A is always the bottom
* layer and Source B is the paint layer.
*
* r - Redraw last animation.
*
* c - Clear SketchArea
*
* s - Quick save. The SketchArea will take the last set of recorded points
* along with what mode it is in and save it to a number key. For example if
* nothing has been saved and you hit save it will register at key 1. Next
* save will be key 2, etc.
*
* 1 - 0 Plays back a saved animation.
*
* d - Activates the draw tool. Once in draw mode you will see a representation
* of the brush that follows the mouse. When you are done you can hit d to
* exit draw mode. Hitting replay automatically exits you from draw mode.
*
* + - Increases the size of the brush.
*
* - - Decreases the size of the brush.
*
* The main goal of this demo is to illustrate how custom wipe on/off transitions
* can be built dynamically in flash based on user generated array of points.
*
* Important - this is a proof of concept, there is still a lot of work and
* optimizations I need to do to get this clas production ready.
*
*/
public class FlashTest extends Sprite {
protected var sketchArea : SketchArea;
protected var loader : Loader;
private var label : TextField;
public function FlashTest() {
configureStage();
init();
}
protected function configureStage() : void {
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
}
/**
*
* Create a loader to get a background image.
*
*/
protected function init() : void {
loader = new Loader();
loader.load(new URLRequest("http://demos.flashartofwar.com/RandomImageComposite/images/skin3/photo.jpg"), new LoaderContext(true));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoad);
}
/**
*
* Once image is loaded we can create our SketchArea class. Here we are
* passing in a bitmap of the image we just loaded (will go into sourceA)
* then create a white layer and pass it in to represent sourceB (our
* paint layer/color).
*
*/
protected function onImageLoad(event : Event) : void {
var bmd : BitmapData = Bitmap(loader.content).bitmapData.clone();
var sourceA : Bitmap = new Bitmap(bmd); // This is the background layer
var sourceB : Shape = new Shape();
sourceB.graphics.beginFill(0xffffff);
sourceB.graphics.drawRect(0, 0, sourceA.width, sourceA.height);
sourceB.graphics.endFill();
sketchArea = new SketchArea(loader.width + loader.x, loader.height + loader.y, sourceA, sourceB);
addChild(sketchArea);
loader = null;
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
label = new TextField();
label.autoSize = TextFieldAutoSize.LEFT;
label.multiline = true;
label.wordWrap = true;
label.width = 400;
label.htmlText = "<b>Keyboard Short Cuts</b>: <br><b>Space Bar</b> - flips between src a and src b <br> <b>R</b> - Redraw last animation <br> <b>C</b> - Clear SketchArea <br> <b>S</b> - Quick save | <b>1</b> - <b>0</b> Plays back a saved animation <br> <b>D</b> - Activates the draw tool | <b>+</b>/<b>-</b> - Increases/Decrease brush size";
label.y = sketchArea.height;
addChild(label);
sketchArea.toggleDrawMode();
}
protected function keyDownHandler(event : KeyboardEvent) : void {
switch(event.keyCode) {
case(32):
trace("Spacebar");
sketchArea.toggleDrawMode();
break;
case(82):
trace("r");
sketchArea.redraw();
break;
case(67):
trace("c");
sketchArea.clear();
break;
case(83):
sketchArea.quickSave();
break;
case(49):
case(50):
case(51):
case(52):
case(53):
case(54):
case(55):
case(56):
case(57):
var saveID : Number = event.keyCode - 49;
sketchArea.quickPlayBack(saveID);
break;
case(68):
sketchArea.activateDraw();
break;
case(187):
sketchArea.increaseThickness();
break;
case(189):
sketchArea.decreaseThickness();
break;
}
//trace("keyDownHandler: " + event.keyCode);
}
}
}
import flash.geom.Rectangle;
import flash.geom.Matrix;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.geom.Point;
import flash.utils.Dictionary;
import flash.utils.Timer;
/**
* @author jessefreeman
*/
class SketchArea extends Sprite {
private static const SOURCE_A : String = "sourceA";
private static const SOURCE_B : String = "sourceB";
private static const BORDER : String = "border";
private static const SKETCH_LAYER : String = "sketchArea";
private static const BRUSH_PREVIEW : String = "brushPreview";
protected var _width : Number;
protected var _height : Number;
protected var sketchCoords : Array = new Array;
protected var currentVectorID : Number = 0;
protected var currentPoint : Object;
protected var thickness : Number = 150 ;
protected var photoContainer : Sprite;
protected var lineColor : uint = 0xffffff;
protected var redrawSpeed : Number = 10;
protected var redrawTimer : Timer;
protected var eraseMode : Boolean = false;
protected var savedSketches : Array = new Array();
protected var active : Boolean;
protected var display : Bitmap;
protected var layers : Dictionary = new Dictionary(true);
protected var brushPreviewMatrix : Matrix = new Matrix();
protected var sampleRect : Rectangle;
protected var samplePoint : Point;
protected var sourceBitmapData : BitmapData;
protected var sketchBitmapData : BitmapData;
/**
* The SketchArea represents a canvass where a use can draw on, play back
* the drawing's animation, and save it temporarily.
*
* At it's core the SketchArea has 2 layers, sourceA (the background) and
* sourceB (the paint fill).
*
* My swapping sourceA and sourceB you can easily create reveal and remove
* animations for images.
*
* @param width - width of the canvas
* @param height - height of the canvas
* @param sourceA - represents the background image used by the canvas
* @param sourceB - represents the fill image for the brush
*
*/
public function SketchArea(width : Number = 600, height : Number = 400, sourceA : DisplayObject = null, sourceB : DisplayObject = null) {
_width = width;
_height = height;
if(sourceA)
this.sourceA = sourceA;
if(sourceB)
this.sourceB = sourceB;
init();
}
public function get sourceA() : DisplayObject {
return layers[SOURCE_A];
}
/**
* Sets the background image for the SketchArea
* @param source - DisplayObject to be used as sourceA.
*/
public function set sourceA(source : DisplayObject) : void {
layers[SOURCE_A] = source;
}
public function get sourceB() : DisplayObject {
return layers[SOURCE_B];
}
/**
* Sets the fill for the brush in the SketchArea
* @param source - DisplayObject to be used as sourceB.
*/
public function set sourceB(source : DisplayObject) : void {
layers[SOURCE_B] = source;
}
/**
* This makes the line thickness of the brush smaller. Decreases by 10px
* as long as the thickness is greater then 0.
*/
public function decreaseThickness() : void {
thickness -= 10;
if(thickness < 0 ) thickness = 0;
drawBrushPreview(thickness);
}
/**
* This makes the line thickness of the brush larger. Increase by 10px
* with a 300 px max.
*/
public function increaseThickness() : void {
thickness += 10;
if(thickness > 300 ) thickness = 300;
drawBrushPreview(thickness);
}
/**
* Turns on the Drawing listeners so a use can begin recording their
* sketch.
*/
public function activateDraw() : void {
active = !active;
if(active) {
addMousePressEventListeners();
} else {
removeMousePressEventListeners();
}
toggleBrushPreview();
compositeImage();
}
/**
* Drawing mode represents what source is used as the background. This
* method simply swaps out the instance of source a with the instance
* of source b.
*
* This lets gives you the effect of revialing or removing an image.
*/
public function toggleDrawMode() : void {
var aSrc : DisplayObject = layers[SOURCE_A];
var bSrc : DisplayObject = layers[SOURCE_B];
layers[SOURCE_A] = bSrc;
layers[SOURCE_B] = aSrc;
clear();
eraseMode = !eraseMode;
}
/**
* Clears the SketchArea by removing any graphics drawn as well as resetting
* the sketch coordinates array.
*/
public function clear() : void {
clearGraphics();
sketchCoords = new Array;
compositeImage();
}
/**
* This simply clears the graphics layer of the SketchArea.
*/
public function clearGraphics() : void {
layers[SKETCH_LAYER].graphics.clear();
compositeImage();
}
/**
* This will playback the currently active sketch ie what ever the last
* sketch that was created.
*/
public function redraw() : void {
if(active)
activateDraw();
if(redrawTimer && redrawTimer.running)
redrawTimer.stop();
clearGraphics();
var total : Number = sketchCoords.length - 1;
redrawTimer = new Timer(redrawSpeed, total);
redrawTimer.addEventListener(TimerEvent.TIMER, onRedrawTick);
currentVectorID = 0;
var startPoint : Object = sketchCoords[ currentVectorID ];
moveLine(startPoint.x, startPoint.y);
redrawTimer.start();
}
/**
* This saves out the mode and sketchCoords into an object then pushes
* it into a savedSketches array. The id of the newly created object can
* be used with qucikPlayBack to view the results.
*/
public function quickSave() : void {
var savedState : Object = new Object();
savedState.eraseMode = eraseMode;
savedState.sketchCoords = sketchCoords.concat();
savedSketches.push(savedState);
}
/**
* Quick playback takes a saved sketch id (corresponds to an index in the
* savedSketces array and redraws the sketch.
* @param id - id repressing a valid index of the saved coords array.
*/
public function quickPlayBack(id : Number) : void {
if(id < savedSketches.length) {
var state : Object = savedSketches[id];
if(state.eraseMode != eraseMode)
toggleDrawMode();
sketchCoords = state.sketchCoords.concat();
redraw();
}
}
protected function toggleBrushPreview() : void {
if(active) {
drawBrushPreview(thickness);
} else {
layers[BRUSH_PREVIEW].graphics.clear();
}
}
protected function createBorder() : void {
var border : Shape = new Shape();
border.graphics.lineStyle(1, 0x000000, .3);
border.graphics.drawRect(0, 0, _width - 1, _height - 1);
border.graphics.endFill();
layers[BORDER] = border;
}
protected function init() : void {
display = new Bitmap();
display.bitmapData = new BitmapData(_width, _height, true, 0xffffff);
addChild(display);
layers[SKETCH_LAYER] = new Shape();
if(!layers[SOURCE_B]) {
var matt : Shape = new Shape();
matt.graphics.beginFill(0xff0000);
matt.graphics.drawRect(0, 0, _width, _height);
matt.graphics.endFill();
layers[SOURCE_B] = matt;
}
//
layers[BRUSH_PREVIEW] = new Shape();
createBorder();
//
//createMouseBrush( thickness );
compositeImage();
toggleDrawMode();
}
private function onEnterFrame(event : Event) : void {
compositeImage();
}
protected function drawBrushPreview(size : Number) : void {
layers[BRUSH_PREVIEW].graphics.clear();
layers[BRUSH_PREVIEW].graphics.lineStyle(1, 0x000000, .3);
var d : Number = size * .5;
layers[BRUSH_PREVIEW].graphics.drawCircle(0, 0, d);
layers[BRUSH_PREVIEW].graphics.endFill();
}
protected function addMousePressEventListeners() : void {
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
protected function removeMousePressEventListeners() : void {
removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}
protected function onMouseUp(event : MouseEvent) : void {
removeDrawListeners();
sketchCoords.push({x:-1, y:-1});
layers[SKETCH_LAYER].graphics.endFill();
}
protected function onMouseDown(event : MouseEvent) : void {
layers[SKETCH_LAYER].graphics.moveTo(mouseX, mouseY);
layers[SKETCH_LAYER].graphics.lineStyle(thickness, lineColor, 1);
addDrawListeners();
}
protected function addDrawListeners() : void {
addEventListener(MouseEvent.MOUSE_MOVE, onDraw);
}
protected function removeDrawListeners() : void {
removeEventListener(MouseEvent.MOUSE_MOVE, onDraw);
}
protected function onDraw(event : MouseEvent) : void {
currentPoint = {x: mouseX, y: mouseY, thickness: thickness };
sketchCoords.push(currentPoint);
draw(currentPoint);
}
protected function draw(point : Object) : void {
layers[SKETCH_LAYER].graphics.lineTo(point.x, point.y);
}
protected function drawArea(width : Number, height : Number, alpha : Number = 1) : void {
graphics.clear();
graphics.beginFill(0xffffff, alpha);
graphics.drawRect(0, 0, width, height);
graphics.endFill();
}
protected function onRedrawTick(event : TimerEvent = null) : void {
var currentPoint : Object = sketchCoords[ currentVectorID ];
var nextPoint : Object = (currentVectorID == sketchCoords.length) ? {x:-1, y:-1} : sketchCoords[ currentVectorID + 1 ];
var ink : Number = 1;
if((currentPoint.x == -1) && (currentPoint.y == -1)) {
moveLine(nextPoint.x, nextPoint.y);
} else {
layers[SKETCH_LAYER].graphics.lineStyle(thickness, lineColor, ink);
drawLine(currentPoint.x, currentPoint.y);
}
if(currentVectorID > sketchCoords.length) {
redrawTimer.stop();
layers[SKETCH_LAYER].graphics.endFill();
} else {
currentVectorID++;
}
compositeImage();
}
protected function moveLine(x : Number, y : Number) : void {
layers[SKETCH_LAYER].graphics.moveTo(x, y);
}
protected function drawLine(x : Number, y : Number) : void {
layers[SKETCH_LAYER].graphics.lineTo(x, y);
}
protected function compositeImage() : void {
// Create Bitmap data for final image
var bmd : BitmapData = display.bitmapData;
bmd.draw(layers[SOURCE_A]);
brushPreviewMatrix = new Matrix();
brushPreviewMatrix.translate(mouseX, mouseY);
sampleRect = new Rectangle(0, 0, _width, _height);
samplePoint = new Point(0, 0);
sourceBitmapData = new BitmapData(_width, _height, true, 0x000000);
sourceBitmapData.draw(layers[SOURCE_B]);
sketchBitmapData = new BitmapData(_width, _height, true, 0x000000);
sketchBitmapData.draw(layers[SKETCH_LAYER]);
bmd.copyPixels(sourceBitmapData, sampleRect, samplePoint, sketchBitmapData, null, true);
bmd.draw(layers[BORDER]);
bmd.draw(layers[BRUSH_PREVIEW], brushPreviewMatrix);
}
}