How to put function into my image in svg using d3? - image

I have an rectangle and an image in it. I am trying to put a function to the image ( If i clicked it, a link to an websited appeared ) but i cant find a proper way.
This is my code
var svgContainer = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500);
var g = d3.select("svg") .append("g")
var rectangle = g.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 300)
.attr("height", 200)
.style("fill","pink");
var text = g.append("text")
.attr("x",150)
.attr("y",100)
.attr("text-anchor","middle")
.text('Nam oc cho')
var img = g.append("image")
.attr("x",200)
.attr("y",85)
.attr("href","firefox.jpg")
.attr("height","20px")
.attr("width","20px")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Thanks

Related

Make donut chart clickable

Can anybody help me regarding how I can make my following donut chart Clickable? I am just creat a donut chart from some dummy data and want each portion of the donut to be clickable. I am quite new in D3 and finding it hard to incorporate the click function in the donut chart.
I am finding it difficult to make each portion of the donut chart clickable in d3.js. Any help is appreciated. I have added my code snippet here.
function myFunction(width,height,margin,datax,dis,hg) {
var radius = Math.min(width, height) / 2 - margin
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", hg)
.append("g")
.attr("transform", "translate(" + width / dis + "," + height / 2 + ")");
var color = d3.scaleOrdinal()
.domain(Object.keys(data))
.range(d3.schemeDark2);
var pie = d3.pie()
.sort(null)
.value(function(d) {return d.value; })
var data_ready = pie(d3.entries(data))
var arc = d3.arc()
.innerRadius(radius * 0.5)
.outerRadius(radius * 0.8)
svg
.selectAll('allSlices')
.data(data_ready)
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d){ return(color(d.data.key)) })
.attr("stroke", "white")
.style("stroke-width", "2px")
.style("opacity", 0.7)
; }
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
You just need to select all path and bind event click on it
svg.selectAll('path')
.on('click', (d, i, n) => {
console.log(d, i, n)
})

Rotating an SVG image using D3.JS not working

I've been trying to rotate some gears in an svg image clockwise repeatedly using D3.js but I can't seem to understand what's wrong with the code or the image. I was able to translate and rotate each gear using this code below...
d3.selectAll(".st6")
.transition()
.delay(function(d, i, n) { return i * 50; })
.on("start", function repeat() {
d3.active(this)
.transition()
.duration(2500)
.attr('transform', 'rotate(0)')
.transition() //And rotate back again
.duration(2500)
.attr('transform' , 'rotate(90) ')
.on("start", repeat); //at end, call it again to create infinite loop
});
But when I tried using the same code that did rotate a text repeatedly for me, the image became static and not moving again...
here is the code that rotate a text repeatedly for me...
var width = 600;
var height = 300;
var holder = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
//draw the text
holder.append("text")
.style("fill", "black")
.style("font-size", "56px")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("transform", "translate(300,150) rotate(0)")
.text("Hi, how r u doing");
var i = 0;
var timeInterval = 10;
setInterval(function(){
i += 1;
update(i % 360)
}, timeInterval);
var n;
// update the element
function update(n) {
// rotate the text
holder.select("text")
.attr("transform", "translate(300,150) rotate("+n+")");
}
Here is what I tried replicating the above method but to no avail...
var width = 600;
var height = 300;
var holder = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
holder.append("svg:image")
.attr("image-anchor", "middle")
.attr("xlink:href", "produc.svg")
.attr("transform", "translate(300,150) rotate(0)")
// Initial starting angle of the text
var i = 0;
var timeInterval = 10;
setInterval(function(){
i += 1;
update(i % 360)
},timeInterval);
var n;
// update the element
function update(n) {
// rotate the text
holder.select("text")
.attr("transform", "translate(300,150) rotate("+n+")");
}
Here is a working example on CodePenclick here
As for the problem with your code (without considering if it is "a sledgehammer to crack a nut"):
You're taking working code that rotates text and not fully updating it to reflect that you are now working with an image. You have updated the append statement to append an image, but you haven't updated the update function to select that image - it's still looking for a text element:
function update(n) {
// rotate the text
holder.select("text")
.attr("transform", "translate(300,150) rotate("+n+")");
}
Since there is no longer a text element, this function doesn't select anything and consequently, doesn't set any element's transform. AS noted in the comment you need to select the image:
function update(n) {
// rotate the text
holder.select("image")
.attr("transform", "translate(300,150) rotate("+n+")");
}
As seen below:
var width = 600;
var height = 300;
var holder = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
holder.append("svg:image")
.attr("image-anchor", "middle")
.attr("xlink:href", "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/compass.svg")
.attr("transform", "translate(200,20) rotate(0)")
.attr("width", 100)
// Initial starting angle of the text
var i = 0;
var timeInterval = 10;
setInterval(function(){
i += 1;
update(i % 360)
},timeInterval);
// update the element
function update(n) {
// rotate the text
holder.select("image")
.attr("transform", "translate(200,20) rotate("+n+",50,50)");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

d3 click to center content at position of element or click

I have been trying to get the basics of how I make a pannable zoomable, and click to center zoom on element d3 work. This example is what I want to do but I am having trouble translating it outside of the geo context: https://bl.ocks.org/mbostock/2206340
What I have accomplished is the first two parts pan and zoom, see a basic fiddle here https://jsfiddle.net/e9fbn2xp/
How can I accomplish centering the the circle in the center of the viewable window, so it looks like the circle is zoomed to? Note that although this is a fixed position circle I will eventually have dynamic data, so ideally I could reference the circles position dynamically.
Here is my code:
HTML (note that this is React JSX syntax but that should be irrelevant to question)
<div style={{width: 800}}>
<svg style={{border: '1px solid black'}} id="viz" width="800" height="800">
</svg>
</div>
JAVASCRIPT
var svg = d3.select("#viz")
var width = svg.attr("width");
var height = svg.attr("height");
var testLayer = svg.append('g');
var aRect = testLayer.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("height", 800)
.attr("width", 800)
.attr("fill", 'green');
var aCircle = testLayer.append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", 40)
.attr("cx", 200)
.attr("cy", 200)
.on("mousedown", zoomToMe);
function zoomToMe(){
console.log("do the zoom")
}
var zoom = d3.zoom()
.scaleExtent([.5, 40])
.translateExtent([[0, 0], [width, height]])
.on("zoom", zoomed);
svg.call(zoom);
function zoomed() {
testLayer.attr("transform", d3.event.transform);
}
svg.on("click", function() {
var coords = d3.mouse(this);
})
I got a working solution and thought I would share the code in case others find it useful. It is a fairly different approach then my original but accomplishes the three goals, pan, mouse zoom, zoom to element. While these are three simple static circles the same concept should work with a dynamic dataset.
fiddle: https://jsfiddle.net/uc7oprx3/5/
HTML
<svg id="viz" width="400" height="400" />
JAVASCRIPT
var zoom = d3.zoom()
.scaleExtent([0.3,2])
.on("zoom", zoomed);
var svg = d3.select("#viz")
var width = svg.attr("width");
var height = svg.attr("height");
var zoomer = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.call(zoom);
var g = svg.append("g");
var aCircle = g.append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", 40)
.attr("cx", 200)
.attr("cy", 200)
.on("mousedown", () => centerNode(200, 200));
var bCircle = g.append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", 40)
.attr("cx", 400)
.attr("cy", 400)
.on("mousedown", () => centerNode(400, 400));
var cCircle = g.append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", 40)
.attr("cx", 600)
.attr("cy", 600)
.on("mousedown", () => centerNode(600, 600));
function zoomed() {
g.attr("transform", d3.event.transform);
}
function centerNode(xx, yy){
g.transition()
.duration(500)
.attr("transform", "translate(" + (width/2 - xx) + "," + (height/2 - yy) + ")scale(" + 1 + ")")
.on("end", function(){ zoomer.call(zoom.transform, d3.zoomIdentity.translate((width/2 - xx),(height/2 - yy)).scale(1))});
}

D3js chained animations as function of JSON

I am learning D3js and trying to chain some animations together.
Basically I have three svg elements that I want to simultaneously transition. There is a circle, a rectangle, and a text element.
For example, I am trying to make the circle transition from black to green, the rectangle transition from black to blue, and replace the text with a fade out, fade in effect.
I can do this one time, but I want a series of these animations to occur in sequence.
Here is the first fiddle of 1 transition working:
http://jsfiddle.net/mgcdanny/6nbRK/
Here is my attempt to sequence these using a for loop and JSON, but only the last transition happens:
http://jsfiddle.net/mgcdanny/U7StQ/
Perhaps this would be better as a series of enter(), update(), exit() commands?
Thanks for the help!
Fiddle 1 Code:
`
var svg = d3.select("body")
.append('svg')
.attr('width', 500)
.attr('height', 500)
var circle = d3.select("svg")
.append("circle")
.attr('cx', 10)
.attr('cy', 10)
.attr('r', 10)
var rect = d3.select("svg")
.append("rect")
.attr('width', 20)
.attr('height', 20)
.attr('x', 30)
.attr('y', 30)
var text = d3.select("svg")
.append("text")
.text("hello")
.attr("x",60)
.attr("y",60)
.attr('opacity',1)
var tran = svg.transition().duration(2000)
tran.select("rect").attr('fill', 'red')
tran.select("circle").attr('fill', 'blue')
tran.select("text").attr('opacity', 0)
//new transition
svg.transition()
.duration(2000)
.delay(2000)
.select("text")
.attr('opacity', 1)
.text("hello again!")
.attr("x",60)
.attr("y",60)
</script>
</body>
`
Fiddle 2 Code:
'
theData = [
{"words":"today", "rect_color": "red", "circle_color":"blue"},
{"words":"tomorrow", "rect_color": "green", "circle_color":"yellow"},
{"words":"Day After That", "rect_color": "purple", "circle_color":"brown"}]
var svg = d3.select("body")
.append('svg')
.attr('width', 500)
.attr('height', 500)
var circle = d3.select("svg")
.append("circle")
.attr('cx', 10)
.attr('cy', 10)
.attr('r', 10)
var rect = d3.select("svg")
.append("rect")
.attr('width', 20)
.attr('height', 20)
.attr('x', 30)
.attr('y', 30)
var text = d3.select("svg")
.append("text")
.text("hello")
.attr("x",60)
.attr("y",60)
.attr('opacity',1)
var tranFunc = function(object){
var tran = svg.transition().duration(2000)
tran.select("rect").attr('fill', object.rect_color)
tran.select("circle").attr('fill', object.circle_color)
tran.select("text").attr('opacity', 0)
var tran2 = tran
tran2.transition().select("text")
.attr('opacity', 1)
.text(object.words)
}
var allTran = function(list){
for(var i = 0; i < list.length; i++){
tranFunc(list[i])
}
}
allTran(theData)
</script>
'

Showing Data in Rectangle on the click of circle using d3.js

I Have few circles that contain people names,I need to show their information on the click of circle in rectangle using d3.js
Below is my script
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
d3.json("data.json", function (json) {
/* Define the data for the circles */
var elem = svg.selectAll("g myCircleText")
.data(json.nodes)
/*Create and place the "blocks" containing the circle and the text */
var elemEnter = elem.enter()
.append("g")
.attr("transform", function (d) { return "translate(" + d.x + ",80)" })
/*Create the circle for each block */
var circle = elemEnter.append("circle")
.attr("r", function (d) { return d.r })
.attr("stroke", "black")
.attr("fill", "white")
.on("click", function () {
var s = svg
.selectAll("circle");
s
.append("rect")
.attr("x", 100)
.attr("y", 200)
.attr("width", 200)
.attr("width", 200)
.style("fill", "red");
});
/* Create the text for each block */
elemEnter.append("text")
.attr("dx", function (d) { return -20 })
.text(function (d) { return d.label })
})
below is the json file:
{"nodes":[
{"x":80, "r":40, "label":"Sam","info":"Developer"},
{"x":200, "r":60, "label":"Pam","info":"Programmer"},
{"x":380, "r":80, "label":"Ram","info":"Architect"}
]}
Circles are being drawn with names but when I click on circles nothing is happening.
Please help.
Thanks
Two issues with your onclick function. First, width is set a second time instead of height:
.attr("width", 200)
.attr("width", 200)
Second, you're appending a rectangle to a circle:
var s = svg.selectAll("circle");
s.append("rect")
which isn't valid for a svg:
<circle r="60" stroke="black" fill="white">
<rect></rect>
</circle>
Instead, the rectangles should be appended to root of the svg or a g element.
Working code:
.on("click", function () {
svg.append("rect")
.attr("x", 100)
.attr("y", 200)
.attr("width", 200)
.attr("height", 200)
.style("fill", "red");
});

Resources