I'd like to be able to both zoom and mouseover an element. Included is an example followed by more details about the scenario.
https://jsfiddle.net/pkerpedjiev/ny5ob3h2/4/
var svg = d3.select('svg')
var zoom = d3.behavior.zoom()
.on('zoom', draw)
svg.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', 400)
.attr('height', 400)
.attr('fill', 'transparent')
.call(zoom)
var xScale = d3.scale.linear()
.domain([0, 10])
.range([0,10])
zoom.x(xScale)
svg.append('text')
.attr('x', 50)
.attr('y', 100)
.text('Hi there')
.attr('visibility', 'hidden')
svg.append('circle')
.attr('cx', 50)
.attr('cy', 50)
.attr('r', 10)
//.attr('pointer-events', 'none')
.on('mouseover', function(d) {
svg.selectAll('text')
.attr('visibility', 'visible');
})
.on('mouseout', function(d) {
svg.selectAll('text')
.attr('visibility', 'hidden')
});
function draw() {
d3.selectAll('circle')
.attr('r', xScale(10));
}
The example just contains a circle and some text. The text is invisible unless the mouse is over the circle. If I scroll using the mouse wheel, the circle changes in size in response to the zoom behavior. If, however, the mouse is over the circle, zooming doesn't work.
Is there a way to fix this? Setting pointer-events to none on the circle fixes the zooming, but then the mouseover event doesn't get called.
Is there way to have both the circle's mouseover get called and be able to zoom while the mouse is over the circle?
Yes this is possible by giving the zoom on the circle also.
svg.append('circle')
.attr('cx', 50)
.attr('cy', 50)
.attr('r', 10)
.call(zoom)//giving on circle also
//.attr('pointer-events', 'none')
.on('mouseover', function(d) {
svg.selectAll('text')
.attr('visibility', 'visible');
})
.on('mouseout', function(d) {
svg.selectAll('text')
.attr('visibility', 'hidden')
});
working example here
Hope this helps!
EDIT
If you have lot of elements and you don't like to attach the zoom listener to all the elements then you can attach the zoom to the main group which holds everything.
Like this:
var svg = d3.select('svg').attr("x",500).attr("y",500).append("g")
Attach zoom listener to the group.
svg.call(zoom);
Working code here
Related
I have this piece of code in which circles are drawn, I need to put a text inside each circle, I would also like to know how I can put a certain size to each of the elements of the circle.
Thank you very much.
svg = d3.select(selector)
.append('svg')
.attr('width', width)
.attr('height', height);
// Bind nodes data to what will become DOM elements to represent them.
bubbles = svg.selectAll('.bubble')
.data(nodes, function (d) { return d.id; });
// Create new circle elements each with class `bubble`.
// There will be one circle.bubble for each object in the nodes array.
// Initially, their radius (r attribute) will be 0.
bubbles.enter().append('circle')
.classed('bubble', true)
.attr('r', 0)
.attr('fill', function (d) { return fillColor(d.group); })
.attr('stroke', function (d) { return d3.rgb(fillColor(d.group)).darker(); })
.attr('stroke-width', 2)
.on('mouseover', showDetail)
.on('mouseout', hideDetail);
// Fancy transition to make bubbles appear, ending with the
// correct radius
bubbles.transition()
.duration(2000)
.attr('r', function (d) { return d.radius; });
A good practice would be to create a group element for each bubble because they will be composed of two elements - a circle and text.
// Bind nodes data to what will become DOM elements to represent them.
bubbles = svg.selectAll('.bubble')
.data(nodes, function(d) {
return d.id;
})
.enter()
.append('g')
.attr("transform", d => `translate(${d.x}, ${d.y})`)
.classed('bubble', true)
.on('mouseover', showDetail)
.on('mouseout', hideDetail)
After that, circles and texts can be appended:
circles = bubbles.append('circle')
.attr('r', 0)
.attr('stroke-width', 2)
texts = bubbles.append('text')
.attr('text-anchor', 'middle')
.attr('alignment-baseline', 'middle')
.style('font-size', d => d.radius * 0.4 + 'px')
.attr('fill-opacity', 0)
.attr('fill', 'white')
.text(d => d.text)
// Fancy transition to make bubbles appear, ending with the
// correct radius
circles.transition()
.duration(2000)
.attr('r', function(d) {
return d.radius;
});
For hiding/showing text, you can use fill-opacity attribute and set it 0 when the text should be hidden, and 1 if it should be shown:
function showDetail(d, i) {
d3.select(this.childNodes[1]).attr('fill-opacity', 1)
}
function hideDetail(d, i) {
d3.select(this.childNodes[1]).attr('fill-opacity', 0)
}
example: https://jsfiddle.net/r880wm24/
Font icon in an SVG foreign element isn't working in IE11. Though it works in Chrome and Firefox. Here is my code
var svg = d3.select('#item svg');
svg.append('circle')
.attr('r', 50)
.attr('cx',100)
.attr('cy', 100)
.style('fill', 'lightgray');
svg.append('svg:foreignObject')
.attr('x', 100)
.attr('y', 100)
.append('xhtml:span')
.attr('class', ' glyphicon glyphicon-glass')
.style('fill', 'white');
If you open this fiddler in IE 11, you will see no icon on the circle. However html icon (outside of svg) works fine in IE11.
Thanks in advance.
Why not just use a <text> element?
https://jsfiddle.net/vyz3dgff/2/
var svg = d3.select('#item svg');
svg.append('circle')
.attr('r', 50)
.attr('cx',100)
.attr('cy', 100)
.style('fill', 'lightgray');
svg.append('svg:text')
.attr('x', 100)
.attr('y', 100)
.attr('class', 'glyphicon') // note no glyph selection
.attr('text-anchor', 'middle') // horizontal alignment
.attr('dy', '0.5em') // vertical alignment
.style('font-size', '48px')
.style('fill', 'white')
.text("\ue001"); // glyph as defined in the font
I'm trying to drag a circle along the x and y-axes but with a lock (don't allow diagonal dragging during the same drag event). I tried to achieve this by drawing two perpendicular rectangles and test whether the mouse is inside one of them, but it doesn't work as it starts jumping around and doesn't lock properly, and there's a problem with the intersection area as well. Has anyone encountered this problem before and has some sort of idea on how to achieve what I'm trying to do? Thank you very much in advance!
Code
var svg = d3.select("body").append("svg")
.attr("width", 200)
.attr("height", 200)
var circle = svg.append("circle")
.attr("cx", 100)
.attr("cy", 100)
.attr("r", 15)
.style("fill", "purple");
var rectH = svg.insert('rect', 'circle')
.attr('x', 85)
.attr('y', 0)
.attr('width', 30)
.attr('height', 200)
.attr('fill-opacity', 0.25)
var rectV = svg.insert('rect', 'circle')
.attr('x', 0)
.attr('y', 85)
.attr('width', 200)
.attr('height', 30)
.attr('fill-opacity', 0.25)
circle.call(d3.behavior.drag()
.on('drag', function(d) {
//
})
)
Here's a jsfiddle to illustrate better what I want to achieve:
Fiddle
Basically, during a drag event, the circle should be allowed to only move inside the two rectangles.
I'm using d3 to create a map and add some data on it in. So far, I managed to draw circles based on the data that I pull from database. What I want to do is, when I mouseover one of those circle, create a new bigger circle with some text on it. I was able to draw the bigger circle but couldn't figure out how to add the label or text on it.
This is how I add circles to the map.
for (var i = 0; i < data.length; i++) {
var coordinates = projection([data[i]["Longitude"], data[i]["Latitude"]]);
svg.append('svg:circle')
.attr('cx', coordinates[0])
.attr('cy', coordinates[1])
.attr('r', 5)
.attr('fill', 'black')
.attr('class', 'pClass')
.attr('id', data[i]["Id"])
.attr('dataId', data[i]["DataId"])
.on('mouseover', dataMouseover);
}
Here is the mouseover event
function dataMouseover() {
var id = $(this).attr('id');
var d= $.grep(data, function (e) { return e.Id == id; });
var coordinates = projection([d[0]["Longitude"], d[0]["Latitude"]]);
svg.append('svg:circle')
.attr('cx', coordinates[0])
.attr('cy', coordinates[1])
.attr('r', 120)
.attr('fill', 'darkblue')
.attr('class', 'pClass')
.attr('id', data[0]["Id"] + "popUp")
.attr('dataId', plaques[0]["DataId"])
.attr("stroke", "white")
.attr("stroke-width", "5")
.on('mouseout', function () {
d3.select(this).remove();
});
}
So, I'm also removing the bigger circle when mouse is out. What I want is to put a text in that circle from the data while drawing it in there.
UPDATE: I updated my code to change current circle's radius instead of drawing new one.
for (var i = 0; i < data.length; i++) {
var coordinates = projection([data[i]["Longitude"], data[i]["Latitude"]]);
svg.append('svg:circle')
.attr('cx', coordinates[0])
.attr('cy', coordinates[1])
.attr('r', 5)
.attr('fill', 'black')
.attr('class', 'pClass')
.attr('id', data[i]["Id"])
.attr('dataId', data[i]["DataId"])
.on('click', function () {
$("#dialog").dialog('open');
})
.on('mouseover', function (data) {
var sel = d3.select(this);
sel.moveToFront();
d3.select(this)
.transition()
.duration(200)
.attr('fill', 'darkblue')
.attr('r', 120)
.attr('stroke', 'white')
.attr('stroke-width', '5')
})
.on('mouseout', function () {
d3.select(this)
.transition()
.duration(200)
.attr('fill', 'black')
.attr('r', 5)
.attr('stroke', 'none')
.attr('stroke-width', '0')
});
}
Still could use some guidance on how to use g element to cover both circle and text in my case.
You can't actually add text inside an svg circle. I discovered this to my own chagrin as well a few weeks ago. :\
Instead, encapsulate both the circle and the text inside a g element. Here's a link to a SO post explaining exactly how to do that: d3 add text to circle
Steps to going about doing this:
On hover: Instead of appending a circle, first append a g element.
Then append a circle element to that g element
Then append a text element to that g element
??? (stylize the elements as you will...)
Profit from having it look like the text is inside the circle!
Also: instead of redrawing the entire circle on mouseover, see if you can just change the r attr of the circle on mouseover. Might save you some code & make your app update a bit faster.
I have been struggling past 2 days to achieve below requirements.
Circle is behaving in abnormal and indeterminate way.
Requirements
1.Text should come inside the circle when circle is full hovered.
2.When circle is hovered and you take mouse cursor on text,the circle and text should not flicker.
3.Invisible circle should come back to its previous radius.
Here is the code
d3.select("#outerG")
.insert("g", "#invisibleG").attr("class", "maincircles").append("circle")
.attr("cx",120)
.attr("cy", 120)
.attr("r", 30)
.attr("class", "circle")
.style('fill', '#19b2e8');
d3.select("#invisibleG").append("g")
.attr("class","invisibleCircle")
.append("circle")
.attr("cx",120)
.attr("cy",120)
.attr("r",30)
.attr("class","inviCircle")
.style('fill-opacity', '0')
.attr("prevRadius",30)
.attr("xcoords",120)
.attr("ycoords",120)
.on("mouseover",function(){
var r=d3.select(this).attr("r");
d3.select(this).style('fill','#19b2e8')
.style('fill-opacity','1')
.transition()
.duration(1000)
.attr("r",50);
var prevRadius=d3.select(this).attr("prevRadius");
var xcoord= d3.select(this).attr("xcoords");//to centre text
var ycoord= d3.select(this).attr("ycoords");//to centre text
d3.select(this.parentNode).append("text")
.transition()
.duration(1000)
.text("Hello")
.attr("x",120)
.attr("y",120)
.style("font-family","sans-serif")
.style("font-size","20px")
.style("text-anchor","middle");
})
.on("mouseout",function(){
d3.select(this.parentNode).select("text").remove();
//d3.select(this).interrupt();
var r=d3.select(this).attr("prevRadius");
d3.select(this)
.transition()
.duration(1000)
.attr("r",r).attr("stroke-width",0)
.style('fill-opacity','0');
});
here is the HTML
<svg width="300" height="300">
<g id="outerG">
<g id="invisibleG"></g>
</g>
</svg>
Check fiddle http://jsfiddle.net/razC9/5/
Problem is that text is messing up mouse events on mouseover and solution is to add this css
svg text {
pointer-events: none;
}
Here is how it looks like now - http://jsfiddle.net/razC9/7/
Based on your requirements, here is a working fiddle. It is done with 1 circle, instead of two, drying up the code some. EDIT: Timing set to flash text once radius is fully expanded:
http://jsfiddle.net/razC9/42/
Key snippet:
.on("mouseover", function(){
d3.select(this)
.transition().attr("r", 50).duration(750)
.style("fill", "#19b2e8")
.style('fill-opacity','1');
d3.select("#outerG") .append("text")
.transition().delay(750)
.attr("class", "text")
.text("Hello")
.attr("x", 100)
.attr("y", 120)
.attr("fill", "black");
})
.on("mouseout", function(){
d3.select(this)
.transition().attr("r", 20)
.style("fill", "#19b2e8");
d3.select("#outerG").select(".text").remove();
});