Record Paper.js path object and redraw again later - html

I draw with a mouse Paper.js. I need to keep these strokes and replay them at the same rate as in the video replay. How can I accomplish this?

In paper.js, the onFrame() function is called up to 60 times per second, while the onMouseMove() function "is called when the mouse moves within the project view", and contains the position of the mouse. By using both functions you can store the mouse motions and replay them later with close to the same time between positions.
var mousePosition = null;
function onMouseMove(event) {
if (mousePosition != null) {
var path = new Path();
path.strokeColor = 'black';
path.moveTo(mousePosition);
path.lineTo(event.point);
}
mousePosition = event.point;
}
var recordedPositions = [];
var delayFrames = 60;
function onFrame(event) {
if (mousePosition != null) {
recordedPositions.push(mousePosition);
if (recordedPositions.length > delayFrames) {
var path = new Path();
path.strokeColor = 'red';
delayedPositionIndex = recordedPositions.length - delayFrames;
path.moveTo(recordedPositions[delayedPositionIndex - 1]);
path.lineTo(recordedPositions[delayedPositionIndex]);
}
}
}
I do not know the timing accuracy/resolution/dependability of onFrame(). Alternatively you could just use javascript timing events as in this answer: How can I use javascript timing to control on mouse stop and on mouse move events

Related

How to apply drag and drop function to all frames in movie clip? Actionscript3

Each frame contain 1 text field. I apply the code on timeline.
But it only gets applied to the last object, which means that I can only drag and drop the last object. Why?
How can I improve this so that I can drag and drop all objects?
for(var j:uint=0; j<3; j++)
{
var q:Ans = new Ans();
q.stop();
q.x = j * 300+50;// set position
q.y = 500;
var r:uint = Math.floor(Math.random() * q_list.length);
q.qface = q_list[r];// assign face to card
q_list.splice(r,1);// remove face from list;
q.gotoAndStop(q.qface+1);
q.addEventListener(MouseEvent.MOUSE_DOWN, startAnsDrag);
q.addEventListener(MouseEvent.MOUSE_UP, stopAnsDrag);
q.addEventListener(Event.ENTER_FRAME, dragAns);
addChild(q);// show the card
}
//----------------------------drag
// offset between sprite location and click
var clickOffset:Point = null;
// user clicked
function startAnsDrag(event:MouseEvent) :void
{
clickOffset = new Point(event.localX, event.localY);
}
// user released
function stopAnsDrag(event:MouseEvent) :void
{
clickOffset = null;
}
// run every frame
function dragAns(event:Event) :void
{
if (clickOffset != null)
{ // must be dragging
q.x = clickOffset.x+mouseX+135;
q.y = clickOffset.y+mouseY;
}
}
Make a new layer in the timeline just for your drag-and-drop code, which you can remove from your other actionscript. Put the code on the first frame in that layer. Now click on and select the last frame on that layer in which you want the code to be effective (probably the last frame of the MovieClip). Press F5 to draw-out the range of frames which will be affected by the code. Voila!

How to slowly move item up and down?

I need to do simple thing, by clicking button 1 time, slowly move item1 by 100 px up and move down by 100 px. I've tried this, but item1 immediately increasing by 50px and immediately decreasing by 50px, I need to make It slower.
var moving:Boolean = false;
if(!moving){
item1.y -= 50;
moving = true;
}
else {
item1.y += 50;
moving = false;
}
You could set up a max moving value like 50, and then move the item1 on the Y with 1, and decrease that max value by 1. When the max value reaches 0, the item reached it's destiny.
if(!moving){
item1.y-=1;
maxValue--;
if(maxValue==0){
//reached final position
}
}
Use a Timer object. Declare that object inside of your class. Then, when the button has been clicked, set the object to a new instance of a Timer that will run very quickly and for many iterations, add an event listener to your new Timer, and in the event listener, apply much smaller increments or decrements to item1.y. For example:
private var m_tmr:Timer;
private function buttonClickHandler(pEvent:MouseEvent):void
{
// This is the function that's called when the button's clicked.
if (m_tmr == null)
{
m_tmr = new Timer(200, 0);
m_tmr.addEventListener(TimerEvent.TIMER, onTimer);
m_tmr.start();
}
}
private function onTimer(pEvent:TimerEvent):void
{
// The first several times this function is called (should be around every
// 200 milliseconds), increment item1.y by 1 or 2 or something else small.
// After the first many times, start decrementing item1.y by the same amount.
// Then call m_tmr.removeEventListener(TimerEvent.TIMER, onTimer);
}
You have to tell Flash to re-draw the screen after each incremental move. An excellent way to do this is with an ENTER_FRAME loop:
var moving:Boolean = false;
const initY = item1.y; // your starting y value
const limitY = initY - 100; // your move will end here
if(!moving){
moving = true;
addEventListener(Event.ENTER_FRAME,moveit)
function moveit(e)
{
item1.y -=1
if (item1.y < limitY)
removeEventListener(Event.ENTER_FRAME,moveit)
}
you have to removeEventListener(...) once you've got to where you want to be, otherwise the loop will go on and hog memory and performance.
UPDATE
So, to move up on a mouse click, you'd do this:
var moving:Boolean = false;
const initY = item1.y; // your starting y value
const limitY = initY - 100; // your move will end here
stage.addEventListener(MouseEvent.CLICK, moveUp)
function moveUp(e)
{
stage.removeEventListener(MouseEvent.CLICK, moveUp)
if(!moving){
moving = true;
addEventListener(Event.ENTER_FRAME,moveit)
function moveit(e)
{
item1.y -=1;
if (item1.y < limitY)
{
removeEventListener(Event.ENTER_FRAME,moveit);
item1.y = limitY;
moving = false;
}
}
}
Instead of targeting stage you may just want to target your button when you use the addEventListener method to register the listener function with the mouse click.
To move back to the start position, apply the same idea to another button or another MouseEvent. For instance you could move up on MOUSE_DOWN and move down on MOUSE_UP.
There are more sophisticated things you can do inside the listener functions (in this case the moving functions). You could apply "easing" to the beginning and ending of the moves so that the motion seems more natural. But, you'll have to read up on that - this answer is too long already!
I would like participate in this conversation. My version of object movement without If statements. Movement is based on trigonometric function:
var objectToAnimate:Shape = new Shape();
objectToAnimate.graphics.beginFill(0x009900);
objectToAnimate.graphics.drawCircle(0, 0, 20);
addChild(objectToAnimate);
//Place it somewhere
objectToAnimate.x = objectToAnimate.y = 200;
//Config for movement
var step:Number = 1; //really slow... 1° per frame
var maxOffsetY:Number = -100; //Move object maximum on 100px top
var cursor:Number = -90;
var position: Number = objectToAnimate.y; // catch current position
var timer:Timer = new Timer(30, 180);
timer.addEventListener(TimerEvent.TIMER, updateAnimation);
timer.start();
function updateAnimation(e:TimerEvent):void {
objectToAnimate.y = position + Math.cos(cursor * Math.PI / 180) * maxOffsetY;
cursor += step;
}

as3: Mute button and volume slider on one sound channel

I have truly exhausted all my knowledge on this problem so I hope that my peers will be able to help me?
I am building a audio mixer that has five tracks with a volume slider and mute button per track. The reason for a mute button as opposed to a start/stop button per track is so that all the samples will be in sync regardless of when a sample is introduced.
The app has global start, stop and pause buttons which all function normally but I cannot get the volume slider and mute button to work in tandem on an individual sound channel.
The volume slider and the mute button will both work if I comment out the other function but when both are in play then only the volume slider works.
I'm guessing that there is a conflict because I have two separate variables using the soundTransform object/class but maybe you can shed some light on this conundrum?
Here is my code for one track... Any help appricated.
var mySound1:Sound1 = new Sound1();
var myChannel1:SoundChannel = new SoundChannel();
var volumeAdjust1:SoundTransform = new SoundTransform();
volumeAdjust1.volume = 0;
mute_btn1.stop();
mute_btn1.addEventListener(MouseEvent.CLICK,togglemute_btn1);
var Mute1:Boolean = false;
function togglemute_btn1(event:MouseEvent)
{
if (Mute1)
{
mute_btn1.gotoAndStop(1);
volumeAdjust1.volume = 1;
myChannel1.soundTransform = volumeAdjust1;
Mute1 = false;
}
else
{
mute_btn1.gotoAndStop(2)
volumeAdjust1.volume = 0;
myChannel1.soundTransform = volumeAdjust1;
Mute1 = true;
}
}
/*if the section below is commented out then the mute_btn1 works fine
otherwise the volume slider functions are dominent*/
var dragging1:Boolean = false;
var mySliderLength1:uint = 300;
var boundingBox1:Rectangle = new Rectangle(0,0,0,mySliderLength1);
slider_mc1.knob_mc1.addEventListener(MouseEvent.MOUSE_DOWN, dragKnob1);
stage.addEventListener(MouseEvent.MOUSE_UP, releaseKnob1);
slider_mc1.knob_mc1.buttonMode = true;
function dragKnob1(myEvent:Event):void
{
slider_mc1.knob_mc1.startDrag(false, boundingBox1);
dragging1 = true;
slider_mc1.knob_mc1.addEventListener(Event.ENTER_FRAME, adjustVolume1);
}
function releaseKnob1(myEvent:Event):void
{
if (dragging1)
{
slider_mc1.knob_mc1.stopDrag();
dragging1 = false;
}
}
function adjustVolume1(myEvent:Event):void
{
var myVolume1:Number = slider_mc1.knob_mc1.y / mySliderLength1;
var myTransform1:SoundTransform = new SoundTransform(myVolume1);
if (myChannel1!=null)
{
myChannel1.soundTransform = myTransform1;
}
}
You should check your Mute1 variable in that listener of yours, and if muted, then volume=0, otherwise volume is calculated. And indeed, do remove your enter frame listener at the point of stopDrag() call.
function dragKnob1(myEvent:Event):void
{
slider_mc1.knob_mc1.startDrag(false, boundingBox1);
dragging1 = true;
slider_mc1.knob_mc1.addEventListener(Event.ENTER_FRAME, adjustVolume1);
}
function releaseKnob1(myEvent:Event):void
{
if (dragging1)
{
slider_mc1.knob_mc1.stopDrag();
dragging1 = false;
slider_mc1.knob_mc1.removeEventListener(Event.ENTER_FRAME, adjustVolume1);
// ^ this line added
}
}
function adjustVolume1(myEvent:Event):void
{
if (Mute1) return;
// ^ and this line added
var myVolume1:Number = slider_mc1.knob_mc1.y / mySliderLength1;
var myTransform1:SoundTransform = new SoundTransform(myVolume1);
if (myChannel1!=null)
{
myChannel1.soundTransform = myTransform1;
}
}
I believe your issue is you keep adding the Enter_Frame listener every time the mouse is clicked but it never gets removed. So even after you let go of the knob the adjustVolume1 function is still getting called (which messes up anything the mute function call is doing on the frame after the mute toggle function is called).
So how I think I would deal with this given the current state is move the Enter_Frame listener addition outside of the dragKnob function and in the adjustVolume1 handler just check if dragging1 is true otherwise just return.
slider_mc1.knob_mc1.addEventListener(Event.ENTER_FRAME, adjustVolume1);
function dragKnob1(myEvent:Event):void
{
...
}
function adjustVolume1(myEvent:Event):void
{
if(!dragging1)
return;
...
}

timerEvent within enterFrame function?

I'm calling a timerEvent function from enterFrame, it's running the timerEvent function on everyFrame. Is there a way to control it?
I've got my bullets firing function with timerEvent of 500, so it shoots a bullet every half a second on 24fps. It's working fine for now. Now I want to change bullet speed and skin with respect to weapon.
////////////////////////////////
//this function is called first time within an EnterFrame function
///////////////////////////////
function weaponCheck():void
{
switch (weaponState)
{
case STATE_GUN :
gun();
break;
case STATE_DOUBLE_GUN :
doubleGun();
break;
}
}
function gun():void
{
trace("single gun");
laserTimer = new Timer(600);
laserTimer.addEventListener(TimerEvent.TIMER, timerListener);
laserTimer.start();
function timerListener(e:TimerEvent):void
{
var tempLaser:MovieClip = new Laser();
var tempGunBlast:MovieClip = new Gun_blast_01();
tempLaser.x = player.x +((player.width/2)+12);
tempLaser.y = player.y;
tempGunBlast.x = stage.mouseX + 104;
tempGunBlast.y = tempLaser.y;
Lasers.push(tempLaser);
addChildAt(tempLaser,1);
addChildAt(tempGunBlast, 3);
if (tempGunBlast.currentFrame >= tempGunBlast.totalFrames)
{
removeChild(tempGunBlast);
}
}
}
function doubleGun():void
{
trace("Double gun");
doubleGunTimer = new Timer(400);
doubleGunTimer.addEventListener(TimerEvent.TIMER, timerListener2);
doubleGunTimer.start();
function timerListener2(e:TimerEvent):void
{
var tempLaser:MovieClip = new doubleG();
var tempGunBlast:MovieClip = new Gun_blast_01();
tempLaser.x = player.x +((player.width/2)+12);
tempLaser.y = player.y;
tempGunBlast.x = stage.mouseX + 104;
tempGunBlast.y = tempLaser.y;
Lasers.push(tempLaser);
addChildAt(tempLaser,1);
addChildAt(tempGunBlast, 3);
if (tempGunBlast.currentFrame >= tempGunBlast.totalFrames)
{
removeChild(tempGunBlast);
}
}
}
///////////////////////////////////////////////////
//Following function is called within an enterFrame event function
/////////////////////////////////////////////////
function playGame():void
{
weaponCheck();
blah1();
blah2();
blah3();
testForEnd();
}
function testForEnd():void
{
if (level == 3)
{
laserTimer.stop();
weaponState = STATE_DOUBLE_GUN;
weaponCheck();
}
}
So when the game runs for first time, it works fine and uses the timer event of 600 to hit the bullets, but when level == 3 and weaponState changes, the 2nd firing function doubleGun(); is called but it starts to fire the bullets on a per frame count, not on a controlled timerEvent. Please Help. Thanks.
Why don't you drop timers and use enter frame listener as a manner to count time? You are already calling weaponCheck() from an enterframe listener. Make it so that the actual gun() and doublegun() calls will only generate animation, such as firin' mah lazers and blasts, and the main function will just count time.
function weaponCheck():void
{
this.reloading+=this.weaponFiringSpeed; // you alter this to make your weapon fire slower or faster
if (this.reloading<FULLY_RELOADED) return; // we are not yet ready to fire
this.reloading=0;
switch (weaponState) // and here we fire with the gun state
{
case STATE_GUN :
gun();
break;
case STATE_DOUBLE_GUN :
doubleGun();
break;
}
}
function gun():void
{
trace("single gun");
var tempLaser:MovieClip = new Laser();
var tempGunBlast:MovieClip = new Gun_blast_01();
tempLaser.x = player.x +((player.width/2)+12);
tempLaser.y = player.y;
tempGunBlast.x = stage.mouseX + 104;
tempGunBlast.y = tempLaser.y;
Lasers.push(tempLaser);
addChildAt(tempLaser,1);
addChildAt(tempGunBlast, 3);
}
And similarly double gun. FULLY_RELOADED is a constant, reloading is a variable used to track time, it should be a property of the one who's firing.
Note, this approach requires you to manage your "tempGunBlast"s elsewhere, perhaps in the very weaponCheck function, if so, modify it as follows:
function weaponCheck():void
{
if (tempGunBlast) if (tempGunBlast.currentFrame >= tempGunBlast.totalFrames)
removeChild(tempGunBlast);
this.reloading+=this.weaponFiringSpeed; // you alter this to make your weapon fire slower or faster
if (this.reloading<FULLY_RELOADED) return; // we are not yet ready to fire
... // rest of code unchanged
You will most likely not be able to copypastely implement this, but please try.

as3 marquee select drag multiple child objects

Can anyone tell me how to achieve marquee selection effect with AS3 to select multiple movieclips by drawing a dynamic rectangle around them and then drag and drop them anywhere?
Don't use startDrag() if you need multiple objects to be draggable, since it only allows one object to be dragged at a time. Instead, listen for mouse events and do the moving manually:
var oldX:int;
var oldY:int;
var dragging:Boolean = false;
function onMouseDown(evt:MouseEvent):void {
dragging = true;
oldX = evt.stageX;
oldY = evt.stageY;
}
function onMouseMove(evt:MouseEvent):void {
if (!dragging) return;
var dX:int = evt.stageX - oldX;
var dY:int = evt.stageY - oldY;
for (int i = 0; i < selectedClips.length; i++) {
var clip:DisplayObject = selectedClips[i];
clip.x += dX;
clip.y += dY;
}
oldX = evt.stageX;
oldY = evt.stageY;
}
function onMouseUp(evt:MouseEvent):void {
dragging = false;
}
This code assumes that:
Your array of selected objects is called selectedClips.
Your array of selected objects all inherit from DisplayObject.
You have added event listeners on all draggable objects for the MOUSE_DOWN, MOUSE_MOVE, and MOUSE_UP mouse events which call these functions.
If any of those three conditions are not met, update my code or your code to work properly. Also, if you need to do any additional handling when the objects are dropped, you can use the mouse up handler to add custom code.