Animated dragging. Performance sucks. I probably won't use this code.
authorBenjamin Smedberg <benjamin@smedbergs.us>
Wed, 26 Mar 2008 14:01:36 -0400
changeset 8 7c39dfdaedc6f86dcfe80416acf5993899cab3c8
parent 7 d500162b6b9e47431dd651a62a480c981069e85c
push id1
push userbsmedberg@mozilla.com
push dateMon, 14 Apr 2008 15:21:26 +0000
Animated dragging. Performance sucks. I probably won't use this code.
www/index.xhtml
www/jquery-1.2.3.js
www/navigate.js
--- a/www/index.xhtml
+++ b/www/index.xhtml
@@ -66,16 +66,17 @@
 	<path d="M 0 0 L 10 5 L 0 10 z" />
       </marker>
       <line id="arrow-template" class="arrow"
 	    x1="500" y1="350" x2="200" y2="300"
             visibility="hidden" />
     </defs>
     <g id="scroller" transform="translate(0,0)">
     </g>
+    <line id="test-line" x1="100" y1="100" x2="400" y2="400" stroke="red" stroke-width="2" marker-end="url(#arrowhead)" />
   </svg>
   <div id="inside-scrolling" />
 
   <div id="revision-template"
        class="revision">
     <div class="node">{Node}</div>
     <div class="user">{User}</div>
     <div class="date">{Date}</div>
--- a/www/jquery-1.2.3.js
+++ b/www/jquery-1.2.3.js
@@ -3155,16 +3155,19 @@ jQuery.fx.prototype = {
 
 		// Set display property to block for height/width animations
 		if ( this.prop == "height" || this.prop == "width" )
 			this.elem.style.display = "block";
 	},
 
 	// Get the current size
 	cur: function(force){
+		if ( jQuery.fx.cur[this.prop] )
+			return jQuery.fx.cur[this.prop](this);
+		
 		if ( this.elem[this.prop] != null && this.elem.style[this.prop] == null )
 			return this.elem[ this.prop ];
 
 		var r = parseFloat(jQuery.css(this.elem, this.prop, force));
 		return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
 	},
 
 	// Start an animation from one number to another
@@ -3285,16 +3288,18 @@ jQuery.fx.prototype = {
 			this.update();
 		}
 
 		return true;
 	}
 
 };
 
+jQuery.fx.cur = {};
+
 jQuery.fx.step = {
 	scrollLeft: function(fx){
 		fx.elem.scrollLeft = fx.now;
 	},
 
 	scrollTop: function(fx){
 		fx.elem.scrollTop = fx.now;
 	},
--- a/www/navigate.js
+++ b/www/navigate.js
@@ -1,16 +1,43 @@
 const BASEURL = '/hgwebdir.cgi/';
 const SVGNS = 'http://www.w3.org/2000/svg';
 
 const REVWIDTH = 254;
 const HSPACING = 40;
 const VSPACING = 30;
 
 /**
+ * jQuery doesn't animate SVG lines correctly
+ */
+function getCurFunction(prop)
+{
+  return function(fx) {
+    return fx.elem[prop].baseVal.value;
+  }
+}
+
+$.fx.cur.x1 = getCurFunction('x1');
+$.fx.cur.x2 = getCurFunction('x2');
+$.fx.cur.y1 = getCurFunction('y1');
+$.fx.cur.y2 = getCurFunction('y2');
+
+function getStepFunction(prop)
+{
+  return function(fx) {
+    fx.elem[prop].baseVal.value = fx.now;
+  }
+}
+
+$.fx.step.x1 = getStepFunction('x1');
+$.fx.step.x2 = getStepFunction('x2');
+$.fx.step.y1 = getStepFunction('y1');
+$.fx.step.y2 = getStepFunction('y2');
+
+/**
  * map from long node strings to Revision objects
  * The following mappings are added to the JSON:
  *   .element from the node to the element
  *   .parentArrows is a map of arrows pointing to this element, keyed on
  *                 parent node
  *   .childArrows  is a map of arrows pointing from this element, keyed on
  *                 child node
  */
@@ -80,24 +107,30 @@ Revision.prototype = {
   visible: function r_visible() {
     return $(this.element).css('visibility') != 'hidden';
   },
 
   x: function r_x() {
     if (!this.visible())
       throw Error("Revision " + this.node + " is not visible.");
 
-    return measure($(this.element), 'left');
+    if (this._xpos == null)
+      throw Error("X position has not been set properly!");
+
+    return this._xpos;
   },
 
   y: function r_y() {
     if (!this.visible())
       throw Error("Revision " + this.node + " is not visible.");
 
-    return measure($(this.element), 'top');
+    if (this._ypos == null)
+      throw Error("Y position has not been set properly!");
+
+    return this._ypos;
   },
 
   center: function r_center() {
     if (!this.visible())
       throw Error("Revision " + this.node + " is not visible.");
     
     return {x: this.x() + REVWIDTH / 2,
             y: this.y() + this.height / 2};
@@ -122,37 +155,72 @@ Revision.prototype = {
   
   /**
    * Move the center of the box to this point
    */
   moveTo: function r_move(point) {
     var e, child, parent, p, a;
     
     e = $(this.element);
+    this._xpos = point.x - REVWIDTH / 2;
+    this._ypos = point.y - this.height / 2;
+
+    if (this.visible()) {
+      e.animate({left: this._xpos,
+                 top: this._ypos},
+                {duration: 150,
+                 queue: false});
+    }
+    else {
+      e.css('left', this._xpos);
+      e.css('top', this._ypos);
+      e.css('opacity', 0);
+      e.animate({opacity: 1},
+                {duration: 150,
+                 queue: false});
+    }
     e.css('visibility', 'visible');
-    e.css('left', point.x - REVWIDTH / 2);
-    e.css('top', point.y - this.height / 2);
-    
+
     p = this.childPoint();
     for each (child in this.children) {
       a = $(this.childArrows[child]);
-      a.attr('x1', p.x);
-      a.attr('y1', p.y);
+      if (a.css('visibility') == 'visible') {
+        a.animate({x1: p.x,
+                   y1: p.y},
+                  {duration: 150});
+      }
+      else {
+        a.attr('x1', p.x);
+        a.attr('y1', p.y);
+        a.css('opacity', 0);
+        a.animate({opacity: 1},
+                  {duration: 150});
+      }
       if (!(child in revs)) {
         a.attr('x2', p.x + 25);
         a.attr('y2', p.y);
       }
       a.css('visibility', 'visible');
     }
     
     p = this.parentPoint();
     for each (parent in this.parents) {
       a = $(this.parentArrows[parent]);
-      a.attr('x2', p.x);
-      a.attr('y2', p.y);
+      if (a.css('visibility') == 'visible') {
+        a.animate({x2: p.x,
+                   y2: p.y},
+                   {duration: 150});
+      }
+      else {
+        a.attr('x2', p.x);
+        a.attr('y2', p.y);
+        a.css('opacity', 0);
+        a.animate({opacity: 1},
+                  {duration: 150});
+      }
       if (!(parent in revs)) {
         a.attr('x1', p.x - 25);
         a.attr('y1', p.y);
       }
       a.css('visibility', 'visible');
     }
   },
   
@@ -164,23 +232,33 @@ Revision.prototype = {
   {
     var child, parent, p, a, i;
 
     if (!this.gc) {
       $(this.element).css('visibility', 'hidden');
 
       for each (child in this.children) {
         if (!(child in revs)) {
-          $(this.childArrows[child]).css('visibility', 'hidden');
+          a = $(this.childArrows[child]);
+          if (a.css('visibility') != 'hidden') {
+            a.animate({opacity: 0,
+                       visibility: 'hidden'},
+                      {duration: 150});
+          }
         }
       }
       
       for each (parent in this.parents) {
         if (!(parent in revs) || !(revs[parent].gc)) {
-          $(this.parentArrows[parent]).css('visibility', 'hidden');
+          a = $(this.parentArrows[parent]);
+          if (a.css('visibility') != 'hidden') {
+            a.animate({opacity: 0,
+                       visibility: 'hidden'},
+                      {duration: 150});
+          }
         }
       }
     }
     else {
       // We've already been positioned and are visible; all we need is to position the "other" end
       // of arrows that point to offscreen revisions
       p = this.childPoint();
       for (i in this.children) {
@@ -341,18 +419,16 @@ function doLayout(node)
   
   drawChildren(contextrev);
   drawParents(contextrev);
   
   for each (rev in revs)
     rev.cleanLayout();
 }
 
-
-
 function processContextData(data)
 {
   for each (var node in data.nodes) {
       if (node.node in revs)
           continue;
 
       new Revision(node);
   }