Animating Bezier Curves with SVG and jQuery (finished) - part 5 in a series

the reconsituted Bezier Curve animation

Doesn't look much different than before, just different innards; curve is a little cleaner looking, draws a little faster.
Doesn't look much different than before, just different innards; curve is a little cleaner looking, draws a little faster.

Wrapping up the experiment with SVG and jQuery

I have been writing a series of hubs to show some techniques for programming in Javascript.  I first used native javascript to implement a graphing calculator.  Next, I used the jQuery library to animate the design of Bezier Curves.  In my last Hub, I started an experiment to adapt my previous work to use SVG.  When I left off, it was admittedly a work in progress.

The main thing that needed cleaning up, was to mesh up my coordinate system with that of SVG. SVG uses two sets of coordinates. One is the positioning of the tag (top, left, height, width). The other is the viewBox, basically where coordinates specified in svg tag elements appear with its screen area.

You could think of the viewBox as a virtual screen that is scaled to fit into the screen area of the SVG element. Since I am already using real screen coordinates, I set the viewBox to be an identity mapping.

Once the coordinates were meshed up, there was basically no change to the function that animates the drawing of the Bezier Curve while the control points are dragged around. So, SVG tags are indeed part of the repertoire that jQuery can manipulate.

The other change I needed to make was to assign the SVG element a class name that I could use to remove previous curves as the control points are moved about during animation.

meshing SVG with my coordinate system

function drawcurve( curvefn, coord, fnstart, fnstop, delta ) {
	var pt;
	var fofpt;
	var screenpt, newscreenpt;
	var top;
	var left;
	var width;
	var height;

	if( coord.screenx0 < coord.screenx1 ) {
		left = coord.screenx0;
		width = coord.screenx1 - coord.screenx0;
	} else {
		left = coord.screenx1;
		width = coord.screenx0 - coord.screenx1;
	}
	if( coord.screeny0 < coord.screeny1 ) {
		top = coord.screeny0;
		height = coord.screeny1 - coord.screeny0;
	} else {
		top = coord.screeny1;
		height = coord.screeny0 - coord.screeny1;
	}	

	var path="<svg class='newrectangle' style='position:absolute;" +
		"height:" + height + "px; width:" + width + "px; top:" + top + "px; left:" + left +
		"px;' viewBox='" + left + " " + top + " " + width + " " + height + 
		"' >" +
  		"<path stroke=black stroke-width=4 fill=none stroke-linejoin=round d='";
	var op = "M ";

	fofpt = curvefn( fnstart );
	screenpt = coord.pointToScreenCoordinates( fofpt.x, fofpt.y );
	for( pt = fnstart + delta ; pt < fnstop ; pt += delta ) {
		fofpt = curvefn( pt );
		newscreenpt = coord.pointToScreenCoordinates( fofpt.x, fofpt.y );
		if( screenpt.x != newscreenpt.x || screenpt.y != newscreenpt.y ) {
			if( op == "M " ) {
				path += op + screenpt.x + "," + screenpt.y;
				op = " L ";	
			}
			path += op + newscreenpt.x + "," + newscreenpt.y;
			screenpt = newscreenpt;	
		}
	}
	path += "'/></svg>";
	$( path ).appendTo( "body" );
}

no changes to the animation function

I added one line that reads,     $( ".newrectangle" ).attr( "myclass", "oldrectangle" );
This is a result of a bit of experimenting, because I ran into a problem with using jQuery to change the class of an SVG element.  I probably just need to get an even later version of the jQuery library, or maybe there is some rule that I am still unaware of.  In the worst case, I could use another attribute to tag SVG elements.  As it is, a previously drawn graph, will be erased when the animation starts.  I am not really trying to rival Adobe Illustrator, here, just demonstrate some techniques.  So, I think this is an acceptable bug, and I left it.

recalling the function that animated bezier curves

function jgbezier ( drawarea ) {
	var bpoints = new Array();
	var bpointcnt = 0;
	unitsquare.dontscale = true;
	$( ".newrectangle" ).attr( "myclass", "oldrectangle" );
	$( "#mousetarget" ).bind( "click", function (e) {
		if( bpointcnt < 4 ) {
			var rect = drawrectangle( "blackrectangle", e.pageY + 2, e.pageX - 2, 4, 4 

);
			$( rect ).attr( "id", "bcctl_" + bpointcnt );
			bpoints[bpointcnt] = drawarea.screenCoordinatesToPoint( e.pageX, e.pageY );
			++bpointcnt;
			if( bpointcnt == 4 ) {
				// mark what is already here
				$( ".blackrectangle" ).addClass( "oldrectangle" );
				// attach mouse handlers
				$( "#mousetarget" ).remove();
				var drawing = false;
				$( ".blackrectangle" ).bind( "mousedown", function ( e ) {
					var movetarget = this;
					$( "body" ).bind( "mousemove", function ( e ) {
						$( movetarget ).css( "top", e.pageY + 2 );
						$( movetarget ).css( "left", e.pageX - 2 );
						var myid = $( movetarget ).attr( "id" );
						var myindex = parseInt( myid.substr( 6 ) );
						bpoints[ myindex ] = 

drawarea.screenCoordinatesToPoint( e.pageX, e.pageY );
						if( !drawing ) {
							drawing = true;
							drawbcurve();
							// mark the new points
							drawing = false;
						}
					} );
				});
				$( "body" ).bind( "mouseup", function ( e ) {
					$( "body" ).unbind( "mousemove" );
					drawbcurve();
				});
				//draw curve
				var drawbcurve = function () {
					// remove old curve
					$( ".newrectangle" ).remove();
					var xpath = genbezierequation( bpoints[0].x, bpoints[1].x - 

bpoints[0].x, bpoints[2].x, bpoints[2].x - bpoints[3].x );
					var ypath = genbezierequation( bpoints[0].y, bpoints[1].y - 

bpoints[0].y, bpoints[2].y, bpoints[2].y - bpoints[3].y );	
					$( "#xformula" ).attr( "value", xpath );
					$( "#yformula" ).attr( "value", ypath );
					$( "#jgmeshstart" ).attr( "value", '0' );
					$( "#jgmeshend" ).attr( "value", '1' );
					$( "#start_calc" ).trigger( "click" );
					// mark the new points
					$( ".blackrectangle" ).addClass( "newrectangle" );
					$( ".oldrectangle" ).removeClass( "newrectangle" );
				}
				drawbcurve();
				// mark the new points
				$( ".blackrectangle" ).addClass( "newrectangle" );
				$( ".oldrectangle" ).removeClass( "newrectangle" );
			}
		}
	});
}

Summary

As long as you are willing to forgo users that may be using older browsers, SVG is a good alternative to the div based mechanism that I started with.  jQuery is quite able to manipulate SVG elements, and they interact well with the display of other DOM elements.  The SVG based rendering performs better and renders better than the div based implementation.

The techniques of of using divs in my previous hubs are still quite useful for developing UI widgets for Web pages.  Also, only a small portion of my code needed to change to accommodate using SVG.

Perhaps, next I will try a similar experiment with HTML 5 canvas elements.

More by this Author


Comments

No comments yet.

    Sign in or sign up and post using a HubPages Network account.

    0 of 8192 characters used
    Post Comment

    No HTML is allowed in comments, but URLs will be hyperlinked. Comments are not for promoting your articles or other sites.


    Click to Rate This Article
    working