Core JavaScript

SVG - A Draggable Cubic

The acronym SVG stands for Scalable Vector Graphics and refers to graphics that are generated by a geometric specification rather than by pixels, like an image (called rasterized graphics as opposed to the vectorized graphics described here).

The program below fetches the body element first. Then a div element is created and assigned the name qMainDiv. Inside qMainDiv, we add a svg element and put the line element inside it.

The code creates a cubic bezier curve that interpolates the endpoints and has two control points that can be dragged. Also, the cursor changes when it is close enough to a one of these points to signal that it may be dragged.

SvgDraggableCubic.html

<!DOCTYPE html>
<html>
<head>
    <title>XoaX.net's Javascript</title>
</head>
<body>
    <script type="text/javascript" src="SvgDraggableCubic.js"></script>
</body>
</html>

SvgDraggableCubic.js

var qBody = document.getElementsByTagName("body")[0];

// The containing div and all of the variables
var qMainDiv = document.createElement("div");
qMainDiv.style.backgroundColor = "#bbdd99";
qMainDiv.style.width = "600px";
qMainDiv.style.height = "600px";
qMainDiv.style.margin = "20px";
qMainDiv.mbDrag1 = false;
qMainDiv.mbDrag2 = false;
qMainDiv.mbDragC1 = false;
qMainDiv.mbDragC2 = false;
qMainDiv.mdX1	 = 100.0;
qMainDiv.mdY1	 = 200.0;
qMainDiv.mdXC1	 = 100.0;
qMainDiv.mdYC1	 = 100.0;
qMainDiv.mdXC2	 = 300.0;
qMainDiv.mdYC2	 = 100.0;
qMainDiv.mdX2	 = 300.0;
qMainDiv.mdY2	 = 200.0;
qMainDiv.mdDistSq1	 = 200.0;
qMainDiv.mdDistSqC1	 = 200.0;
qMainDiv.mdDistSqC2	 = 200.0;
qMainDiv.mdDistSq2	 = 200.0;
qMainDiv.mqPoint1	= null;
qMainDiv.mqPointC1	= null;
qMainDiv.mqPointC2	= null;
qMainDiv.mqPoint2	= null;
qMainDiv.mqCubicSvg = null;
qMainDiv.mqCubicPath = null;
qMainDiv.mqLinePath = null;
qMainDiv.onmousemove = function(e) {
	this.SetCursorCoordinates(e);
	this.SetCursorDistancesSquared();
	// Make a case for draging each of the four points
	if (this.mbDrag1) {
		this.mdX1 = this.mdCursorX;
		this.mdY1 = this.mdCursorY;
		this.mqPoint1.setAttribute('cx', this.mdX1);
		this.mqPoint1.setAttribute('cy', this.mdY1);
	} else if (this.mbDrag2){
		this.mdX2 = this.mdCursorX;
		this.mdY2 = this.mdCursorY;
		this.mqPoint2.setAttribute('cx', this.mdX2);
		this.mqPoint2.setAttribute('cy', this.mdY2);
	} else if (this.mbDragC1){
		this.mdXC1 = this.mdCursorX;
		this.mdYC1 = this.mdCursorY;
		this.mqPointC1.setAttribute('cx', this.mdXC1);
		this.mqPointC1.setAttribute('cy', this.mdYC1);
	} else if (this.mbDragC2){
		this.mdXC2 = this.mdCursorX;
		this.mdYC2 = this.mdCursorY;
		this.mqPointC2.setAttribute('cx', this.mdXC2);
		this.mqPointC2.setAttribute('cy', this.mdYC2);
	} else {
		// If the cursor is close to a drag point, change it.
		if ((this.mdDistSq1 <= 25) ||
		    (this.mdDistSqC1 <= 25) ||
		    (this.mdDistSqC2 <= 25) ||
		    (this.mdDistSq2 <= 25)) {
			this.style.cursor = "pointer";
		} else {
			this.style.cursor = "auto";
		}
	}
	this.mqLinePath.setAttribute('d', 'M'+this.mdX1+','+this.mdY1+
	  ' L'+this.mdXC1+','+this.mdYC1+' L'+this.mdXC2+','+this.mdYC2+
	  ' L'+this.mdX2+','+this.mdY2);
	this.mqCubicPath.setAttribute('d', 'M'+this.mdX1+','+this.mdY1+
	  ' C'+this.mdXC1+','+this.mdYC1+' '+this.mdXC2+','+this.mdYC2+
	  ' '+this.mdX2+','+this.mdY2);
};
qMainDiv.onmousedown = function(e) {
	this.SetCursorCoordinates(e);
	this.SetCursorDistancesSquared();
	// Check if any point is in range to be dragged
	if (this.mdDistSq1 <= 25) {
		this.mbDrag1 = true;
	} else if (this.mdDistSqC1 <= 25) {
		this.mbDragC1 = true;
	} else if (this.mdDistSqC2 <= 25) {
		this.mbDragC2 = true;
	} else if (this.mdDistSq2 <= 25) {
		this.mbDrag2 = true;
	}
};
qMainDiv.onmouseup = function(e) {
	this.mbDrag1 = false;
	this.mbDragC1 = false;
	this.mbDragC2 = false;
	this.mbDrag2 = false;
};
// The function that is used to calculate the cursor coordinates
qMainDiv.SetCursorCoordinates = function(e) {
	var dElementOffsetX = 0;
	var dElementOffsetY = 0;
	var qElement = this;

	do{
	    dElementOffsetX += qElement.offsetLeft - qElement.scrollLeft;
	    dElementOffsetY += qElement.offsetTop - qElement.scrollTop;
	    qElement = qElement.offsetParent
	} while(qElement != document.body)

	this.mdCursorX = e.pageX - dElementOffsetX;
	this.mdCursorY = e.pageY - dElementOffsetY;
}
qMainDiv.SetCursorDistancesSquared = function() {
	var dX1 = this.mdX1;
	var dY1 = this.mdY1;
	var dXC1 = this.mdXC1;
	var dYC1 = this.mdYC1;
	var dXC2 = this.mdXC2;
	var dYC2 = this.mdYC2;
	var dX2 = this.mdX2;
	var dY2 = this.mdY2;
	// Calculate the distance squared from the cursor to each point
	this.mdDistSq1 = (dX1 - this.mdCursorX)*(dX1 - this.mdCursorX)
		+ (dY1 - this.mdCursorY)*(dY1 - this.mdCursorY);
	this.mdDistSqC1 = (dXC1 - this.mdCursorX)*(dXC1 - this.mdCursorX)
		+ (dYC1 - this.mdCursorY)*(dYC1 - this.mdCursorY);
	this.mdDistSqC2 = (dXC2 - this.mdCursorX)*(dXC2 - this.mdCursorX)
		+ (dYC2 - this.mdCursorY)*(dYC2 - this.mdCursorY);
	this.mdDistSq2 = (dX2 - this.mdCursorX)*(dX2 - this.mdCursorX)
		+ (dY2 - this.mdCursorY)*(dY2 - this.mdCursorY);
}

// The whole SVG region
var qCubicSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
qCubicSvg.style.width="600px";
qCubicSvg.style.height="600px";
qMainDiv.mqCubicSvg = qCubicSvg;

// The cubic interpolation path
var qCubicPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
qCubicPath.setAttribute('d', 'M'+qMainDiv.mdX1+','+qMainDiv.mdY1+
							' C'+qMainDiv.mdXC1+','+qMainDiv.mdYC1+
							' '+qMainDiv.mdXC2+','+qMainDiv.mdYC2+
							' '+qMainDiv.mdX2+','+qMainDiv.mdY2);
qCubicPath.setAttribute('stroke-width', '3');
qCubicPath.setAttribute('stroke', 'rgb(240, 255, 220)');
qCubicPath.setAttribute('fill', 'none');
qMainDiv.mqCubicPath = qCubicPath;

// The three-piece linear path bounding the quadratic
var qLinePath = document.createElementNS("http://www.w3.org/2000/svg", "path");
qLinePath.setAttribute('d', 'M'+qMainDiv.mdX1+','+qMainDiv.mdY1+
  ' L'+qMainDiv.mdXC1+','+qMainDiv.mdYC1+
  ' L'+qMainDiv.mdXC2+','+qMainDiv.mdYC2+
  ' L'+qMainDiv.mdX2+','+qMainDiv.mdY2);
qLinePath.setAttribute('stroke-width', '1');
qLinePath.setAttribute('stroke', 'rgb(240, 255, 220)');
qLinePath.setAttribute('fill', 'none');
qMainDiv.mqLinePath = qLinePath;

// The first endpoint
var qPoint1 = document.createElementNS("http://www.w3.org/2000/svg", "circle");
qPoint1.setAttribute('cx', qMainDiv.mdX1);
qPoint1.setAttribute('cy', qMainDiv.mdY1);
qPoint1.setAttribute('r', 4);
qPoint1.setAttribute('fill', 'rgb(240, 255, 220)');
qMainDiv.mqPoint1 = qPoint1;

// The first control point
var qPointC1 = document.createElementNS("http://www.w3.org/2000/svg", "circle");
qPointC1.setAttribute('cx', qMainDiv.mdXC1);
qPointC1.setAttribute('cy', qMainDiv.mdYC1);
qPointC1.setAttribute('r', 4);
qPointC1.setAttribute('fill', 'rgb(240, 255, 220)');
qMainDiv.mqPointC1 = qPointC1;

// The second control point
var qPointC2 = document.createElementNS("http://www.w3.org/2000/svg", "circle");
qPointC2.setAttribute('cx', qMainDiv.mdXC2);
qPointC2.setAttribute('cy', qMainDiv.mdYC2);
qPointC2.setAttribute('r', 4);
qPointC2.setAttribute('fill', 'rgb(240, 255, 220)');
qMainDiv.mqPointC2 = qPointC2;

// The second endpoint
var qPoint2 = document.createElementNS("http://www.w3.org/2000/svg", "circle");
qPoint2.setAttribute('cx', qMainDiv.mdX2);
qPoint2.setAttribute('cy', qMainDiv.mdY2);
qPoint2.setAttribute('r', 4);
qPoint2.setAttribute('fill', 'rgb(240, 255, 220)');
qMainDiv.mqPoint2 = qPoint2;

// Put all of the elements together
qCubicSvg.appendChild(qCubicPath);
qCubicSvg.appendChild(qLinePath);
qCubicSvg.appendChild(qPoint1);
qCubicSvg.appendChild(qPointC1);
qCubicSvg.appendChild(qPointC2);
qCubicSvg.appendChild(qPoint2);
qMainDiv.appendChild(qCubicSvg);
qBody.appendChild(qMainDiv);



 

Output

 
 

© 2007–2024 XoaX.net LLC. All rights reserved.