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');
}
Related
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.
i am building a d3 Graph based on a json-File with an svg and groups:
Presentation.prototype.drawGraph = function() {
var _this;
this.margin = {
top: 0,
right: 0,
bottom: 0,
left: 0
};
this.width = 1000 - this.margin.left - this.margin.right;
this.height = 720 - this.margin.top - this.margin.bottom;
this.singleWidth = 50;
this.singleHeight = 32;
_this = this;
return d3.json("result.json", (function(_this) {
return function(err, json) {
if (err) {
console.error(err);
return;
}
// do something with the data
_this.x = d3.scale.ordinal().rangeBands([0, _this.width], 0, 0);
_this.y = d3.scale.linear().range([_this.height, 0]);
_this.svg = d3.select("#graph").append("svg").attr("width", _this.width + _this.margin.right + _this.margin.left).attr("height", _this.height + _this.margin.top + _this.margin.bottom).append("g").attr("transform", "translate(" + 0 + "," + 50 + ")");
_this.svg.append("rect").attr("x", -200).attr("y", -200).attr("width", 1200).attr("height", 1200).attr("fill", "#FFFFFF").on("click", function(data, index) {
_this.removeRenderedContent();
return _this.clickOnBackgroundRect();
});
_this.groups = _this.svg.selectAll("g").data(json).enter().append("g").attr("class", function(data, index) {return "group-case " + data.type;});
_this.groups.on("click", function(data, index) {
_this.removeRenderedContent();
_this.clickOnGroupCaseRect(data, index, this);
return false;
});
_this.groups.attr("transform", function(data, index) {
return "translate(" + data.x + "," + data.y + ")";
});
_this.groups.append("rect").attr("width", _this.widthFilter).attr("height", _this.heightFilter).attr("x", 0).attr("y", 0).attr("rx", 10).attr("ry", 10);
_this.groups.append("g").append("text").attr("y", 7).attr("x", 7).text(function(data) {return data.title;}).call(function(data, xpadding) {return _this.wrapGroup(data, xpadding);}, 7);
_this = _this;
return _this.groups.append("g").each(function(data, index) {
return _this.drawChildren(this, data, index);
});
};
})(this));
};
What i want to do is to make this graph responsive to the current browser-window size. Does anyone know the easiest way to do this?
Thanks in advance.
I'm developing a tool to make areas and tripwires and I'm working with Raphael JS. The thing is that I want to build a path dinamically and I think I got it, at least in Chrome, because in Firefox it gets crazy.
What I mean with crazy is that the position of the points do not correspond with the real position of the cursor (and I repeat, only in Firefox).
You have a fiddle I did here.
http://jsfiddle.net/6dd2870b/4/
Any thoughts on why can this be happening?
Thank you in advance.
The javascript code of the fiddle is:
var canvas = document.getElementById('canvas'),
paper = new Raphael(canvas, canvas.height, canvas.width),
mousedown = false,
lastX, lastY, path, pathString, pathAux, pointsAux = [];
$(canvas).mousedown(function (e) {
var x = e.offsetX,
y = e.offsetY;
if (!mousedown) {
pathString = 'M ' + x + ' ' + y + ' ';
mousedown = true;
} else {
pathString += 'l ' + (x - lastX) + ' ' + (y - lastY) + ' ';
}
pointsAux.push({x: x, y: y});
path = paper.circle(x,y,9);
path = paper.path(pathString);
lastX = x;
lastY = y;
});
$(canvas).mousemove(function (e) {
if (!mousedown) {
return;
}
var x = e.offsetX,
y = e.offsetY;
pathAux = pathString + 'l ' + (x - lastX) + ' ' + (y - lastY);
path.attr('path', pathAux);
});
$('#finish').click(function (e) {
if ($('#type').val() != 1) {
pathAux = pathString + ' z';
path.attr('path', pathAux);
} else {
path.attr('path', pathString);
}
mousedown = false;
});
$('#cancel').click(function (e) {
paper.clear();
mousedown = false;
});
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.
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.