Flex 4.5.1 choppy animation when using MOUSE_MOVE event to trigger a <s:Move> animation of a TileGroup inside a Canvas
Basic开发者_StackOverflowally what Im trying to do is have a TileGroup component's horizontalCenter and verticalCenter attributes modified inversely of the mouses movement, and use an spark move effect to make that motion be smooth. The TileGroup is a child of a mx:Canvas that is set to be 100% wide and 100% tall. Their are about 20 or so BorderContainers inside of the TileGroup.
For an example of how it should be working, look at http://gallery.artofgregmartin.com/
My version moves just like that one, but its not nearly as smooth. Looking at CPU usage both mine and his use about the same (80-90% when the motion is going) but we differ in GPU usage. Mine only utilizes about 4% while his is near 10%.
Here is my movement code:
private function onMouseMove(event:MouseEvent):void{
var stageCenterX:Number = this.stage.stageWidth/2;
var stageCenterY:Number = this.stage.stageHeight/2;
var sliderPanelCenterX:Number = sliderPanel.width/2;
var sliderPanelCenterY:Number = sliderPanel.height/2;
var mouseX:Number = event.stageX;
var mouseY:Number = event.stageY;
var offsetX:Number = 0;
var offsetY:Number = 0;
var padding:Number = 400;
var multX:Number = (stageCenterX - mouseX)/stageCenterX;
offsetX = (multX * sliderPanelCenterX);
if(mouseX <= stageCenterX){
offsetX = offsetX - Math.abs(multX * stageCenterX) + Math.abs(multX * padding);
}
else {
offsetX = offsetX + Math.abs(multX * stageCenterX) - Math.abs(multX * padding);
}
var multY:Number = (stageCenterY - mouseY)/stageCenterY;
offsetY = (multY * sliderPanelCenterY);
if(mouseY <= stageCenterY){
offsetY = offsetY - Math.abs(multY * stageCenterY) + Math.abs(multY * padding);
}
else {
offsetY = offsetY + Math.abs(multY * stageCenterY) - Math.abs(multY * padding);
}
panelHC = Math.round(offsetX);
panelVC = Math.round(offsetY);
movePanel.captureStartValues();
sliderPanel.verticalCenter = panelVC; //sliderPanel is the id for the TileGroup
sliderPanel.horizontalCenter = panelHC;
movePanel.play();
}
And here is the reverent mxml code:
<fx:Declarations>
<s:Move id="movePanel" target="{sliderPanel}" />
</fx:Declarations>
<mx:Canvas width="100%" height="100%" backgroundColor="#111111"
horizontalScrollPolicy="off"
verticalScrollPolicy="off">
<s:TileGroup id="sliderPanel" horizontalGap="2" verticalGap="2" width="2010"
horizontalCenter="0" verticalCenter="0" z="1" />
</mx:Canvas>
This could happen:
Flash by default updates the screen frame by frame - after the frame. Mouse events can be dispatched multiple times per frame. The screen update uses only the values from the last mouse event dispatched.
You can convince the screen to update immediately by calling the event's updateAfterEvent()
method. If you do so, Flash puts an additional screen update right in your frame.
You can track the screen updates by listening to the RENDER
event of any display object. Here is a test class that shows updateAfterEvent
in action. There are 2 boxes you may hover with the mouse. The left box uses updateAfterEvent()
. The right box updates with the regular screen update. You will see that the left box is immediately updated. Instead, the right box is updated after the frame finishes. Since the frame rate is .5, this can take up to 2 seconds. Also try to moving the mouse within both boxes. The left box will update immediately, the right one only frame by frame.
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.utils.getTimer;
public class UpdateAfterEvent2 extends Sprite {
public var box1 : Sprite;
public var box2 : Sprite;
public function UpdateAfterEvent2() {
stage.frameRate = .5;
box1 = new Sprite();
box1.name = "box1";
box1.x = 10;
box1.y = 10;
colorize(box1, 0xFF0000);
box1.addEventListener(MouseEvent.MOUSE_OVER, mouseOver);
box1.addEventListener(MouseEvent.MOUSE_OUT, mouseOut);
box1.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
addChild(box1);
box2 = new Sprite();
box2.name = "box2";
box2.x = 70;
box2.y = 10;
colorize(box2, 0xFF0000);
box2.addEventListener(MouseEvent.MOUSE_OVER, mouseOver);
box2.addEventListener(MouseEvent.MOUSE_OUT, mouseOut);
box2.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
addChild(box2);
addEventListener(Event.ENTER_FRAME, enterFrame);
addEventListener(Event.EXIT_FRAME, exitFrame);
addEventListener(Event.RENDER, render);
}
private function enterFrame(event : Event) : void {
trace ("----------------enterFrame", getTimer());
}
private function exitFrame(event : Event) : void {
trace ("----------------exitFrame", getTimer());
}
private function mouseMove(event : MouseEvent) : void {
var box : Sprite = event.currentTarget as Sprite;
trace ("move", box.name, getTimer());
if (box == box1) {
trace ("--------UPDATE_AFTER_EVENT");
event.updateAfterEvent();
}
box.alpha = box.alpha == 1 ? .5 : 1;
stage.invalidate();
}
private function mouseOut(event : MouseEvent) : void {
var box : Sprite = event.currentTarget as Sprite;
trace ("out", box.name, getTimer());
if (box == box1) {
trace ("--------UPDATE_AFTER_EVENT");
event.updateAfterEvent();
}
colorize(box, 0xFF0000);
stage.invalidate();
}
private function mouseOver(event : MouseEvent) : void {
var box : Sprite = event.currentTarget as Sprite;
trace ("over", box.name, getTimer());
if (box == box1) {
trace ("--------UPDATE_AFTER_EVENT");
event.updateAfterEvent();
}
colorize(box, 0x0000FF);
stage.invalidate();
}
private function render(event : Event) : void {
trace ("--------RENDER", getTimer());
}
private function colorize(box : Sprite, color : uint) : void {
with (box.graphics) {
clear();
beginFill(color);
drawRect(0, 0, 50, 50);
}
}
}
}
Could it be possible that your effect never applies?
Within the event handler you directly set the verticalCenter
and horizontalCenter
properties to the panel. From what I understand the panel is here not tweened but hard set to the new position.
I bet that commenting out:
// movePanel.captureStartValues();
sliderPanel.verticalCenter = panelVC; //sliderPanel is the id for the TileGroup
sliderPanel.horizontalCenter = panelHC;
// movePanel.play();
will have no effect to your animation.
There are no tween values set to your movePanel
. You should do something like:
movePanel.xBy = ...
movePanel.xTo = ...
movePanel.yBy = ...
movePanel.yTo = ...
...
The docs: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/effects/Move.html
Well it would seem that the built in animation routines are not exactly optimized. I switched all the tweening methods out for TweenMax ( http://www.greensock.com/tweenmax/ ) as well as followed some of the optimization tips on that site and its running smooth as butter now.
Thanks for all the helpful response!
-Nigel
TweenMax method:
Replaced
movePanel.captureStartValues();
movePanel.easer = powerEasing;
sliderPanel.verticalCenter = panelVC;
sliderPanel.horizontalCenter = panelHC;
movePanel.stop();
movePanel.play();
With
TweenMax.to(sliderPanel,2.5,
{verticalCenter:panelVC,horizontalCenter:panelHC, ease:Back.easeOut});
精彩评论