I have an application that allows the user to create a custom banner. The app allows the user to add uploaded images (using FileReference) to the banner as well as adding images that are built-in to the application and can be selected within the app. Most of the elements that can be added to the banner are movieclips or textfields.
What I want to do is have a 'Save Design' button that saves the current configuration that the user has set so that if they close and reopen the app they can click the 'Load Design' button and it will load in their previously made design with the right properties (Colours, image position etc.)
What is the best way to do this?
The best way to do this is to store the information in a SharedObject.
The images that are loaded using FileReference can each be stored as a ByteArray. Then, when they're retrieved, you can use Loader.loadBytes() to reload the images.
Here's a quick example which is in no way complete, but it should get you started in the right direction:
import flash.net.SharedObject;
import flash.display.DisplayObject;
var so:SharedObject = SharedObject.getLocal("savedBanner");
function save():void
{
so.data.objects = [];
// loop over all of your display objects and save their information/position
for(var i:int = 0; i < numChildren; i++)
{
var displayObject:DisplayObject = getChildAt(i);
var savedObject:Object = {};
savedObject.className = getQualifiedClassName(displayObject);
savedObject.x = displayObject.x;
savedObject.y = displayObject.y;
savedObject.rotation = displayObject.rotation;
savedObject.scaleX = displayObject.scaleX;
savedObject.scaleY = displayObject.scaleY;
// store any other pertinent data
so.data.objects.push(savedObject);
}
so.flush();
}
function load():void {
for each(var savedObject:Object in so.data.objects)
{
var newObject:DisplayObject = new (getDefinitionByName(savedObject.className) as Class)();
newObject.x = savedObject.x;
newObject.y = savedObject.y;
newObject.rotation = savedObject.rotation;
newObject.scaleX = savedObject.scaleX;
newObject.scaleY = savedObject.scaleY;
addChild(newObject);
}
}
Related
I have a preload movie that loads an interface. In the interface I have many buttons. When I click on a button I want to load a text file into a movie called "TextMovie.swf". I want to be able to click on a button, take the name of the button and load a text file that is the same name as the button instance, and have the window appear close to the mouse click. There will be hundreds of buttons and hundreds of text files. I'm sure this is easy for some. Here is the loader for a button:
button_1.addEventListener(MouseEvent.CLICK, fl_ClickToPosition_2);
function fl_ClickToPosition_2(event:MouseEvent):void
{
var my2ndLoader:Loader = new Loader();
var url2:URLRequest = new URLRequest("TextMovie.swf");
my2ndLoader.load(url2);
addChild(my2ndLoader);
my2ndLoader.x = 200;
my2ndLoader.y = 10;
}
Then in the TextMovie.swf I have:
import flash.net.URLLoader;
import flash.events.Event;
import flash.net.URLRequest;
import flash.text.StyleSheet;
var css:StyleSheet = new StyleSheet();
css.setStyle("a:hover", {textDecoration:"underline"});
var textLoader: URLLoader = new URLLoader();
textLoader.dataFormat = URLLoaderDataFormat.VARIABLES;
textLoader.addEventListener(Event.COMPLETE, textLoaded);
loadText();
function textLoaded(evt: Event): void {
title_txt.htmlText = textLoader.data.txtTitle;
main_txt.htmlText = textLoader.data.txtBody;
URL_txt.htmlText = textLoader.data.txtURL;
URL_txt.styleSheet = css
}
function loadText(): void {
// var fileName: String = "assets/test_01.txt";
var fileName: String = "assets/" + + ".txt";
textLoader.load(new URLRequest(fileName));
}
I'm not sure how to get the name of the button I pushed and apply it to the string that will load the correct text file. I also can't get the window to appear close to the mouse click. Right now I just have it set with an x and y for testing.
I know it's a lot but thanks for any help.
I suggest you take a look at the docs for MouseEvent so you better understand what data is passed as parameters when the event is triggered.
In the event handler, a reference is passed to the object which was clicked: the event target– and from that you can get its name, location, etc. This is good because it means you only need one event handler for all your buttons.
Once you have the name, you can build the string you need for your url. (I wouldn't do it that way if it is a project which is going to have any sort of lifespan but it's an ok approach).
You can get the x and y location of the target, its width and height, and use that data to position your window. The localX and localY properties refer to the click location relative to the rect of the target (button) – with top/left of the target== 0,0.
This is the trace output from the function below:
red red.swf
600 400
25 8.5
button_1.addEventListener(MouseEvent.CLICK, onHandleButtonClick);
function onHandleButtonClick(event:MouseEvent):void
{
var btnName:String = event.target.name;
var swfName:String = btnName + ".swf";
trace(btnName, swfName);
trace(event.target.x, event.target.y);
trace(event.localX, event.localY);
}
This question already has an answer here:
Flash AS3: saving/loading the positions of all the children of a MovieClip
(1 answer)
Closed 6 years ago.
I have a drag and drop project where the user can drag copies of a MovieClip,
and I would like to know how to save and load the position of the draggable copies
using the SharedObject class. I currently use SharedObject with a save and a load
button. But it only saves the position of the last dragged MovieClip copy.
How do I save the position of the MovieClip copies on stage, so that when
I click the load button it loads the position of all copies as they were positioned when
I clicked the save button?
Code:
latestClone.addEventListener(MouseEvent.MOUSE_DOWN, onCloneClick);
function onCloneClick(e:MouseEvent):void
{
var target:MovieClip = e.currentTarget as MovieClip;
latestClone = target;
}
}
save.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler);
function fl_MouseClickHandler(event:MouseEvent):void
var mySo:SharedObject = SharedObject.getLocal("SaveData");
mySo.data.my_x = latestClone.x;
mySo.data.my_y = latestClone.y;
mySo.flush();
}
loader.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_2);
function fl_MouseClickHandler_2(event:MouseEvent):void
{
var mySo:SharedObject = SharedObject.getLocal("SaveData");
latestClone.x = mySo.data.my_x;
latestClone.y = mySo.data.my_y;
}
Using SharedObject does mean that your app is not in charge of the data persistence, the OS or the user can remove that data or even not allow that data at all at any time.
So first problem, your code is using that data like it will always exist. Wrong way, first thing to do is check if that data is available, ex:
var globaldata:Object;
var data:Object = SharedObject.getLocal("SaveData").data;
if(data["mydata"] != undefined)
{
//there's some data here
globaldata = data["mydata"];
}
else
{
//there's no data
globaldata = {};
}
Next keep a global object and put your values in it:
globaldata["mc1"] = {x:mc1.x, y:mc1.y};
globaldata["mc2"] = {x:mc2.x, y:mc2.y};
//etc .....
data["mydata"] = globaldata;
//no need to call flush
Now you are ready to use that data if it's still there:
mc1.x = data["mydata"]["mc1"].x;
//etc ....
I have a simple horizontal gallery of 24 pictures.
The idea is to show 3 pictures on the screen from all (24) and the middle one to be shown above as the big one automaticly ( like the scheme ). This means that when some of the arrows is pressed the movement of the images have to be by one. Also they have to be auto looped. So the first one can go in the middle to.
Here is my code so far. ( I didn't put the all 24 thumbnails, here are only 3 thumbnails only for the tests ) I've managed to move the thumbnails and select a big picture. But I need it to be auto selected when comes to the middle.
buttonL1_btn.addEventListener(MouseEvent.CLICK, left);
buttonR1_btn.addEventListener(MouseEvent.CLICK, right);
function left(event:Event):void{
box_mc.x -=50;
}
function right(event:Event):void{
box_mc.x +=50;
}
//imgs
img_3_big.visible = false;
box_mc.img_1.addEventListener(MouseEvent.CLICK, img1_show);
function img1_show(e:MouseEvent):void {
img_3_big.visible = true;
img_3_big.gotoAndStop(3);
}
box_mc.img_2.addEventListener(MouseEvent.CLICK, img2_show);
function img2_show(e:MouseEvent):void {
img_3_big.visible = true;
img_3_big.gotoAndStop(2);
}
box_mc.img_3.addEventListener(MouseEvent.CLICK, img3_show);
function img3_show(e:MouseEvent):void {
img_3_big.visible = true;
img_3_big.gotoAndStop(1);
}
Although a different approach than what you have already done, the way I would do it is by keeping all 24 images outside of the .swf in their own image files and keeping track of the current image index. Something similar to this (untested code):
import flash.display.Bitmap;
import flash.display.Loader;
import flash.events.MouseEvent;
var allImagePaths:Vector.<String> = new Vector.<String>(); // Holds reference to all big images
var allImageThumbPaths:Vector.<String> = new Vector.<String>(); // Holds reference to all thumbnail images
var currentImageIndex:int = 0; // Keeps track of what image is currently selected
// Loaders that would load big image and thumbnails
var loaderMain:Loader;
var loaderThumb_Left:Loader;
var loaderThumb_Middle:Loader;
var loaderThumb_Right:Loader;
SetupGallery(); // initialize gallery
function SetupGallery()
{
// Setup image loaders
loaderMain = new Loader();
loaderThumb_Left = new Loader();
loaderThumb_Middle = new Loader();
loaderThumb_Right = new Loader();
// Add loaders to existing MovieClips on stage.
imageHolderMain_mc.addChild(loaderMain);
imageHolderThumbLeft_mc.addChild(loaderThumb_Left);
imageHolderThumbMiddle_mc.addChild(loaderThumb_Middle);
imageHolderThumbRight_mc.addChild(loaderThumb_Right);
// Load image paths into a vector collection (like an array)
loadImages();
// Setup arrow button listeners
buttonL1_btn.addEventListener(MouseEvent.CLICK, showImageLeft);
buttonR1_btn.addEventListener(MouseEvent.CLICK, showImageRight);
}
function loadImages()
{
// Initialize collection of images for easier looping/displaying
allImagePaths = new Vector.<String>();
allImageThumbPaths = new Vector.<String>();
// Load up all image paths into the image array(vector).
allImagePaths.push("image1.jpg");
allImageThumbPaths.push("image1_thumb.jpg");
// ... image2 - image23 ...
allImagePaths.push("image24.jpg");
allImageThumbPaths.push("image24_thumb.jpg");
// NOTE: Consider loading paths to image files via xml
}
function showImageLeft(evt:MouseEvent)
{
currentImageIndex--;
showCurrentImage();
}
function showImageRight(evt:MouseEvent)
{
currentImageIndex++;
showCurrentImage();
}
// Call this after each arrow click, direction will be determined by currentImageIndex
function showCurrentImage()
{
// Keep current index within bounds, if out of range then "loop back"
if(currentImageIndex < 0) currentImageIndex = allImagePaths.length - 1;
if(currentImageIndex > allImagePaths.length - 1) currentImageIndex = 0;
// Set right and left thumbnail indexes based on current image
// (Assuming my logic is correct of course :)
var indexRight:int = currentImageIndex - 1;
if(indexRight < 0) indexRight = allImagePaths.length - 1; // "Loop back" if needed
var indexLeft:int = currentImageIndex + 1;
if(indexLeft > allImagePaths.length - 1) indexLeft = 0; // "Loop back" if needed
// clear out any existing images
loaderMain.unload();
loaderThumb_Left.unload();
loaderThumb_Middle.unload();
loaderThumb_Right.unload();
// set correct images based on new indexes
loaderMain.load(allImagePaths[currentImageIndex]);
loaderThumb_Left.load(allImageThumbPaths[indexLeft]);
loaderThumb_Middle.load(allImageThumbPaths[currentImageIndex]);
loaderThumb_Right.load(allImageThumbPaths[indexRight]);
}
There may be some additional things you'll have to handle, but that's the general idea.
Going with this approach you will not deal with keyframes, in fact you would want to have only one keyframe where the AS code is located and you would want to remove all images from the .swf file itself.
This approach will:
Allow for "Looping back" of images when at the end or beginning of the gallery.
Make it easier to add or remove images in the future.
Improve initial load time for the end user.
I am using following function to Save the output from SWF and send it to a PHP page so that it creates JPG, my problem if there is not INTERNET connection and the SWF is in a CDROM can the Save function be used in FLASH so that it outputs JPG and save it on a computer.
In short can we Save movieclip output as an JPG
/**
Screenshot and jpg output
**/
import flash.display.BitmapData;
import flash.geom.Matrix;
//Buttons handlers. Should add an extra function because delegate doesn't allow to pass parameters
shaF.onPress = mx.utils.Delegate.create(this,makeShadow);
//Helper functions to pass parameters
function makeShadow() { capture(0) }
/*
create a function that takes a snapshot of the Video object whenever it is called
and shows in different clips
*/
function capture(nr){
this["snapshot"+nr] = new BitmapData(abc._width,abc._height);
//the bitmap object with no transformations applied
this["snapshot"+nr].draw(abc,new Matrix());
var t:MovieClip = createEmptyMovieClip("bitmap_mc"+nr,nr);
//positions clip in correct place
//t._x = 350; t._y = 10+(nr*130); t._xscale = t._yscale = 50
//display the specified bitmap object inside the movie clip
t.attachBitmap(this["snapshot"+nr],1);
output(nr);
//attachMovie("print_but", "bot"+nr, 100+nr, {_x:t._x+t._width+50, _y:t._y+t._height/2})
}
//Create a new bitmapdata, resize it 50 %, pass image data to a server script
// using a LoadVars object (large packet)
function output(nr){
//Here we will copy pixels data
var pixels:Array = new Array()
//Create a new BitmapData
var snap = new BitmapData(this["snapshot"+nr].width, this["snapshot"+nr].height);
//Matrix to scale the new image
myMatrix = new Matrix();
myMatrix.scale(1, 1)
//Copy image
snap.draw(this["snapshot"+nr], myMatrix);
var w:Number = snap.width, tmp
var h:Number = snap.height
//Build pixels array
for(var a=0; a<=w; a++){
for(var b=0; b<=h; b++){
tmp = snap.getPixel32(a, b).toString(16)
//if(tmp == "-fcffff")
//{
//tmp="-ff0000";
//}
pixels.push(tmp.substr(1))
}
}
//Create the LoadVars object and pass data to PHP script
var output:LoadVars = new LoadVars()
output.img = pixels.toString()
output.height = h
output.width = w
//The page (and this movie itself) should be in a server to work
output.send("show.php", "output", "POST")
}
stop()
Since you already have the BitmapData, this is fairly easy. Modifying your output function:
//Copy image
snap.draw(this["snapshot"+nr], myMatrix);
//Now check if we want to save as JPG instead of sending data to the server
if (runningFromCdrom) {
var encoder:JPEGEncoder = new JPEGEncoder();
var bytes:ByteArray = encoder.encode(snap);
var file:FileReference = new FileReference();
file.save(bytes);
} else {
// ...the rest of your output method...
}
How to determine the runningFromCdrom value is up to you. If the SWF is being run inside an HTML document, the best way to tell if the program is being run from a CD-ROM would be to specify it in the FlashVars.
Yes, you can save JPEG's directly from Flash. There is no need for the intermediate step to PHP.
You can use one of these JPEG encoders:
https://github.com/mikechambers/as3corelib
http://www.switchonthecode.com/tutorials/flex-tutorial-an-asynchronous-jpeg-encoder
I would recommend the second as it can work asynchronously (kind of) which means your UI shouldn't lock up while encoding the JPEG.
I have come a little closer with my project, and still need some help please. I have 5 Flvs which i want to play randomly on this page www.music-puppets.com/
The .fla file I have created contains this code:
var files:Array = [ "Sz01Puppet.flv", "Sz02Puppet.flv", "Sz03Puppet.flv", "Sz04Puppet.flv", "Sz05Puppet.flv" ];
var shuffledFiles:Array = shuffleArray(files);
//quick test
var testTimer:Timer = new Timer(1000);
testTimer.addEventListener(TimerEvent.TIMER,updateFile);
testTimer.start();
function updateFile(event:TimerEvent):void{
if(shuffledFiles.length == 0) shuffledFiles = shuffleArray(files); //all files played, repeat process
trace('play file',shuffledFiles[0]);
shuffledFiles.shift();
}
function shuffleArray(source:Array,clone:Boolean = true):Array {
var output:Array = [];
var input:Array = clone ? [].concat(source) : source; //clone ? preserve orignal items by making a copy for shuffling, or not
while(input.length) output.push(input.splice(int(Math.random() * input.length-1),1) [0]);
return output;
}
This script works. In the Output every flv is listed randomly, and then repeated. Next up i want this AS Script to work with an FLV Component.
But how to I get that to work?
In my library I have the 5 flvs, and flvplayback component.
I dragged the FLVPlayback component to the stage, but I can only add one flv in the source. How do I get my working actionscript to work with the FLVPlayback component.
Here you can see how my screen looks like.
capture01.jpg
capture02.jpg
Would be great to get some feedback :)
First of all you need to give your FLVPlayback component an instance name using the properties panel. That will let you talk to it from the code.
Next you will need to add a listener to the FLVPlayback component to detect when each video finishes. Let's assume you've given it an instance name of myVideo:
myVideo.addEventListener(VideoEvent.COMPLETE,onVideoComplete);
You will need a handler function for this, and that will look similar to the function you currently use for each iteration of your timer:
function onVideoComplete(event:VideoEvent):void {
if(shuffledFiles.length == 0) shuffledFiles = shuffleArray(files); //all files played, repeat process
myVideo.source = shuffledFiles.shift(); //grab the first array item to play in the FLVPlayback component
}
Finally, make sure that your FLVPlayback component is set to play a new source automatically, then assign the first video to get things going:
myVideo.autoPlay = true;
myVideo.source = shuffledFiles.shift();
If anyone is interested. I have got the running code :)
import flash.events.Event;
import fl.video.*;
var files:Array;
var shuffledFiles:Array;
loaderInfo.addEventListener(Event.COMPLETE,ready);
function ready(event:Event):void{
loaderInfo.removeEventListener(Event.COMPLETE,ready);
//swf rescale setup
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.addEventListener(Event.RESIZE,stageResized);
//get FlashVars - a string converted into an Array by spliting it on the , character
//if the files FlashVar is setup correctly use the data, else use default values
if(loaderInfo.parameters.files != undefined) files = loaderInfo.parameters.files.indexOf(',') > 0 ? loaderInfo.parameters.files.split(",") : [loaderInfo.parameters.files];
else files = [ "Sz01Puppet.flv", "Sz02Puppet.flv", "Sz03Puppet.flv", "Sz04Puppet.flv", "Sz05Puppet.flv" ];
shuffledFiles = shuffleArray(files);
//play the 1st video
videoPlayer.source = shuffledFiles[0];
shuffledFiles.shift();
//see when the video finished playing
videoPlayer.addEventListener(VideoEvent.COMPLETE,videoFinished);
}
function videoFinished(event:VideoEvent):void{
if(shuffledFiles.length == 0) shuffledFiles = shuffleArray(files);//all files played, repeat process
videoPlayer.source = shuffledFiles[0];//play the first video in the random list
videoPlayer.play();
trace('playing',shuffledFiles[0]);
shuffledFiles.shift();//remove the first video from the random list (e.g. [2,0,1].shift() becomes [0,1])
}
function stageResized(event:Event):void{
videoPlayer.width = stage.stageWidth;
videoPlayer.height = stage.stageHeight;
}
function shuffleArray(source:Array,clone:Boolean = true):Array {
var output:Array = [];
var input:Array = clone ? [].concat(source) : source;//clone ? preserve orignal items by making a copy for shuffling, or not
while(input.length) output.push(input.splice(int(Math.random() * input.length-1),1)[0]);
return output;
}