For the Note animation, I coded three events: mouseenter, mousemove, and compositionReady.
mouseenter
This event captures the mouse coordinates as it enters the Stage, whether from outside of the Stage or on first movement if hovering over the Stage. The mouseover event does likewise. Some articles say to use mouseover rather than mouseenter, but Adobe (in its API link at the beginning of this post) says that to avoid conflict with other elements that may be running at the same time, use mouseenter.
NOTE: As discussed earlier, if you move the mouse too quickly, this event will fail to capture the mouse coordinates and will instead return NaN. This causes problems with mousemove, as it does not understand what numbers it is supposed to use to move the Notes in order to follow the mouse. However, if mouseenter first captures actual coordinates, you can then move the mouse in, out, and around as much as you like and all will be well.
As with the clickable image, problems could arise if the timeline deals with mouse events before their time. The following code prevents the timeline from even attempting to detect the mouse until it reaches the correct Label:
var startLabel = sym.getLabelPosition("NoteStart");
if (sym.getPosition() >= startLabel) {
this.onMouseEnter( e.pageX, e.pageY );
}
When the timeline does reach the correct label, this code sends the mouse coordinates to a function defined in compositionReady.
mousemove
This event captures the mouse coordinates as it moves across the Stage. It has similar error-checking to mouseenter and likewise sends its results to a function defined in compositionready.
var startLabel = sym.getLabelPosition("NoteStart");
if (sym.getPosition() >= startLabel) {
this.onMove( e.pageX, e.pageY );
}
compositionReady
This event runs when the project is fully loaded. For this project, it defines three functions: onMouseEnter(mouseX, mouseY), onMove(mouseX, mouseY), and kill(type).
onMouseEnter receives the mouse coordinates as it enters the Stage. It:
1) Gets the Stage width and subtracts the width of a Note object. This value acts to offset the right Stage margin in order to keep the moveable Notes in bounds on the right side.
2) Gets the Stage height. This value helps determine a Note’s current playback time relative to its physical position on the Stage. This then helps adjust how slowly the Note reacts to the mouse’s up/down movements.
3) Gets the initial mouse X/Y values and the initial Note_2 X value, to help set up the first time the Notes follow the mouse.
onMove receives the mouse coordinates every time the mouse moves across the Stage. It:
1) Loads the initial X values from onMouseEnter.
2) Calculates the X distance from where the mouse entered the Stage to where the mouse is now. Calculates how far L-R each Note should move relative to that difference. (At the current setting, each Note moves 1/3 as far as the mouse.) Assigns new X values to each Note. This all turns the mouse entry point into a “magnet”: The farther you move the mouse away from it, the slower the Notes will follow, and vice versa. Moving the mouse off and then back on the Stage resets the “magnet.” This makes it so that you have to move in and out of the Stage several times in order to move the Notes completely from one side of the Stage to the other. However, this also makes it very smooth and more interesting.
3) Checks that Note 2’s X value is at least 0 to keep all even Notes inside the left Stage boundary. Checks that Note 8’s X value is no more than the modified Stage width calculated earlier, to keep all even Notes inside the right Stage boundary. Moves the Notes along the X axis.
4) Loads the initial Y values from onMouseEnter.
5) Calculates the current timeline positions of each of the even Notes. Checks if any of them are NaN and if so, assigns 0 (start of timeline). Gets the Y difference from where the mouse was last to where it is now. (Normally, this will be either 0 (no Y movement) or 1px.) Uses that difference, the Stage height, and a hard-coded value of 3000 (representing the minimum number of ms it takes each Note to fall) to calculate how far to move the Note along its own timeline from its current timeline position. (At the current setting, each Note moves back and forth along its timeline, which is the same as up and down physically, 1/3 as far as the mouse.) Moves each Note back or forth along its timeline, i.e. the Y-axis, and restarts playback of each Note from that respective point.
6) Saves the new mouse Y position for the next round.
kill(type) discovers if the device the viewer is using is touch-enabled, and if so prevents either mouseenter or mousemove from firing.
Here is the code for compositionReady:
compositionReady
// insert code to be run when the composition is fully loaded here
// Control the timeline with the mouse cursor - Get the tutorial here http://tinyurl.com/cxws32a
// Per http://www.adobe.com/devnet-docs/edgeanimate/api/current/index.html
// "When using mouseover on a symbol, child elements of the symbol
// may interrupt the mouse event. Use mouseenter instead to avoid
// the conflict."
this.onMouseEnter = function( mouseX, mouseY ){
// Get initial Stage width, modified by Note width.
// Use to prevent right-most Note from going out of bounds,
// regardless of Responsive Scaling adjustments.
var note_2W = parseInt(sym.$("Note_2_SYM").css("width"), 10);
var stageW = parseInt(sym.$("Stage").css("width"), 10) - note_2W;
sym.setVariable("stageW_G", stageW);
// Get initial Stage height.
// Use to calculate a Note's current animation time relative to
// its physical fall position down the Stage.
var stageH = parseInt(sym.$("Stage").css("height"), 10);
sym.setVariable("stageH_G", stageH);
// Get initial mouse X/Y positions for Note drag control.
// X-pos is used as a constant.
// Y-pos is set first here and then reused.
sym.setVariable("mouse_InitX_G", mouseX);
sym.setVariable("mouse_SaveY", mouseY);
// Get initial Note_2 X-position for L-R drag control.
var note_2_InitX = parseInt(sym.$("Note_2_SYM").css("left"), 10);
sym.setVariable("note_2_InitX_G", note_2_InitX);
}
this.onMove = function( mouseX, mouseY ){
// Get initial values of X-variables as set when mouse entered Stage.
var stageW = sym.getVariable("stageW_G");
var mouse_InitX = sym.getVariable("mouse_InitX_G");
var note_2_InitX = sym.getVariable("note_2_InitX_G");
// Make Notes follow mouse L-R, but more slowly than mouse.
// - Get distance between initial & current mouse positions.
// - Set Notes to move at a fraction (1/3) of that distance.
// Ex. If mouse enters Stage @ 100px w/Note_4 @ 200px, and
// mouse moves left to 80px (20px diff), Notes will also
// move left but only to 195px (5px diff).
// - Also, enforce Note separations as set up in project.
// Otherwise, every Note will lock onto the same X value
// when the mouse moves.
var mouse_DiffX = mouse_InitX - mouseX;
var note_2_DiffX = mouse_DiffX / 3;
var note_2X = note_2_InitX - note_2_DiffX;
var note_4X = note_2X + 180;
var note_6X = note_4X + 180;
var note_8X = note_6X + 180;
// Move the notes, but keep the outer Notes in bounds.
if (note_2X >= 0 && note_8X <= stageW){
sym.$("Note_2_SYM").css({"left": note_2X});
sym.$("Note_4_SYM").css({"left": note_4X});
sym.$("Note_6_SYM").css({"left": note_6X});
sym.$("Note_8_SYM").css({"left": note_8X});
}
// Get initial values of Y-variables as set when mouse entered Stage.
var stageH = sym.getVariable("stageH_G");
var mouse_OldY = sym.getVariable("mouse_SaveY");
// Make Notes follow mouse T-B, but more slowly than mouse.
// - Get current Timeline position.
// Will use this to calculate the Y-follow-mouse position.
// - If the mouse is INSIDE the Stage when the animation
// starts, animation MAY lock up when mouse is first
// moved if mouse if moved too quickly. The Timeline
// position ends up as NaN. If so, set Note animation
// to 0 (beginning of timeline).
var note_2_TimelineY = sym.getSymbol("Note_2_SYM").getPosition();
var note_4_TimelineY = sym.getSymbol("Note_4_SYM").getPosition();
var note_6_TimelineY = sym.getSymbol("Note_6_SYM").getPosition();
var note_8_TimelineY = sym.getSymbol("Note_8_SYM").getPosition();
if (isNaN(note_2_TimelineY)){
note_2_TimelineY = 0;
}
if (isNaN(note_4_TimelineY)){
note_4_TimelineY = 0;
}
if (isNaN(note_6_TimelineY)){
note_6_TimelineY = 0;
}
if (isNaN(note_8_TimelineY)){
note_8_TimelineY = 0;
}
// - Get distance between previous & current mouse positions.
// - Set Notes to move at a fraction (1/3) of the mouse distance.
// Get ratio of animation (each Note takes at least 3000ms
// to fall) to Stage height, multiply by difference in mouse
// positions to move Notes by the same relative amount, but
// a fraction as much (1/3) to make it slower.
// - Restart the Note's Timeline at that point.
// Effect is as though the Note follows the mouse but
// continues playing on its merry way.
var mouse_DiffY = mouseY - mouse_OldY;
var note_2_DiffY = ((3000 / stageH) * mouse_DiffY / 3);
sym.getSymbol("Note_2_SYM").play(note_2_TimelineY + note_2_DiffY);
sym.getSymbol("Note_4_SYM").play(note_4_TimelineY + note_2_DiffY);
sym.getSymbol("Note_6_SYM").play(note_6_TimelineY + note_2_DiffY);
sym.getSymbol("Note_8_SYM").play(note_8_TimelineY + note_2_DiffY);
// Save the new mouse Y position.
sym.setVariable("mouse_SaveY", mouseY);
}
// Disable mousemove event for mobile.
// From the Two Birds project at:
// http://html.adobe.com/edge/animate/showcase/birds/
var isTouch = ('ontouchstart' in window);
function kill(type){
window.document.body.addEventListener(type, function(e){
e.preventDefault();
e.stopPropagation();
return false;
}, true);
}
if( isTouch ){
kill('mouseenter');
kill('mousemove');
}