Dart Documentationdartkart.mapPanBehaviour

PanBehaviour class

PanBehaviour controls animated panning of a map viewport.

class PanBehaviour {
 const double ACCELERATION = -2.0; // px / (100ms)²
 const double SPEED = 20.0;        // px / 100ms
 const int DELTA_T = 20;           // ms, duration of animation step

 /// the map viewport controlled by this behaviour
 final MapViewport viewport;
 
 /// Creates a new behaviour controlling [viewport].
 PanBehaviour(this.viewport);

 /**
  * Animate the panning of the map viewport by a vector given
  * by [panBy].
  */
 void animate(Point2D panBy) {
   var dist = math.sqrt(math.pow(panBy.x,2) + math.pow(panBy.y,2));
   var theta = math.asin(panBy.y / dist);
   if (panBy.x <= 0) {
     theta = math.PI - theta;
   }

   var fx = math.cos(theta);
   //y-axis is inverted, therefore not
   //  -math.sin(theta)
   var fy = math.sin(theta);

   now() => new DateTime.now().millisecondsSinceEpoch;

   pan(dx,dy) {
     var p = new Point2D(dx,dy).toInt();
     if (p != new Point2D.origin()) viewport.pan(p);
   }

   bounded(num v, num bound) => bound < 0
       ? math.max(v, bound)
       : math.min(v, bound);

   // pan long distance (fast) as much and possible, the complete
   // the future with the remaining pan distance
   Future<Point2D> panLongDistance(Point2D panBy) {
     var completer = new Completer<Point2D>();
     var pannedX = 0;
     var pannedY = 0;
     bool isLongDistance() =>
             (panBy.x - pannedX).abs() > 100
         || (panBy.y  - pannedY).abs() > 100;

     // animation step
     step(Timer timer){
       if (!isLongDistance()) {
         timer.cancel();
         var rest = new Point2D(panBy.x - pannedX, panBy.y - pannedY);
         completer.complete(rest);
       } else {
         var dx = bounded(40 * fx, panBy.x).toInt();
         var dy = bounded(40 * fy, panBy.y).toInt();
         pannedX += dx;
         pannedY += dy;
         pan(dx, dy);
       }
     }
     new Timer.periodic(new Duration(milliseconds: DELTA_T), step);
     return completer.future;
   }

   // pan a short distance, deaccelerate and make sure the final
   // point is reached
   panShortDistance(Point2D panBy) {
     panBy = panBy.toInt();
     var lastX = 0;
     var lastY = 0;
     var initialTime = now();

     // animation step
     step(Timer timer) {
       // scale time by 100 for kinetics calcuations
       var t  = (now() - initialTime) / 100;
       var p = ACCELERATION * math.pow(t,2) / 2 + SPEED * t;
       var x = bounded(p * fx, panBy.x).toInt();
       var y = bounded(p * fy, panBy.y).toInt();
       var v = ACCELERATION * t + SPEED;
       if (v <= 0 || panBy == new Point2D(x,y)){
         // last step - pan to the end point
         x = panBy.x;
         y = panBy.y;
         timer.cancel();
       }
       var dx = x - lastX;
       var dy = y - lastY;
       lastX = x;
       lastY = y;

       pan(dx, dy);
     }
     new Timer.periodic(new Duration(milliseconds: DELTA_T), step);
   }

   panLongDistance(panBy).then((rest) {
     panShortDistance(rest);
   });
 }
}

Constructors

new PanBehaviour(MapViewport viewport) #

Creates a new behaviour controlling viewport.

PanBehaviour(this.viewport);

Properties

const double ACCELERATION #

const double ACCELERATION = -2.0

const int DELTA_T #

const int DELTA_T = 20

const double SPEED #

const double SPEED = 20.0

final MapViewport viewport #

the map viewport controlled by this behaviour

final MapViewport viewport

Methods

void animate(Point2D panBy) #

Animate the panning of the map viewport by a vector given by panBy.

void animate(Point2D panBy) {
 var dist = math.sqrt(math.pow(panBy.x,2) + math.pow(panBy.y,2));
 var theta = math.asin(panBy.y / dist);
 if (panBy.x <= 0) {
   theta = math.PI - theta;
 }

 var fx = math.cos(theta);
 //y-axis is inverted, therefore not
 //  -math.sin(theta)
 var fy = math.sin(theta);

 now() => new DateTime.now().millisecondsSinceEpoch;

 pan(dx,dy) {
   var p = new Point2D(dx,dy).toInt();
   if (p != new Point2D.origin()) viewport.pan(p);
 }

 bounded(num v, num bound) => bound < 0
     ? math.max(v, bound)
     : math.min(v, bound);

 // pan long distance (fast) as much and possible, the complete
 // the future with the remaining pan distance
 Future<Point2D> panLongDistance(Point2D panBy) {
   var completer = new Completer<Point2D>();
   var pannedX = 0;
   var pannedY = 0;
   bool isLongDistance() =>
           (panBy.x - pannedX).abs() > 100
       || (panBy.y  - pannedY).abs() > 100;

   // animation step
   step(Timer timer){
     if (!isLongDistance()) {
       timer.cancel();
       var rest = new Point2D(panBy.x - pannedX, panBy.y - pannedY);
       completer.complete(rest);
     } else {
       var dx = bounded(40 * fx, panBy.x).toInt();
       var dy = bounded(40 * fy, panBy.y).toInt();
       pannedX += dx;
       pannedY += dy;
       pan(dx, dy);
     }
   }
   new Timer.periodic(new Duration(milliseconds: DELTA_T), step);
   return completer.future;
 }

 // pan a short distance, deaccelerate and make sure the final
 // point is reached
 panShortDistance(Point2D panBy) {
   panBy = panBy.toInt();
   var lastX = 0;
   var lastY = 0;
   var initialTime = now();

   // animation step
   step(Timer timer) {
     // scale time by 100 for kinetics calcuations
     var t  = (now() - initialTime) / 100;
     var p = ACCELERATION * math.pow(t,2) / 2 + SPEED * t;
     var x = bounded(p * fx, panBy.x).toInt();
     var y = bounded(p * fy, panBy.y).toInt();
     var v = ACCELERATION * t + SPEED;
     if (v <= 0 || panBy == new Point2D(x,y)){
       // last step - pan to the end point
       x = panBy.x;
       y = panBy.y;
       timer.cancel();
     }
     var dx = x - lastX;
     var dy = y - lastY;
     lastX = x;
     lastY = y;

     pan(dx, dy);
   }
   new Timer.periodic(new Duration(milliseconds: DELTA_T), step);
 }

 panLongDistance(panBy).then((rest) {
   panShortDistance(rest);
 });
}