I am interested in creating something like this. Usually we see people drawing a bubble - I am keen to draw the space to represent the bubble. I would perhaps place this mask/chart in a shared component -- that is conjoined only by a background image -- so maybe embed this in a bootstrap part like col-md-8.
I've added the the subtraction mask -- and some label/pointer stuff - but its not rendering.
http://jsfiddle.net/NYEaX/1525/
var data = [{
"label": "My Property Value over 3 yrs.",
"value": "148",
"direction": "up"
}]
so the json for this may be something like
$(document).ready(function() {
function maskMaker(el) {
var backcolor = $(el).data("color");
var backopacity = $(el).data("opacity");
// Set the main elements for the series chart
var svgroot = d3.select($(el)[0]).append("svg");
var mask = svgroot
.append("defs")
.append("mask")
.attr("id", "myMask");
mask.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "1200px")
.attr("height", 500)
.style("fill", "white")
.style("opacity", backopacity);
mask.append("circle")
.attr("cx", 550)
.attr("cy", 250)
.attr("r", 150);
var data = [{
label: "text",
x: 222,
y: 222
}]
//__labels
var labels = mask.append("g")
.attr("class", "labels")
//__ enter
var labels = labels.selectAll("text")
.data(data);
labels.enter()
.append("text")
.attr("text-anchor", "middle")
//__ update
labels
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
})
.text(function(d) {
return d.label;
})
.each(function(d) {
var bbox = this.getBBox();
d.sx = d.x - bbox.width / 2 - 2;
d.ox = d.x + bbox.width / 2 + 2;
d.sy = d.oy = d.y + 5;
})
.transition()
.duration(300)
labels
.transition()
.duration(300)
//__ exit
labels.exit().remove();
//__labels
//__labels
//__pointers
var pointers = mask.append("g")
.attr("class", "pointers")
pointers.append("defs").append("marker")
.attr("id", "circ")
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("refX", 3)
.attr("refY", 3)
.append("circle")
.attr("cx", 3)
.attr("cy", 3)
.attr("r", 3);
var pointers = pointers.selectAll("path.pointer")
.data(data);
//__ enter
pointers.enter()
.append("path")
.attr("class", "pointer")
.style("fill", "none")
.style("stroke", "black")
.attr("marker-end", "url(#circ)");
//__ update
pointers
.attr("d", function(d) {
if (d.cx > d.ox) {
return "M" + d.sx + "," + d.sy + "L" + d.ox + "," + d.oy + " " + d.cx + "," + d.cy;
} else {
return "M" + d.ox + "," + d.oy + "L" + d.sx + "," + d.sy + " " + d.cx + "," + d.cy;
}
})
.transition()
.duration(300)
pointers
.transition()
.duration(300)
//__ exit
pointers.exit().remove();
//__pointers
var svg = svgroot
.attr("class", "series")
.attr("width", "1200px")
.attr("height", "500px")
.append("g")
.attr("transform", "translate(0,0)")
var rect = svg
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "750px")
.attr("height", 500)
.attr("mask", "url(#myMask)")
.style("fill", backcolor);
}
//var el = $(".mask"); //selector
$('[data-role="mask"]').each(function(index) {
console.log("test")
maskMaker(this);
});
});
latest answer
http://jsfiddle.net/NYEaX/1535/
You need to do several things:
In SVG DOM have the label and the pointer after the rectangle with the mask (or the rectangle itself before them). This will make them topmost. There is no z-index in SVG.
Add a declaration of the marker to the same 'defs' node at the beginning of SVG
Set pointer target values d.cx and d.cy (in the example below I set them to ordinary values)
Implement enter-update-exit pattern differently. In your example code with comments '__ update' will only be executed for existing elements in the selection, whereas it is empty on first run. See https://bl.ocks.org/mbostock/3808218 on how to merge operations on just added elements and already existing ones.
labels.enter()
.append("text")
.attr("text-anchor", "middle")
//__ update
//labels
.attr("x", function(d) {
return d.x;
})
...
A working example here: http://jsfiddle.net/NYEaX/1528/
Related
I have a tooltip that has multiple different divs inside it. Each row is basically a div. The picture below is what I am aiming to achieve. The problem that i have encountered is that the 13 / 100 is one div, so i cant make the '39' red and the '/ 100' grey. It all has to be red or grey. Is there a way for me to do that inline? something like tooltip.selectAll(".tooltipScore").text(score(change color here) + " / 100")? I thought of one way where i create another div below the score, and using positioning move it left and up from its position, but i am trying to avoid that and it seems a little hacky.
import React, {Component, useRef, useEffect} from 'react';
import ExperienceScoresData from './experience_scores';
import * as d3 from "d3";
import { select, csv, selectAll} from 'd3';
import { extent, max, min } from "d3-array";
class Linechart extends Component {
constructor(props){
super(props)
this.createBarChart = this.createBarChart.bind(this)
}
componentDidMount() {
this.createBarChart()
}
componentDidUpdate() {
this.createBarChart()
}
createBarChart() {
var margin = {top: 85, right: 60, bottom: 60, left: 80},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var node = this.node
var divObj = select(node)
var svgObj = divObj
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
//Read the data
d3.csv("https://raw.githubusercontent.com/QamarFarooq/data-for-testing/main/5_OneCatSevNumOrdered.csv", function(data) {
// group the data: I want to draw one line per group
var sumstat = d3.nest() // nest function allows to group the calculation per level of a factor
.key(function(d) { return d.name;})
.entries(data);
//console.log(sumstat)
// Define the div for the tooltip
var tooltip = divObj
.append("div")
.attr("class","tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.style("background-color", "white")
.style("box-shadow","0 0 4px #000000")
.style("padding", "10px")
const monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
var d = new Date();
tooltip.append("div")
.attr("class", "tooltipDate")
.html(monthNames[d.getMonth()] + " " + "(" + d.getFullYear() + ")")
.style("font-size", "20px")
.style("text-align","center")
tooltip.append("div")
.attr("class", "tooltipName")
.style("text-align","center")
.style("color","grey")
tooltip.append("div")
.attr("class", "tooltipTitle")
.style("text-align","center")
.html("Customer Sentiment")
.style("padding-top","10px")
tooltip.append("div")
.attr("class", "tooltipScore")
.style("text-align","center")
.style("color",'DarkGrey')
.style("font-size", "20px")
tooltip.append("div")
.attr("class", "tooltipPerception")
.style("text-align","center")
// Add title for linechart
svgObj.append("text")
.attr("text-anchor", "end")
.attr("font-size", 25)
.attr("x", 110)
.attr("y", -50)
.text("Online Ratings");
// Add X axis --> it is a date format
var x = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return d.year; }))
.range([ 0, width ]);
svgObj.append("g")
.attr("transform", "translate(0," + height + ")")
.attr("stroke-width","0.3")
.style("opacity","0.5")
.call(d3.axisBottom(x).tickSize(-height).tickFormat('').ticks(5))
// ticks
svgObj.append("g")
.style("opacity","0.7")
.style("font", "14px times")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(5));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return +d.n; })])
.range([ height, 0 ]);
svgObj.append("g")
.attr("stroke-width","0.3")
.style("opacity","0.5")
.call(d3.axisLeft(y).tickSize(-width).tickFormat('').ticks(5))
// ticks
svgObj.append("g")
.style("opacity","0.7")
.style("font", "14px times")
.call(d3.axisLeft(y).ticks(5));
// Add X axis label:
svgObj.append("text")
.attr("text-anchor", "end")
.attr("font-size", 20)
.attr("x", width/2 + margin.left)
.attr("y", height + 50)
.style("fill", d3.color("grey"))
.text("Year Of Birth");
// Add Y axis label:
svgObj.append("text")
.attr("text-anchor", "end")
.attr("font-size", 20)
.attr("transform", "rotate(-90)")
.attr("x", -height/2 + 40)
.attr("y", -margin.left + 25)
.style("fill", d3.color("grey"))
.text("N-Value")
// color palette
var key = sumstat.map(function(d){ return d.key }) // list of group names
var color = d3.scaleOrdinal()
.domain(key)
.range(['#e41a1c','#377eb8','#4daf4a'])
// Add one DOT in the legend for each name.
svgObj.selectAll(".dots")
.data(key)
.enter()
.append("circle")
.attr("cx", function(d,i){ return 250 + i*120})
.attr("cy", -30)
.attr("r", 7)
.style("fill", function(d){ return color(d)})
// Add LABEL for legends of each dot.
svgObj.selectAll(".labels")
.data(key)
.enter()
.append("text")
.style("fill", d3.color("grey"))
.attr("x", function(d,i){ return 270 + i*120})
.attr("y", -28)
.text(function(d){ return d})
.attr("text-anchor", "left")
.style("alignment-baseline", "middle")
// Highlight individual line and show tooltip
var highlightAndShowTooltip = function(d) {
//show tooltip
tooltip.style("visibility", "visible");
var selected_line = d.key
// first every group turns grey
svgObj.selectAll(".line")
.transition().duration(200)
.style("opacity", "0.5")
svgObj.selectAll(".dot")
.transition().duration(200)
.style("opacity", "0.5")
// Second the hovered line takes its color
svgObj.selectAll("." + selected_line)
.transition().duration(200)
.style("stroke", color(selected_line))
.style("opacity", "1")
svgObj.selectAll("." + selected_line)
.transition().duration(200)
.style("stroke", color(selected_line))
.style("opacity", "1")
//Data for Tooltip
tooltip.selectAll(".tooltipName")
.html(d.key)
var score = 12 //this will be dynamic, for now i just set it to 12 to test it out
tooltip.selectAll(".tooltipScore")
.text(score + " / 100")
}
// UnHighlight and hide tooltip
var doNotHighlightAndHideTooltip = function(d) {
//hide tooltip
tooltip.style("visibility", "hidden")
//return other lines back to normal opacity
svgObj.selectAll(".line")
.transition().duration(200).delay(50)
.style("stroke", function(d){ return( color(d.key))})
.style("opacity", "1")
svgObj.selectAll(".dot")
.transition().duration(200).delay(50)
.style("stroke", function(d){ return( color(d.name))})
.style("opacity", "1")
}
// keep showing tooltip as cursor moves along line
var keepShowingTooltip = function(d) {
tooltip.style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px")
}
// Draw the line
svgObj.selectAll(".line")
.data(sumstat)
.enter()
.append("path")
.attr("class", function (d) { return "line " + d.key} ) // 2 class for each line: 'line' and the group name
.attr("fill", "none")
.attr("stroke", function(d){ return color(d.key) })
.attr("stroke-width", 4.5)
.attr("d", function(d){
return d3.line()
.curve(d3.curveMonotoneX)
.x(function(d) { return x(d.year); })
.y(function(d) { return y(+d.n); })
(d.values)
})
.on("mouseover", highlightAndShowTooltip)
.on("mousemove", keepShowingTooltip)
.on("mouseout", doNotHighlightAndHideTooltip )
// Draw dots on points
svgObj.selectAll(".dot")
.data(data)
.enter()
.append("circle")
.attr("class", function (d) { return "dot " + d.name } ) // 2 class for each line: 'line' and the group name
.style("fill","white")
.style("stroke-width", "3px")
.style("stroke", function (d) { return color(d.name) })
.attr("cx", function(d) {return x(d.year); })
.attr("cy", function(d) {return y(d.n); })
.attr("r", 5.5)
})
}
render() {
return <div ref={node => this.node = node} className="example_div"> </div>
}
}
export default Linechart;
Since .tooltipScore is a div, you can change this .text(score + " / 100") to use .html() instead. Then, you can use a span to colour the text differently:
var score = 12;
d3.select("body")
.append("div")
.attr("class", "tooltipScore")
.style("text-align", "center")
.style("color", 'DarkGrey')
.style("font-size", "20px")
.html("<span style='color: #cb9f9e;'>" + score + "</span> / 100")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
I am working on a build where its starting to look good in desktop, but the chart components are not handling a responisve requirement.
I know something like this will create a scaling affect
some of the charts I've made incorporate a masking effect. I've seen sometimes the opacity malfunction when trying to resize/touch these components. Also with the width/height for a chart like this would I pick up the current screensize/height to give this to the chart?
here is a fiddle.
http://jsfiddle.net/NYEaX/1555/
function maskMaker(el) {
var backcolor = $(el).data("color");
var backopacity = $(el).data("opacity");
var data = [{
"label": "Romance level in 2 years",
"x": 222,
"y": 100,
"value": 148
}]
// Set the main elements for the series chart
var svgroot = d3.select($(el)[0]).append("svg");
var defs = svgroot
.append("defs");
var mask = defs
.append("mask")
.attr("id", "myMask");
mask.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "1200px")
.attr("height", 500)
.style("fill", "white")
.style("opacity", backopacity);
var invisiblebubble = mask.append("circle")
.data(data);
invisiblebubble
.attr("cx", 550)
.attr("cy", 250)
.transition()
.duration(900)
.attr("r", function(d) {
return d.value;
});
defs.append(function() {
return mask.node().cloneNode(true)
})
.attr("id", "myMaskForPointer")
.select("rect")
.style("opacity", 1.0);
/*d3.select(defs.append(mask.node().cloneNode()))
.style("opacity", )*/
var svg = svgroot
.attr("class", "series")
.attr("width", "1200px")
.attr("height", "500px")
.append("g")
.attr("transform", "translate(0,0)")
var rect = svg
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "750px")
.attr("height", 500)
.attr("mask", "url(#myMask)")
.style("fill", backcolor);
//__labels
var centrallabel = svgroot.append("g")
.attr("class", "centrallabel")
.data(data);
centrallabel
.append("text")
.attr("text-anchor", "middle")
.attr("x", 550)
.style("opacity",0)
.attr("y", 250 + 10)
.text(function(d) {
return "200";
})
.transition()
.duration(2000)
.style("opacity", 1)
//__labels
var labels = svgroot.append("g")
.attr("class", "labels")
//__ enter
var labels = labels.selectAll("text")
.data(data);
labels.enter()
.append("text")
.attr("text-anchor", "middle")
//__ update
//labels
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y - 10;
})
.text(function(d) {
return d.label;
})
.each(function(d) {
var bbox = this.getBBox();
d.sx = d.x - bbox.width / 2 - 2;
d.ox = d.x + bbox.width / 2 + 2;
d.sy = d.oy = d.y + 5;
d.cx = 550;
d.cy = 250;
})
.transition()
.duration(300)
labels
.transition()
.duration(300)
//__ exit
labels.exit().remove();
//__labels
//__labels
//__pointers
var pointers = svgroot.append("g")
.attr("class", "pointers")
defs.append("marker")
.attr("id", "circ")
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("refX", 3)
.attr("refY", 3)
var pointers = pointers.selectAll("path.pointer")
.data(data);
//__ enter
pointers.enter()
.append("path")
.attr("class", "pointer")
.style("fill", "none")
.attr("marker-end", "url(#circ)")
.attr("mask", "url(#myMaskForPointer)")
//__ update
//pointers
.attr("d", function(d) {
if (d.cx > d.ox) {
return "M" + d.sx + "," + d.sy + "L" + d.ox + "," + d.oy + " " + d.cx + "," + d.cy;
} else {
return "M" + d.ox + "," + d.oy + "L" + d.sx + "," + d.sy + " " + d.cx + "," + d.cy;
}
})
.transition()
.duration(300)
pointers
.transition()
.duration(300)
//__ exit
pointers.exit().remove();
//__pointers
}
my very latest code is like this.
function maskMaker(el){
var backcolor = $(el).data("color");
var backopacity = $(el).data("opacity");
var height = $(el).data("height");
var width = $(el).data("width");
var labelName = $(el).data("label-name");
var bubbleValue = $(el).data("bubble-value");
var displaceLeft = $(el).data("displace-left");
var displaceTop = $(el).data("displace-top");
var data = [{
"label": labelName,
"x": displaceLeft,
"y": displaceTop,
"value": bubbleValue
}];
console.log("MASK data", data);
// Set the main elements for the series chart
var svgroot = d3.select($(el)[0]).append("svg");
// filters go in defs element
var defs = svgroot.append("defs");
var mask = defs.append("mask")
.attr("id", "myMask");
mask.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "100%")
.attr("height", "100%")
.style("fill", "white")
.style("opacity", backopacity);
var invisiblebubble = mask.append("circle")
.data(data);
//create a fixed bubble first
invisiblebubble
.attr("cx", "50%")
.attr("cy", "50%")
.attr("r", function(d) {
return d.value-20;
});
//now mask the fixed circle
var masker = defs.append(function() {
return mask.node().cloneNode(true)
})
.attr("id", "myMaskForPointer")
.select("rect")
.style("opacity", 0.8);
invisiblebubble
.attr("r", 10);
//animate this circle
invisiblebubble
.attr("cx", "50%")
.attr("cy", "50%")
.transition()
.duration(1800)
.attr("r", 10)
.transition()
.duration(900)
.attr("r", function(d) {
return d.value;
});
//apply the rest of the chart elements
var svg = svgroot
.attr("class", "series")
.attr("width", "1120px")
.attr("height", "500px")
.append("g")
.attr("transform", "translate(0,0)")
var rect = svg
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "100%")
.attr("height", "100%")
.attr("mask", "url(#myMask)")
.style("fill", backcolor);
/*
//__labels
var centrallabel = svgroot.append("g")
.attr("class", "centrallabel")
.data(data);
centrallabel
.append("text")
.attr("text-anchor", "middle")
.attr("x", 550)
.attr("y", 250 + 10)
.text(function(d) {
return "200";
})
*/
function addLabel(){
//__labels
var labels = svgroot.append("g")
.attr("class", "labels")
//__ enter
var labels = labels.selectAll("text")
.data(data);
labels.enter()
.append("text")
.attr("text-anchor", "middle")
//__ update
//labels
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y-10;
})
.text(function(d) {
return d.label;
})
.each(function(d) {
var bbox = this.getBBox();
d.sx = d.x - bbox.width / 2 - 2;
d.ox = d.x + bbox.width / 2 + 2;
d.sy = d.oy = d.y + 5;
d.cx = 550;
d.cy = 250;
})
.transition()
.duration(300)
labels
.transition()
.duration(300)
//__ exit
labels.exit().remove();
//__labels
}
function addPointer(){
//__pointers
var pointers = svgroot.append("g")
.attr("class", "pointers");
var dots = defs.append("marker")
.attr("id", "circ")
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("refX", 3)
.attr("refY", 3);
var pointers = pointers.selectAll("path.pointer")
.data(data);
//__ enter
pointers.enter()
.append("path")
.attr("class", "pointer")
.style("fill", "none")
.attr("marker-end", "url(#circ)")
.attr("mask", "url(#myMaskForPointer)")
//__ update
//pointers
.attr("d", function(d) {
if (d.cx > d.ox) {
return "M" + d.sx + "," + d.sy + "L" + d.ox + "," + d.oy + " " + d.cx + "," + d.cy;
} else {
return "M" + d.ox + "," + d.oy + "L" + d.sx + "," + d.sy + " " + d.cx + "," + d.cy;
}
})
.transition()
.duration(300)
pointers
.transition()
.duration(300)
//__ exit
pointers.exit().remove();
//__pointers
}
//delay for the mask
setTimeout(function(){
addLabel();
addPointer();
}, 1000);
}
maskMaker($this);
I am looking to animate this chart.
http://jsfiddle.net/NYEaX/1554/
var invisiblebubble = mask.append("circle")
.data(data);
invisiblebubble
.attr("cx", 550)
.attr("cy", 250)
.transition()
.duration(900)
.attr("r", function(d) {
return d.value;
});
I've animated the mask circle - looking to implement other animations/suggestions for the labels. If they tween like a pie chart, tween in an arc, fade in etc..
I did create a transition on the radius of the circle - kind of looked like the warner bros ending.
var invisiblebubble = mask.append("circle")
.data(data);
invisiblebubble
.attr("cx", 550)
.attr("cy", 250)
.transition()
.duration(2000)
.attr("r", 10)
.transition()
.duration(900)
.attr("r", function(d) {
return d.value;
});
How do I animate other features like the labels/pointers
I've managed to improve the inverse bubble chart with this code.
Where I have to set a fixed size for the circle first, mask it, then animate it - for the purpose of the labels.
function maskMaker(el){
var backcolor = $(el).data("color");
var backopacity = $(el).data("opacity");
var height = $(el).data("height");
var width = $(el).data("width");
var labelName = $(el).data("label-name");
var bubbleValue = $(el).data("bubble-value");
var displaceLeft = $(el).data("displace-left");
var displaceTop = $(el).data("displace-top");
var data = [{
"label": labelName,
"x": displaceLeft,
"y": displaceTop,
"value": bubbleValue
}];
console.log("MASK data", data);
// Set the main elements for the series chart
var svgroot = d3.select($(el)[0]).append("svg");
// filters go in defs element
var defs = svgroot.append("defs");
var mask = defs.append("mask")
.attr("id", "myMask");
mask.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "100%")
.attr("height", "100%")
.style("fill", "white")
.style("opacity", backopacity);
var invisiblebubble = mask.append("circle")
.data(data);
//create a fixed bubble first
invisiblebubble
.attr("cx", "50%")
.attr("cy", "50%")
.attr("r", function(d) {
return d.value-20;
});
//now mask the fixed circle
var masker = defs.append(function() {
return mask.node().cloneNode(true)
})
.attr("id", "myMaskForPointer")
.select("rect")
.style("opacity", 0.8);
//animate this circle
invisiblebubble
.attr("cx", "50%")
.attr("cy", "50%")
.attr("r", 10)
.transition()
.duration(900)
.attr("r", function(d) {
return d.value;
});
//apply the rest of the chart elements
var svg = svgroot
.attr("class", "series")
.attr("width", "1120px")
.attr("height", "500px")
.append("g")
.attr("transform", "translate(0,0)")
var rect = svg
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "100%")
.attr("height", "100%")
.attr("mask", "url(#myMask)")
.style("fill", backcolor);
/*
//__labels
var centrallabel = svgroot.append("g")
.attr("class", "centrallabel")
.data(data);
centrallabel
.append("text")
.attr("text-anchor", "middle")
.attr("x", 550)
.attr("y", 250 + 10)
.text(function(d) {
return "200";
})
*/
function addLabel(){
//__labels
var labels = svgroot.append("g")
.attr("class", "labels")
//__ enter
var labels = labels.selectAll("text")
.data(data);
labels.enter()
.append("text")
.attr("text-anchor", "middle")
//__ update
//labels
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y-10;
})
.text(function(d) {
return d.label;
})
.each(function(d) {
var bbox = this.getBBox();
d.sx = d.x - bbox.width / 2 - 2;
d.ox = d.x + bbox.width / 2 + 2;
d.sy = d.oy = d.y + 5;
d.cx = 550;
d.cy = 250;
})
.transition()
.duration(300)
labels
.transition()
.duration(300)
//__ exit
labels.exit().remove();
//__labels
}
function addPointer(){
//__pointers
var pointers = svgroot.append("g")
.attr("class", "pointers");
var dots = defs.append("marker")
.attr("id", "circ")
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("refX", 3)
.attr("refY", 3);
var pointers = pointers.selectAll("path.pointer")
.data(data);
//__ enter
pointers.enter()
.append("path")
.attr("class", "pointer")
.style("fill", "none")
.attr("marker-end", "url(#circ)")
.attr("mask", "url(#myMaskForPointer)")
//__ update
//pointers
.attr("d", function(d) {
if (d.cx > d.ox) {
return "M" + d.sx + "," + d.sy + "L" + d.ox + "," + d.oy + " " + d.cx + "," + d.cy;
} else {
return "M" + d.ox + "," + d.oy + "L" + d.sx + "," + d.sy + " " + d.cx + "," + d.cy;
}
})
.transition()
.duration(300)
pointers
.transition()
.duration(300)
//__ exit
pointers.exit().remove();
//__pointers
}
//delay for the mask
setTimeout(function(){
addLabel();
addPointer();
}, 1000);
}
Fade in could be implementing like :
centrallabel
.append("text")
.attr("text-anchor", "middle")
.attr("x", 550)
.style("opacity",0)
.attr("y", 250 + 10)
.text(function(d) {
return "200";
})
.transition()
.duration(2000)
.style("opacity", 1)
As far as other animations you could start the pointer line off screen then transition it to it's endpoint. Or start with its length at 0 and transition it to full size. Transform/Translate would probably be useful - see (https://bl.ocks.org/mbostock/1345853)
I have created a horizontal bar chart using d3.js,the data is also inside the js file only.
But now i want to have a separate json file,from there i need to retrieve the data and use in js file.
I have tried but did not able to access the data from json file.
Here is the code.
Html
<head>
<title>Enjalot's Bar</title>
<script src="d3.v3.min.js"></script>
</head>
JavaScript
<script type="text/javascript">
var data= [{"label":"1990", "value":16},
{"label":"1991", "value":56},
{"label":"1992", "value":7},
{"label":"1993", "value":60},
{"label":"1994", "value":22}
];
var data_max = 60,
num_ticks = 6,
left_margin = 60,
right_margin = 60,
top_margin = 30,
bottom_margin = 0;
var w = 200, //width
h = 200, //height
color = function(id) { return '#00b3dc' };
var x = d3.scale.linear()
.domain([0, data_max])
.range([0, w - ( left_margin + right_margin ) ]),
y = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangeBands([bottom_margin, h - top_margin], .5);
var chart_top = h - y.rangeBand()/2 - top_margin;
var chart_bottom = bottom_margin + y.rangeBand()/2;
var chart_left = left_margin;
var chart_right = w - right_margin;
var vis = d3.select("body")
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("id", "barchart")
.attr("class", "barchart")
var rules = vis.selectAll("g.rule")
.data(x.ticks(num_ticks))
.enter()
.append("svg:g")
.attr("transform", function(d)
{
return "translate(" + (chart_left + x(d)) + ")";});
rules.append("svg:line")
.attr("class", "tick")
.attr("y1", chart_top)
.attr("y2", chart_top + 4)
.attr("stroke", "#939597");
rules.append("svg:text")
.attr("class", "tick_label")
.attr("text-anchor", "middle")
.attr("y", chart_top + 3)
.attr("fill","#939597")
.attr("font-size","0.667em")
.text(function(d)
{
return d;
});
var bbox = vis.selectAll(".tick_label").node().getBBox();
vis.selectAll(".tick_label")
.attr("transform", function(d)
{
return "translate(0," + (bbox.height) + ")";
});
var bars = vis.selectAll("g.bar")
.data(data)
.enter()
.append("svg:g")
.attr("class", "bar")
.attr("transform", function(d, i) {
return "translate(0, " + y(i) + ")"; });
bars.append("svg:rect")
.attr("x", right_margin)
.attr("width", function(d) {
return (x(d.value));
})
.attr("height", y.rangeBand())
.attr("fill", color(0))
.attr("stroke", color(0));
//Labels
var labels = vis.select("g.bar")
.append("svg:text")
.attr("class", "label")
.attr("x", 0)
.attr("text-anchor", "right")
.attr("transform", "rotate(270)")
.attr("y", 40)
.attr("x", -55)
.attr("dy", ".71em")
.text("Depts")
.style({"text-anchor":"end","font-size":"0.667em","fill":"#939597"});
var bbox = labels.node().getBBox();
labels = vis.selectAll("g.bar")
.append("svg:text")
.attr("class", "value")
.attr("fill","#fff")
.attr("font-size","0.667em")
.attr("x", function(d)
{
return 65;
})
.attr("text-anchor", "start")
.text(function(d)
{
return "" + d.value + "%";
});
bbox = labels.node().getBBox();
vis.selectAll(".value")
.attr("transform", function(d)
{
return "translate(0, " + (y.rangeBand()/2 + bbox.height/4) + ")";
});
//Axes
vis.append("svg:line")
.attr("class", "axes")
.attr("x1", chart_left)
.attr("x2", chart_left)
.attr("y1", chart_bottom)
.attr("y2", chart_top)
.attr("stroke", "#939597");
vis.append("svg:line")
.attr("class", "axes")
.attr("x1", chart_left)
.attr("x2", chart_right+120)
.attr("y1", chart_top)
.attr("y2", chart_top)
.attr("stroke", "#939597");
</script>
You'll need something like this to access a JSON file
d3.json("path/to/file.json", function(error, json) {
// since this function is asynchronous,
// you can't access data outside it
// So do pass the json into the the function that needs it
if (error) return console.warn(error);
someFunc(json);
});
function someFunc(data) {
....
}
Read this wiki for xhr request in D3.
I am unable to add a segment to a D3.js pie chart. I know I need to use .enter() and .append() to stage the new data -- but I am not sure how to apply that when I have the arcs grouped (which I need for the labels).
Here is my update function:
var updateChart = function(dataset) {
arcs.data(donut(dataset));
arcs.transition()
.duration(duration)
.attrTween("d", arcTween);
sliceLabel.data(donut(dataset));
sliceLabel.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; })
.style("fill-opacity", function(d) {
if (d.value === 0) { return 1e-6; }
else { return 1; }
});
};
How I setup the initial graph:
var arc = d3.svg.arc()
.innerRadius(radius * .4)
.outerRadius(radius);
var svg = d3.select("body")
.append("svg")
.append("svg")
.attr("width", width)
.attr("height", height);
var arc_grp = svg.append("g")
.attr("class", "arcGrp")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var label_group = svg.append("g")
.attr("class", "lblGroup")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var arcs = arc_grp.selectAll("path")
.data(donut(data));
arcs.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.each(function(d) { return this.current = d; });
var sliceLabel = label_group.selectAll("text")
.data(donut(data));
sliceLabel.enter()
.append("text")
.attr("class", "arcLabel")
.attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; })
.attr("text-anchor", "middle")
.style("fill-opacity", function(d) {
if (d.value === 0) { return 1e-6; }
else { return 1; }
})
.text(function(d) { return d.data.label; });
Complete jsfiddle: http://jsfiddle.net/kPM5L/
What is a clean way to add the new data to the chart?
To get the transition to work smoothly, you need to add the code that you're using initially to your update function as well. Working jsfiddle here.
And some code to make SO happy -- this is what needs to be in the update function as well:
.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.each(function(d) { return this.current = d; });
.enter()
.append("text")
.attr("class", "arcLabel")
.attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; })
.attr("text-anchor", "middle")
.style("fill-opacity", function(d) {
if (d.value === 0) { return 1e-6; }
else { return 1; }
})
.text(function(d) { return d.data.label; });