var Belay = (function () {
var settings = {
strokeColor: '#fff',
strokeWidth: 2,
opacity: 1,
fill: 'none',
animate: true,
animationDirection: 'right',
animationDuration: .75
};
var me = {};
me.init = function (initObj) {
if (initObj) {
$.each(initObj, function (index, value) {
//TODO validation on settings
settings[index] = value;
});
}
}
me.set = function (prop, val) {
//TODO validate
settings[prop] = val;
}
me.on = function (el1, el2) {
var $el1 = $(el1);
var $el2 = $(el2);
if ($el1.length && $el2.length) {
var svgheight, p, svgleft, svgtop, svgwidth
var el1pos = $(el1).offset();
var el2pos = $(el2).offset();
var el1H = $(el1).outerHeight();
var el1W = $(el1).outerWidth();
var el2H = $(el2).outerHeight();
var el2W = $(el2).outerWidth();
var node1X = Math.round(el1pos.left + el1W);
var node2X = Math.round(el2pos.left);
var overlapping = node1X >= node2X;
var svgwidth = Math.abs(node2X - node1X);
var svgleft = Math.min(node1X, node2X);
var node1Y = Math.round(el1pos.top + el1H / 2);
var node2Y = Math.round(el2pos.top + el1H / 2);
var svgheight = Math.abs(node1Y - node2Y);
var svgtop = Math.min(node1Y, node2Y);
var pt1x = node1X - svgleft;
var pt1y = node1Y - svgtop + settings.strokeWidth;
var pt2x = node2X - svgleft;
var pt2y = node2Y - svgtop + settings.strokeWidth;
// cpt is the length of the control point vector
// variew with distance netween boxes
var cpt = Math.round(svgwidth * Math.min(svgheight / 300, 1));
if (overlapping) {
// Need to widen the svg because otherwise the bezier control
// points (and hence the curve) will extend outside it.
svgleft -= cpt;
svgwidth += 2 * cpt;
pt1x += cpt;
pt2x += cpt;
}
// Build the path decription
p = "M" + pt1x + "," + pt1y +
" L" + (pt1x + pt2x) / 8 + "," + pt1y +
" L" + (pt1x + pt2x) / 8 + "," + pt2y //+
" L" + (pt2x) / 1.03 + "," + pt2y +
" L" + (pt2x) / 1.03 + "," + (pt2y - 5) +
" L" + pt2x + "," + pt2y +
" L" + (pt2x) / 1.03 + "," + (pt2y + 5) +
" L" + (pt2x) / 1.03 + "," + pt2y;
//ugly one-liner
$ropebag = $('#ropebag').length ? $('#ropebag') : $('body').append($("<div id='ropebag' />")).find('#ropebag');
var svgnode = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
var defs = document.createElementNS('http://www.w3.org/2000/svg', "defs");
var marker = document.createElementNS('http://www.w3.org/2000/svg', "marker");
var path = document.createElementNS('http://www.w3.org/2000/svg', "path");
marker.setAttributeNS(null,"id","arrow");
marker.setAttributeNS(null,"markerWidth","13");
marker.setAttributeNS(null,"markerHeight","13");
marker.setAttributeNS(null,"refX","2");
marker.setAttributeNS(null,"refY","7");
marker.setAttributeNS(null,"markerUnits","userSpaceOnUse");
path.setAttributeNS(null,"d","M2,2 L2,13 L8,7 L2,2");
marker.appendChild(path);
defs.appendChild(marker);
var newpath = document.createElementNS('http://www.w3.org/2000/svg', "path");
newpath.setAttributeNS(null, "d", p);
newpath.setAttributeNS(null, "stroke", settings.strokeColor);
newpath.setAttributeNS(null, "stroke-width", settings.strokeWidth);
newpath.setAttributeNS(null, "opacity", settings.opacity);
newpath.setAttributeNS(null, "fill", settings.fill);
newpath.setAttributeNS(null, "marker-end", "url(#arrow)");
svgnode.appendChild(newpath);
svgnode.appendChild(defs);
$(svgnode).css({
left: svgleft,
top: svgtop - settings.strokeWidth,
position: 'absolute',
width: svgwidth,
height: svgheight + settings.strokeWidth * 2,
minHeight: '20px',
'pointer-events': 'none'
});
$ropebag.append(svgnode);
if (settings.animate) {
// THANKS to http://jakearchibald.com/2013/animated-line-drawing-svg/
var pl = newpath.getTotalLength();
// Set up the starting positions
newpath.style.strokeDasharray = pl + ' ' + pl;
if (settings.animationDirection == 'right') {
newpath.style.strokeDashoffset = pl;
} else {
newpath.style.strokeDashoffset = -pl;
}
// Trigger a layout so styles are calculated & the browser
// picks up the starting position before animating
// WON'T WORK IN IE. If you want that, use requestAnimationFrame to update instead of CSS animation
newpath.getBoundingClientRect();
newpath.style.transition = newpath.style.WebkitTransition = 'stroke-dashoffset ' + settings.animationDuration + 's ease-in-out';
// Go!
newpath.style.strokeDashoffset = '0';
}
}
}
me.off = function () {
$("#ropebag").empty();
}
return me;
}());
/*********************** Custom JavaScript **********************************/
$(document).ready(function () {
$(".draggable").draggable({
drag: function (event, ui) {
Belay.off();
drawConnectors();
}
});
function drawConnectors() {
$(".parent").each(function () {
var theID = this.id;
$("." + theID).each(function (i, e) {
var rand = Math.random() * .7 + .3;
Belay.set('animationDuration', rand)
Belay.on($("#" + theID), e)
});
})
}
$(window).resize(function () {
Belay.off();
drawConnectors();
});
Belay.init({
strokeWidth: 1
});
Belay.set('strokeColor', '#999');
drawConnectors();
});
.row{
margin-top:2in;
}
.box{
border:1px solid #ccc;
padding:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script>
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<body>
<div class="row">
<div class="col-md-4">
<div class="pull-left ">
<div class="parent draggable box" id="parent1">Left drag</div>
</div>
<div class="pull-right">
<div class="child parent1 draggable box">Right drag</div>
</div>
</div>
</div>
</body>
Hi as per this example how-to-change-the-lines-between-two-points-from-curved-to-straight-lines-with-on how to draw arrow at the end of the path dynamically
can someone tell me what is wrong in adding marker end in my code
var Belay = (function ()
{
var settings = {
strokeColor: 'red',
strokeWidth: 2,
opacity: 1,
fill: 'none',
animate: true,
animationDirection: 'right',
animationDuration: .75
};
var me = {};
me.init = function (initObj) {
if (initObj) {
$.each(initObj, function (index, value) {
//TODO validation on settings
settings[index] = value;
});
}
}
me.set = function (prop, val) {
//TODO validate
settings[prop] = val;
}
me.on = function (el1, el2) {
var $el1 = $(el1);
var $el2 = $(el2);
if ($el1.length && $el2.length) {
var svgheight, p, svgleft, svgtop, svgwidth
var el1pos = $(el1).offset();
var el2pos = $(el2).offset();
var el1H = $(el1).outerHeight();
var el1W = $(el1).outerWidth();
var el2H = $(el2).outerHeight();
var el2W = $(el2).outerWidth();
var node1X = Math.round(el1pos.left + el1W);
var node2X = Math.round(el2pos.left);
var overlapping = node1X >= node2X;
var svgwidth = Math.abs(node2X - node1X);
var svgleft = Math.min(node1X, node2X);
var node1Y = Math.round(el1pos.top + el1H / 2);
var node2Y = Math.round(el2pos.top + el1H / 2);
var svgheight = Math.abs(node1Y - node2Y);
var svgtop = Math.min(node1Y, node2Y);
var pt1x = node1X - svgleft;
var pt1y = node1Y - svgtop + settings.strokeWidth;
var pt2x = node2X - svgleft;
var pt2y = node2Y - svgtop + settings.strokeWidth;
// cpt is the length of the control point vector
// variew with distance netween boxes
var cpt = Math.round(svgwidth * Math.min(svgheight / 300, 1));
if (overlapping) {
// Need to widen the svg because otherwise the bezier control
// points (and hence the curve) will extend outside it.
svgleft -= cpt;
svgwidth += 2 * cpt;
pt1x += cpt;
pt2x += cpt;
}
p = "M" + pt1x + "," + pt1y +
" L" + (pt1x + pt2x) / 8 + "," + pt1y +
" L" + (pt1x + pt2x) / 8 + "," + pt2y +
" L" + (pt2x) / 1.03 + "," + pt2y +
" L" + (pt2x) / 1.03 + "," + (pt2y - 5) +
" L" + pt2x + "," + pt2y +
" L" + (pt2x) / 1.03 + "," + (pt2y + 5) +
" L" + (pt2x) / 1.03 + "," + pt2y;
//ugly one-liner
$ropebag = $('#ropebag').length ? $('#ropebag') : $('body').append($("<div id='ropebag' />")).find('#ropebag');
var svgnode = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
//try
var defs = document.createElementNS('http://www.w3.org/2000/svg', "defs");
var marker = document.createElementNS('http://www.w3.org/2000/svg', "marker");
var path = document.createElementNS('http://www.w3.org/2000/svg', "path");
marker.setAttributeNS(null,"id","arrow");
marker.setAttributeNS(null,"markerWidth",10);
marker.setAttributeNS(null,"markerHeight",10);
marker.setAttributeNS(null,"refX",0);
marker.setAttributeNS(null,"refY",5);
marker.setAttributeNS(null,"viewbox","0 0 10 10");
marker.setAttributeNS(null,"orient","auto");
marker.setAttributeNS(null,"markerUnits","strokeWidth");
path.setAttributeNS(null,"d","M 0 0 L 10 5 L 0 10");
////try
marker.appendChild(path);
defs.appendChild(marker);
var newpath = document.createElementNS('http://www.w3.org/2000/svg', "path");
newpath.setAttributeNS(null, "d", p);
newpath.setAttributeNS(null, "stroke", settings.strokeColor);
newpath.setAttributeNS(null, "stroke-width", settings.strokeWidth);
newpath.setAttributeNS(null, "opacity", settings.opacity);
newpath.setAttributeNS(null, "fill", settings.fill);
newpath.setAttributeNS(null, "marker-end", "url(#arrow)");
newpath.appendChild(defs);
svgnode.appendChild(newpath);
//for some reason, adding a min-height to the svg div makes the lines appear more correctly.
$(svgnode).css({
left: svgleft,
top: svgtop - settings.strokeWidth,
position: 'absolute',
width: svgwidth,
height: svgheight + settings.strokeWidth * 2,
minHeight: '20px',
'pointer-events': 'none'
});
$ropebag.append(svgnode);
if (settings.animate) {
// THANKS to http://jakearchibald.com/2013/animated-line-drawing-svg/
var pl = newpath.getTotalLength();
// Set up the starting positions
newpath.style.strokeDasharray = pl + ' ' + pl;
if (settings.animationDirection == 'right') {
newpath.style.strokeDashoffset = pl;
} else {
newpath.style.strokeDashoffset = -pl;
}
// Trigger a layout so styles are calculated & the browser
// picks up the starting position before animating
// WON'T WORK IN IE. If you want that, use requestAnimationFrame to update instead of CSS animation
newpath.getBoundingClientRect();
newpath.style.transition = newpath.style.WebkitTransition = 'stroke-dashoffset ' + settings.animationDuration + 's ease-in-out';
// Go!
newpath.style.strokeDashoffset = '0';
}
}
}
me.off = function ()
{
$("#ropebag").empty();
}
return me;
} ());
function drawConnectors()
{
}
You don't seem to be appending newpath to anything.
Also you are appending defs to newpath. I'm not sure that works (I've never tried it). You should probably avoid doing that. Append the defs to the SVG instead.
Related
I have a bizarre problem with animating stroke paths in IE when the stroke is greater than 1px wide. I'm using the following script (not created by me):
//inspired by http://product.voxmedia.com/post/68085482982/polygon-feature-design-svg-animations-for-fun-and
//If you want to add SVG to the DOM, jQuery won't do
//http://www.benknowscode.com/2012/09/using-svg-elements-with-jquery_6812.html
function SVG(tag) {
return document.createElementNS('http://www.w3.org/2000/svg', tag);
}
function replaceRectsWithPaths(parentElement) {
var rects = $(parentElement).find('rect');
$.each(rects, function() {
var rectX = $(this).attr('x');
var rectY = $(this).attr('y');
var rectX2 = parseFloat(rectX) + parseFloat($(this).attr('width'));
var rectY2 = parseFloat(rectY) + parseFloat($(this).attr('height'));
var convertedPath = 'M' + rectX + ',' + rectY + ' ' + rectX2 + ',' + rectY + ' ' + rectX2 + ',' + rectY2 + ' ' + rectX + ',' + rectY2 + ' ' + rectX + ',' + rectY;
$(SVG('path'))
.attr('d', convertedPath)
.attr('fill', $(this).attr('fill'))
.attr('stroke', $(this).attr('stroke'))
.attr('stroke-width', $(this).attr('stroke-width'))
.insertAfter(this);
});
$(rects).remove();
}
function replaceLinesWithPaths(parentElement) {
var lines = $(parentElement).find('line');
$.each(lines, function() {
var lineX1 = $(this).attr('x1');
var lineY1 = $(this).attr('y1');
var lineX2 = $(this).attr('x2');
var lineY2 = $(this).attr('y2');
var convertedPath = 'M' + lineX1 + ',' + lineY1 + ' ' + lineX2 + ',' + lineY2;
$(SVG('path'))
.attr('d', convertedPath)
.attr('fill', $(this).attr('fill'))
.attr('stroke', $(this).attr('stroke'))
.attr('stroke-width', $(this).attr('stroke-width'))
.insertAfter(this);
});
$(lines).remove();
}
function replaceCirclesWithPaths(parentElement) {
var circles = $(parentElement).find('circle');
$.each(circles, function() {
var cX = $(this).attr('cx');
var cY = $(this).attr('cy');
var r = $(this).attr('r');
var r2 = parseFloat(r * 2);
var convertedPath = 'M' + cX + ', ' + cY + ' m' + (-r) + ', 0 ' + 'a ' + r + ', ' + r + ' 0 1,0 ' + r2 + ',0 ' + 'a ' + r + ', ' + r + ' 0 1,0 ' + (-r2) + ',0 ';
$(SVG('path'))
.attr('d', convertedPath)
.attr('fill', $(this).attr('fill'))
.attr('stroke', $(this).attr('stroke'))
.attr('stroke-width', $(this).attr('stroke-width'))
.insertAfter(this);
});
$(circles).remove();
}
function replaceEllipsesWithPaths(parentElement) {
var ellipses = $(parentElement).find('ellipse');
$.each(ellipses, function() {
var cX = $(this).attr('cx');
var cY = $(this).attr('cy');
var rX = $(this).attr('rx');
var rY = $(this).attr('ry');
var convertedPath = 'M' + cX + ', ' + cY + ' m' + (-rX) + ', 0 ' + 'a ' + rX + ', ' + rY + ' 0 1,0 ' + rX*2 + ',0 ' + 'a ' + rX + ', ' + rY + ' 0 1,0 ' + (-rX*2) + ',0 ';
$(SVG('path'))
.attr('d', convertedPath)
.attr('fill', $(this).attr('fill'))
.attr('stroke', $(this).attr('stroke'))
.attr('stroke-width', $(this).attr('stroke-width'))
.insertAfter(this);
});
$(ellipses).remove();
}
function replacePolygonsWithPaths(parentElement) {
var polygons = $(parentElement).find('polygon');
$.each(polygons, function() {
var points = $(this).attr('points');
var polyPoints = points.split(/[ ,]+/);
var endPoint = polyPoints[0] + ', ' + polyPoints[1];
$(SVG('path'))
.attr('d', 'M' + points + ' ' + endPoint)
.attr('fill', $(this).attr('fill'))
.attr('stroke', $(this).attr('stroke'))
.attr('stroke-width', $(this).attr('stroke-width'))
.insertAfter(this);
});
$(polygons).remove();
}
function replacePolylinesWithPaths(parentElement) {
var polylines = $(parentElement).find('polyline');
$.each(polylines, function() {
var points = $(this).attr('points');
$(SVG('path'))
.attr('d', 'M' + points)
.attr('fill', $(this).attr('fill'))
.attr('stroke', $(this).attr('stroke'))
.attr('stroke-width', $(this).attr('stroke-width'))
.insertAfter(this);
});
$(polylines).remove();
}
function hideSVGPaths(parentElement) {
var paths = $(parentElement).find('path');
//for each PATH..
$.each( paths, function() {
//get the total length
var totalLength = this.getTotalLength();
//set PATHs to invisible
$(this).css({
'stroke-dashoffset': totalLength,
'stroke-dasharray': totalLength + ' ' + totalLength
});
});
}
function drawSVGPaths(_parentElement, _timeMin, _timeMax, _timeDelay) {
var paths = $(_parentElement).find('path');
//for each PATH..
$.each( paths, function(i) {
//get the total length
var totalLength = this.getTotalLength();
//set PATHs to invisible
$(this).css({
'stroke-dashoffset': totalLength,
'stroke-dasharray': totalLength + ' ' + totalLength
});
//animate
$(this).delay(_timeDelay*i).animate({
'stroke-dashoffset': 0
}, {
duration: Math.floor(Math.random() * _timeMax) + _timeMin
,easing: 'easeInOutQuad'
});
});
}
function replaceWithPaths(parentElement) {
replaceRectsWithPaths(parentElement);
replaceLinesWithPaths(parentElement);
replaceEllipsesWithPaths(parentElement);
replaceCirclesWithPaths(parentElement);
replacePolygonsWithPaths(parentElement);
replacePolylinesWithPaths(parentElement);
}
function startSVGAnimation(parentElement){
drawSVGPaths(parentElement, 100, 300, 200);
TweenLite.to(".svgWork", 1, {attr:{"fill-opacity":1}}).delay(1);
TweenLite.to(".svgServices", 1, {attr:{"fill-opacity":1}}).delay(1.5);
TweenLite.to(".svgCalc", 1, {attr:{"fill-opacity":1}}).delay(2);
TweenLite.to(".svgTeam", 1, {attr:{"fill-opacity":1}}).delay(1);
TweenLite.to(".svgWorkforus", 1, {attr:{"fill-opacity":1}}).delay(1.5);
}
$(function() {
//if (!Modernizr.touch) {
var animated = $('.js-animate');
replaceWithPaths(animated);
hideSVGPaths(animated);
$(document).scroll(function() {
$(animated).each(function(i) {
if( $(this).visible() ) {
startSVGAnimation(this);
animated.splice(i,1);
};
});
});
$(document).scroll();
//};
});
http://codepen.io/niorad/pen/xmfza
As you can see it works fine in IE (and other browsers). Having implemented it on my site and applied it to an SVG with a stroke-width of greater than 1, the SVG does not draw properly in IE. Example here:
http://upright.cloudlevel.me/
If I reduce it down to a basic SVG circle without anything else e.g.
circle fill="#FFFFFF" stroke="#000000" stroke-width="4" stroke-miterlimit="10" cx="115.5" cy="119.5" r="55.5"
I can increase the stroke-width to 6 before it breaks.
What can I do to fix / get around this issue? At this point the best I have come up with is to include some browser detection to disable animation in IE, but this of course would be far from ideal.
I also met the same problem, I found it render error when stroke-width greater than 6. I suspect it is IE's bug on rendering svg.
When I drawing a SVG path animation with clip-path, I have to use the below code.
var ua = navigator.userAgent.toLowerCase();
if(/msie/.test(ua) || /rv:[\d.]+\) like gecko/.test(ua)){
e.setAttributeNS(null, 'stroke-width', '6');
} else {
e.setAttributeNS(null, 'stroke-width', '15');
}
I am a svg newbie.I need to draw wind arrows in svg with jquery.
I found some code for drawing wind arrows with d3 here.
I converted it into the jquery compatible form. Here is jsfiddle example.
But it is not working. Can anyone help?
Here is the jquery code:
$(function () {
var WindBarbArrowHandler = {
WindArrow: function (speed, direction, container, arrowWidth) {
'use strict';
var index = 0,
i;
this.speed = speed;
this.direction = direction;
this.trigDirection = direction + 90;
this.scale = arrowWidth / 8;
this.ten = 0;
this.five = 0;
this.fifty = 0;
// Create the canvas
$(container).append(
$('<svg></svg>')
.attr({ height: 2 * arrowWidth, width: 2 * arrowWidth })
);
$("svg", container).append('<defs></defs>');
$("defs", container).append($('<clipPath></clipPath>').attr('id', 'clip'));
$("clipPath", container).append($('<rect></rect>')
.attr({ height: 2 * arrowWidth, width: 2 * arrowWidth }));
// Draw the widget area
$("svg", container).append($('<g></g>').attr('class', 'wind-arrow'));
this.widget = $("svg", container);
if (this.speed > 0) {
// Prepare the path
this.path = "";
if (this.speed <= 7) {
// Draw a single line
this.longBar();
index = 1;
} else {
this.shortBar();
}
// Find the number of lines in function of the speed
this.five = Math.floor(this.speed / 5);
if (this.speed % 5 >= 3) {
this.five += 1;
}
// Add triangles (5 * 10)
this.fifty = Math.floor(this.five / 10);
this.five -= this.fifty * 10;
// Add tenLines (5 * 2)
this.ten = Math.floor(this.five / 2);
this.five -= this.ten * 2;
// Draw first the triangles
for (i = 0; i < this.fifty; i++) {
this.addFifty(index + 2 * i);
}
if (this.fifty > 0) {
index += 2 * (this.fifty - 0.5);
}
// Draw the long segments
for (i = 0; i < this.ten; i++) {
this.addTen(index + i);
}
index += this.ten;
// Draw the short segments
for (i = 0; i < this.five; i++) {
this.addFive(index + i);
}
this.path += "Z";
// Add to the widget
this.widget.append($('<g></g>'));
$("g", this.widget).append($('<path></path>').attr({
'd': this.path,
'vector-effect': 'non-scaling-stroke',
'transform': 'translate(' + arrowWidth + ', ' + arrowWidth + ') scale(' + this.scale + ') rotate(' + this.trigDirection + ' ' + 0 + ' ' + 0 + ') translate(-8, -2)',
'class': 'wind-arrow'
}));
}
},
shortBar: function () {
// Draw an horizontal short bar.
'use strict';
this.path += "M1 2 L8 2 ";
},
longBar: function () {
// Draw an horizontal long bar.
'use strict';
this.path += "M0 2 L8 2 ";
},
addTen: function (index) {
// Draw an oblique long segment corresponding to 10 kn.
'use strict';
this.path += "M" + index + " 0 L" + (index + 1) + " 2 ";
},
addFive: function (index) {
// Draw an oblique short segment corresponding to 10 kn.
'use strict';
this.path += "M" + (index + 0.5) + " 1 L" + (index + 1) + " 2 ";
},
addFifty: function (index) {
// Draw a triangle corresponding to 50 kn.
'use strict';
this.path += "M" + index + " 0 L" + (index + 1) + " 2 L" + index + " 2 L" + index + " 0 ";
},
};
WindBarbArrowHandler.WindArrow(30,45, $("#windBarbArrow"), 40);
});
and html code looks like this:
<div id="windBarbArrow"></div>
Just use d3, you'll never look back.
If you have your heart set on jQuery, there's an annoying gotcha.
What it boils down to is anywhere in your code where you create an SVG element on the fly:
$('<g></g>')
Do instead:
$(document.createElementNS('http://www.w3.org/2000/svg', 'g'))
Here's your code updated and working.
I am trying to develop a javascript library that makes it easier for me to generate DOM Elements and edit their attributes. The problem I am having is that there is so many attributes for some elements that it is making for messy code. For instance I have to programmatically set the color of border, background, shadow, etc. using a method call before generation.
See setBorder Nested in function Div in jLibrary.php
function Div() {
Div.POSITION = {
STATIC : 'static',
ABSOLUTE : 'absolute',
RELATIVE : 'relative',
FIXED : 'fixed'
}
Div.BORDER = {
SOLID : 'solid',
DOTTED : 'dotted'
}
Div.ALIGN = {
LEFT : 0,
CENTER : 1,
RIGHT : 2,
TOP : 0,
MIDDLE : 1,
BOTTOM : 2
}
var ELEMENT;
var CSS;
var horizontalAlign;
var verticalAlign;
var colorQueue;
(function() {
this.div = document.createElement('div');
ELEMENT = this.div;
CSS = this.div.style;
colorQueue = 'rgb(' + new Array(0, 0, 0) + ')';
document.body.appendChild(this.div);
}());
this.setPosition = function(postype) {
if (!horizontalAlign && !verticalAlign) {
CSS.position = postype;
}
}
this.setBounds = function(x, y, width, height) {
CSS.left = x + 'px';
CSS.top = y + 'px';
CSS.width = width + 'px';
CSS.height = height + 'px';
}
this.setColorQueue = function(r, g, b) {
colorQueue = 'rgb(' + new Array(r, g, b) + ')';
alert(colorQueue);
}
this.setBackgroundColorToQueue = function(){
CSS.backgroundColor = colorQueue;
}
this.createShadow = function(x, y, width, height){
CSS.boxShadow = y + 'px ' + x + 'px ' + width + 'px ' + height + 'px ' + colorQueue;
}
this.createBorder = function(width,style){
CSS.border = width + 'px ' + style + ' ' + colorQueue;
/* Theoretical Method.
this.setColor = function(r,g,b){ //This will not work the way I want it...
CSS.border = 'rgb(' + new Array(r, g, b) + ')';
}
*/
}
this.rotateDiv = function(degrees){
CSS.transform = 'rotate(' + degrees + 'deg)';
}
this.horizontalAlign = function(horiz) {
var freeSpaceX = ((window.innerWidth - ELEMENT.offsetWidth) / 2);
var defPadding = '8px';
var defPaddingCenter;
var defPaddingRight;
var defPaddingLeft;
horizontalAlign = true;
if (CSS.position == 'relative' || CSS.position == 'static' || CSS.position == 'absolute') {
CSS.position = 'absolute';
defPaddingCenter = 4;
defPaddingRight = 4;
defPaddingLeft = 8;
} else if (CSS.position == 'fixed') {
defPaddingCenter = 4;
defPaddingRight = 4;
defPaddingLeft = 8;
}
if (horiz == 0) {
if (!verticalAlign) {
CSS.marginTop = defPadding;
}
CSS.marginLeft = defPaddingLeft + 'px';
} else if (horiz == 1) {
if (!verticalAlign) {
CSS.marginTop = defPadding;
}
CSS.marginLeft = freeSpaceX - defPaddingCenter + 'px';
} else if (horiz == 2) {
if (!verticalAlign) {
CSS.marginTop = defPadding;
}
CSS.marginLeft = (freeSpaceX - defPaddingRight) * 2 + 'px';
}
}
this.verticalAlign = function(vertical) {
var freeSpaceY = ((window.innerHeight - ELEMENT.offsetHeight) / 2);
var defPadding = '8px';
var defPaddingTop;
var defPaddingMid;
var defPaddingBot;
verticalAlign = true;
if (CSS.position == 'relative' || CSS.position == 'static') {
CSS.position = 'absolute';
}
defPaddingTop = 8;
defPaddingMid = 8;
defPaddingBot = 8;
if (vertical == 0) {
if (!horizontalAlign) {
CSS.marginLeft = defPadding;
}
CSS.marginTop = defPadding;
} else if (vertical == 1) {
if (!horizontalAlign) {
CSS.marginLeft = defPadding;
}
CSS.marginTop = freeSpaceY - defPaddingMid + 'px';
} else if (vertical == 2) {
if (!horizontalAlign) {
CSS.marginLeft = defPadding;
}
CSS.marginTop = (freeSpaceY * 2) - defPaddingBot + 'px';
}
}
}
setBorder Example in index.php
var div1 = new Div();
div1.setPosition(Div.POSITION.ABSOLUTE);
div1.setBounds(0,700, 200,200);
div1.setColorQueue(0,0,0); //This method must be called every time a different color is needed for a certain attribute.
div1.createBorder(5, Div.BORDER.SOLID); // I really want something like this --> div1.createBorder(5,Div.BORDER.SOLID).setColor(0,0,0);
You can try using the Builder pattern:
function DivBuilder() {
var color;
var border;
var position;
var bounds;
this.DivBuilder = function() {}
this.color = function(c) {
//set the color
this.color = c;
return this;
}
this.border = function(b) {
//set the border
this.border = b;
return this;
}
this.position = function(p) {
//set position
this.position = p;
return this;
}
this.bounds = function(b) {
//set bounds
this.border = b;
return this;
}
this.build = function () {
//build the new Div object with the properties of the builder
var d = new Div(this);
return d;
}
}
and then:
var divb = new DivBuilder();
divb.position().bounds().border().color();
var div = divb.buid();
Main advantage over the telescopic constructor pattern (well, the adaptation of it to javascript) is that you can choose easier which property you want to initialize without having to create many different constructor cases.
If you want to write this.createBorder(...).setColor(...) It means that createBorder should return an object with the setColor method...
Thanks to Sebas I was able to come up with this... Please vote up Sebas if this helped you.
Nested in this.createBorder()
this.createBorder = function(width) {
CSS.border = width + 'px';
function DivBorderBuilder() {
this.setColor = function(r, b, g) {
alert('color');
CSS.borderColor = 'rgb(' + new Array(r, g, b) + ')';
return this;
}
this.setStyle = function(s){
CSS.borderStyle = s;
return this;
}
}return new DivBorderBuilder();
}
Creating Border in index.php
<script>
var div1 = new Div();
div1.setPosition(Div.POSITION.ABSOLUTE);
div1.setBounds(0,700, 200,200);
div1.createBorder(5).setStyle(Div.BORDER.SOLID).setColor(255,0,0);// Works Perfectly Now !
</script>
Is there an equivalent implementation of a Bubble Tree in D3? In the link I provided, the Bubble Tree was implemented in RaphaelJS and jQuery.
The straight answer to your question is no.
Using the resources at https://github.com/okfn/bubbletree/tree/master/build, the information you already know, and the information provided on http://d3js.org/ and through D3's documentation on GitHub, you should be able to conjure up your own bubble tree for D3!
This is a piece of JavaScript I used a long time ago to visualize binary tree data:
var updateVisual;
updateVisual = function() {
var drawTree, out;
drawTree = function(out, node) {
var col, gray, i, line, lineElt, lines, sub, _results, _results1;
if (node.lines) {
out.appendChild(document.createElement("div")).innerHTML = "<b>leaf</b>: " + node.lines.length + " lines, " + Math.round(node.height) + " px";
lines = out.appendChild(document.createElement("div"));
lines.style.lineHeight = "6px";
lines.style.marginLeft = "10px";
i = 0;
_results = [];
while (i < node.lines.length) {
line = node.lines[i];
lineElt = lines.appendChild(document.createElement("div"));
lineElt.className = "lineblock";
gray = Math.min(line.text.length * 3, 230);
col = gray.toString(16);
if (col.length === 1) col = "0" + col;
lineElt.style.background = "#" + col + col + col;
console.log(line.height, line);
lineElt.style.width = Math.max(Math.round(line.height / 3), 1) + "px";
_results.push(i++);
}
return _results;
} else {
out.appendChild(document.createElement("div")).innerHTML = "<b>node</b>: " + node.size + " lines, " + Math.round(node.height) + " px";
sub = out.appendChild(document.createElement("div"));
sub.style.paddingLeft = "20px";
i = 0;
_results1 = [];
while (i < node.children.length) {
drawTree(sub, node.children[i]);
_results1.push(++i);
}
return _results1;
}
};
out = document.getElementById("btree-view");
out.innerHTML = "";
return drawTree(out, editor.getDoc());
};
Just insert some circular elements and manipulate it a bit to style in a circular manor and you should have a good program set!
Here you go. I didn't add the text or decorations, but it's the meat and potatoes:
function bubbleChart(config) {
var aspectRatio = 1,
margin = { top: 0, right: 0, bottom: 0, left: 0 },
radiusScale = d3.scale.sqrt(),
scan = function(f, data, a) {
a = a === undefined ? 0 : a;
var results = [a];
data.forEach(function(d, i) {
a = f(a, d);
results.push(a);
});
return results;
},
colorScale = d3.scale.category20(),
result = function(selection) {
selection.each(function(data) {
var outerWidth = $(this).width(),
outerHeight = outerWidth / aspectRatio,
width = outerWidth - margin.left - margin.right,
height = outerHeight - margin.top - margin.bottom,
smallestDimension = Math.min(width, height),
sum = data[1].reduce(function(a, d) {
return a + d[1];
}, 0),
radiusFractions = data[1].map(function(d) {
return Math.sqrt(d[1] / sum);
}),
radiusNormalSum = radiusFractions.reduce(function(a, d) {
return a + d;
}, 0),
scanned = scan(function(a, d) {
return a + d;
}, radiusFractions.map(function(d) {
return d / radiusNormalSum;
}), 0);
radiusScale.domain([0, sum]).range([0, smallestDimension / 6]);
var svg = d3.select(this).selectAll('svg').data([data]),
svgEnter = svg.enter().append('svg');
svg.attr('width', outerWidth).attr('height', outerHeight);
var gEnter = svgEnter.append('g'),
g = svg.select('g').attr('transform', 'translate(' + margin.left + ' ' + margin.top + ')'),
circleRing = g.selectAll('circle.ring').data(data[1]),
circleRingEnter = circleRing.enter().append('circle').attr('class', 'ring');
circleRing.attr('cx', function(d, i) {
return smallestDimension / 3 * Math.cos(2 * Math.PI * (scanned[i] + scanned[i + 1]) / 2) + width / 2;
}).attr('cy', function(d, i) {
return smallestDimension / 3 * Math.sin(2 * Math.PI * (scanned[i] + scanned[i + 1]) / 2) + height / 2;
}).attr('r', function(d) {
return radiusScale(d[1]);
}).style('fill', function(d) {
return colorScale(d[0]);
});
var circleMain = g.selectAll('circle#main').data([data[0]]),
circleMainEnter = circleMain.enter().append('circle').attr('id', 'main');
circleMain.attr('cx', width / 2).attr('cy', height / 2).attr('r', radiusScale(sum)).style('fill', function(d) {
return colorScale(d);
});
});
};
result.aspectRatio = function(value) {
if(value === undefined) return aspectRatio;
aspectRatio = value;
return result;
};
result.margin = function(value) {
if(value === undefined) return margin;
margin = value;
return result;
};
return result;
}
var myBubbleChart = bubbleChart().margin({
top: 1,
right: 1,
bottom : 1,
left: 1
});
var data = ['Random Names, Random Amounts', [['Immanuel', .4], ['Pascal', 42.9], ['Marisa', 3.3], ['Hadumod', 4.5], ['Folker', 3.2], ['Theo', 4.7], ['Barnabas', 1.0], ['Lysann', 11.1], ['Julia', .7], ['Burgis', 28.2]]];
d3.select('#here').datum(data).call(myBubbleChart);
<div class="container">
<div class="row">
<div class="col-xs-12">
<div id="here"></div>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
You can use the pack layout , basically you can bind any data you want to the shapes in the graph and custom parameters for them to position well respect to each other. Another alternative would be the force layout.
The Issue
I am using the following example code to draw arrows on google map.
Here is the main overlay function that creates the svg files for the arrow.
// A small directable Arrow overlay for GMaps V3
// Bill Chadwick Feb 2012 after my previous V2 API version
// Free for any use as far as I am concerned
// Some public domain code VML/SVG utility code of Ben Appleton's is reused at the end of this file
var arrowOverlayMarkerCounter; //unique id counter for SVG arrow head markers
function ArrowOverlay(map, location, rotation, color, opacity, tooltip) {
this.map_ = map;
this.location_ = location;
this.rotation_ = rotation || 0.0;
var r = this.rotation_ + 90; //compass to math
this.dx_ = 20 * Math.cos(r * Math.PI / 180); //other end of arrow line to point
this.dy_ = 20 * Math.sin(r * Math.PI / 180);
this.color_ = color || "#0000FF";
this.opacity_ = opacity || 0.7;
this.tooltip_ = tooltip || "";
this.div_ = null;
this.setMap(map);
this.handle_ = null;//click event handler handle
if (arrowOverlayMarkerCounter == null)
arrowOverlayMarkerCounter = 0;
else
arrowOverlayMarkerCounter += 1;
this.svgId_ = "ArrowOverlay" + arrowOverlayMarkerCounter.toString();
}
ArrowOverlay.prototype = new google.maps.OverlayView();
ArrowOverlay.prototype.onAdd = function() {
// Create the DIV and set some basic attributes.
var div = document.createElement('DIV');
div.title = this.tooltip_;
div.style.cursor = "help";
var obj = this;
this.handle_ = google.maps.event.addDomListener(div, 'click', function() { google.maps.event.trigger(obj, "click") });
//set up arrow invariants
if (supportsVML()) {
var l = createVmlElement('v:line', div);
l.strokeweight = "3px";
l.strokecolor = this.color_;
l.style.position = 'absolute';
var s = createVmlElement("v:stroke", l);
s.opacity = this.opacity_;
s.startarrow = "classic"; // or "block", "open" etc see VML spec
this.vmlLine_ = l;
}
else {
// make a 40x40 pixel space centered on the arrow
var svgNS = "http://www.w3.org/2000/svg";
var svgRoot = document.createElementNS(svgNS, "svg");
svgRoot.setAttribute("width", 40);
svgRoot.setAttribute("height", 40);
svgRoot.setAttribute("stroke", this.color_);
svgRoot.setAttribute("fill", this.color_);
svgRoot.setAttribute("stroke-opacity", this.opacity_);
svgRoot.setAttribute("fill-opacity", this.opacity_);
div.appendChild(svgRoot);
var svgNode = document.createElementNS(svgNS, "line");
svgNode.setAttribute("stroke-width", 3);
svgNode.setAttribute("x1", 20);
svgNode.setAttribute("y1", 20);
svgNode.setAttribute("x2", 20 + this.dx_);
svgNode.setAttribute("y2", 20 + this.dy_);
//make a solid arrow head, can't share these, as in SVG1.1 they can't get color from the referencing object, only their parent
//a bit more involved than the VML
if (this.rotation_ >= 0) {
var svgM = document.createElementNS(svgNS, "marker");
svgM.id = this.svgId_;
svgM.setAttribute("viewBox", "0 0 10 10");
svgM.setAttribute("refX", 0);
svgM.setAttribute("refY", 5);
svgM.setAttribute("markerWidth", 4);
svgM.setAttribute("markerHeight", 3);
svgM.setAttribute("orient", "auto");
var svgPath = document.createElementNS(svgNS, "path"); //could share this with 'def' and 'use' but hardly worth it
svgPath.setAttribute("d", "M 10 0 L 0 5 L 10 10 z");
svgM.appendChild(svgPath);
svgRoot.appendChild(svgM);
svgNode.setAttribute("marker-start", "url(#" + this.svgId_ + ")");
}
svgRoot.appendChild(svgNode);
this.svgRoot_ = svgRoot;
this.svgNode_ = svgNode;
}
// Set the overlay's div_ property to this DIV
this.div_ = div;
var panes = this.getPanes();
panes.overlayImage.appendChild(this.div_);
}
ArrowOverlay.prototype.draw = function() {
var overlayProjection = this.getProjection();
var p = overlayProjection.fromLatLngToDivPixel(this.location_);
var div = this.div_;
if (!div)
return;
if (!div.style)
return;
// Calculate the DIV coordinates of the ref point of our arrow
var x2 = p.x + this.dx_;
var y2 = p.y + this.dy_;
if (supportsVML()) {
this.vmlLine_.from = p.x + "px, " + p.y + "px";
this.vmlLine_.to = x2 + "px, " + y2 + "px";
}
else {
this.svgRoot_.setAttribute("style", "position:absolute; top:" + (p.y - 20) + "px; left:" + (p.x - 20) + "px");
}
}
ArrowOverlay.prototype.onRemove = function() {
if (this.handle_ != null) {
google.maps.eventclear.removeListener(this.handle_);
}
this.div_.parentNode.removeChild(this.div_);
}
ArrowOverlay.prototype.setVisible = function(v) {
if (v)
this.show();
else
this.hide();
}
ArrowOverlay.prototype.getVisible = function(v) {
if (this.div_) {
return (this.div_.style.display == "");
}
return false;
}
ArrowOverlay.prototype.hide = function() {
if (this.div_) {
this.div_.style.display = "none";
}
}
ArrowOverlay.prototype.show = function() {
if (this.div_) {
this.div_.style.display = "";
}
}
ArrowOverlay.prototype.setPosition = function(l) {
this.location_ = l;
this.draw();
}
ArrowOverlay.prototype.getPosition = function() {
return this.location_;
}
ArrowOverlay.prototype.setHeading = function(h) {
this.rotation_ = h || 0.0;
var r = this.rotation_ + 90; //compass to math
this.dx_ = 20 * Math.cos(r * Math.PI / 180); //other end of arrow line to point
this.dy_ = 20 * Math.sin(r * Math.PI / 180);
if (!supportsVML()) {
this.svgNode_.setAttribute("x2", 20 + this.dx_);
this.svgNode_.setAttribute("y2", 20 + this.dy_);
}
this.draw();
}
ArrowOverlay.prototype.getHeading = function() {
return this.rotation_;
}
ArrowOverlay.prototype.setTooltip = function(t) {
this.tooltip_ = t;
}
ArrowOverlay.prototype.getTooltip = function() {
return this.tooltip_;
}
ArrowOverlay.prototype.toggle = function() {
if (this.div_) {
if (this.div_.style.visibility == "hidden") {
this.show();
} else {
this.hide();
}
}
}
ArrowOverlay.prototype.fromDivPixelToLatLng = function(x, y) {
var overlayProjection = this.getProjection();
return overlayProjection.fromDivPixelToLatLng(new google.maps.Point(x, y));
}
ArrowOverlay.prototype.fromLatLngToContainerPixel = function(p) {
var overlayProjection = this.getProjection();
return overlayProjection.fromLatLngToContainerPixel(p);
}
// SVG utils from here http://appleton-static.appspot.com/static/simple_poly.js
// by Ben Appleton of Google
var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
function supportsSVG() {
return document.implementation.hasFeature(
'http://www.w3.org/TR/SVG11/feature#Shape',
'1.1');
}
// VML utils from here http://appleton-static.appspot.com/static/simple_poly.js
// by Ben Appleton of Google
var VML_NAMESPACE = 'urn:schemas-microsoft-com:vml';
function createVmlElement(tagName, parent) {
var element = document.createElement(tagName);
parent.appendChild(element);
element.style['behavior'] = 'url(#default#VML)';
return element;
}
function supportsVML() {
if (supportsVML.result_ == null) {
if (!maybeCreateVmlNamespace()) {
return supportsVML.result_ = false;
}
// Create some VML. Its 'adj' property will be an object only when VML
// is enabled.
var div = document.createElement('DIV');
document.body.appendChild(div);
div.innerHtml = '<v:shape id="vml_flag1" adj="1" />';
var child = div.firstChild;
if (child) child.style['behavior'] = 'url(#default#VML)';
supportsVML.result_ = !child || (typeof child['adj'] == 'object');
div.parentNode.removeChild(div);
}
return supportsVML.result_;
}
function maybeCreateVmlNamespace() {
var hasVmlNamespace = false;
if (document.namespaces) {
for (var x = 0; x < document.namespaces.length; x++) {
var ns = document.namespaces(x);
if (ns.name == 'v') {
if (ns.urn == VML_NAMESPACE) {
hasVmlNamespace = true;
} else {
throw new Error('document namespace v: is required for VML ' +
'but has been reserved for ' + ns.urn);
}
}
}
if (!hasVmlNamespace) {
// Import namespace
hasVmlNamespace = true;
document.namespaces.add('v', VML_NAMESPACE);
}
}
return hasVmlNamespace;
}
Question
I can not figure out how to change the length of those arrow? Can I somehow add a length parameter to the function ArrowOverlay?
I would do something like this (scale also works with x, y):
<svg>
<g id="idG" transform="scale(1, 1)">
..your arrow
</g>
</svg>
document.getElementById("idG").setAttribute("transform", "scale(" + horizontalScale + "," + verticalScale + ")");
Example: http://k8.no-ip.org/stackoverflow/13540341.htm