I was thinking this is a simple task, but I'm wrong.
I used a sprite to display an image, and when user drag it(MOUSE_DOWN and MOUSE_MOVE), I got the position in MOUSE_MOVE
and calculated the offsets:
var current: Point = new Point(event.localX, event.localY);
sprite.x = current.x - start.x;
sprite.y = current.y - start.y;
It works but not smooth. Is there a better solution?
UPDATE
After a day of debugging, I finally found the reason.
Bigger fps can make it smoother, but it's not the key of this question.
The key is I should use stage
to listen MOUSE_MOVE
, not the image itself. And when getting the mouse position, I should use event.stageX/Y
(or stage.mouseX/Y
), not event.localX/Y
. The event.localX/Y
from the moving image, is neither stable nor smooth, that causes my problem.
Following is my working code, enjoy it :)
package {
import flash.display.Bitmap;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.net.URLRequest;
import flash.text.TextField;
public class DragIssue extends Sprite {
private static const URL: String = "assets/m1.jpg";
private var self: DragIssue;
private var sprite: Sprite;
private var startPoint: Point;
private var offsetX: Number = 0;
private var offsetY: Number = 0;
public function DragIssue() {
self = this;
init();
loadImage();
}
protected function init(): void {
if (stage == null) {
this.addEventListener(Event.ADDED_TO_STAGE, on_addedToStage);
} else {
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
}
function on_addedToStage(event: Event): void {
self.removeEventListener(Event.ADDED_TO_STAGE, on_addedToStage);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
}
}
public function loadImage(): void {
var loader: Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoaded);
loader.load(new URLRequest(URL));
function imageLoaded(event: Event): void {
var bitmap: Bitmap = event.target.content as Bitmap;
self.sprite = new Sprite();
sprite.addChild(bitmap);
self.addChild(sprite);
sprite.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
sprite.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
}
private function onMouseMove(event: MouseEvent): void {
if (startPoint) {
sprite.x = offsetX + event.stageX - startPoint.x;
sprite.y = offsetY + event.stageY - startPoint.y;
}
}
private function onMouseUp(event: MouseEvent): void {
startPoint = null;
offsetX = sprite.x;
offsetY = sprite.y;
}
private function onMouseDown(event: MouseEvent): void {
startPoint = new Point(event.stageX, event.stageY);
}
}
}
If you're looking for smooth dragging experience, here is a method you could use: (let's say the thing you're dragging is called 'dragee' and this code is in the scope of dragee's parent)
Here, try this. It's just a simple black square, but it looks fine until you really start dragging it around. As was mentioned, setting the framerate to something higher is ideal. In this case, I decided to up the framerate to 60fps in the MOUSE_DOWN and drop it back to 24 in MOUSE_UP for memory reasons. You can obviously change that how you please.
Make sure you are removing the MOUSE_MOVE event on MOUSE_UP. That is key. Otherwise, you re-add the event on every MOUSE_DOWN and end up running the same code repeatedly, simultaneously. Sorry my syntax isn't 100% proper; I threw this together really quick in CS5.5 rather than doing it in Flash Builder.
You must use a high framerate to have smooth movement. The optimal framerate is 60fps because it is the default for most LCD monitors.
I don't have the code under my hand so I put the code here (not tested)
Try calling
event.updateAfterEvent()
in your drag handler. The function tells Flash to redraw the stage immediately instead of waiting til the next frame.