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
Related
I want to create an interactive tutorial on web that can help students to understand the basic mechanics of skiing. The first thing I want to teach is the concept of conservation of mechanical energy. To do this, I want the students to interact with the skier. They should be able to move the skier up and down on the slope. And when they do this, the amount of potential energy should be calculated automatically. When the students drop the skier, the skier move/transit to the end of the slope where they can see the speed of the skier (ignoring applied forces such as air drag and friction).
When the students move the skier higher up on the slope, the slope should also follow the skier. I am coding the whole thing in D3.js. I don't know how I can do this at the moment, so I would appreciate if someone can guide me. This is my code thus far:
<!DOCTYPE html>
<html>
<head>
<title>Skier!</title>
<link rel="stylesheet" href="style.css" />
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="https://d3js.org/d3-path.v1.min.js"></script>
<script src="https://d3js.org/d3-shape.v1.min.js"></script>
</head>
<body>
<div>
<input type="range" id="nAngle" name="degrees" min="0" max="90">
<label for="degress">Angle</label>
</div>
</body>
<script>
// The values for my SVG canvas
let w = 960,
h = 500,
middleW = w/2,
middleH = h/2;
let svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h)
// Creating a Group element so I can interact with all the elements of the skier
let skier = svg.append('g')
// Main body of the skier
skier.append('rect')
.attr('x', middleW)
.attr('y', middleH)
.attr('width', 30)
.attr('height', 50)
.attr('rx', 15)
.attr('ry', 12)
.attr('fill', 'green')
.style("stroke", "black")
.style("stroke-width", 5) // set the stroke width // set the line colour
.attr("transform", "rotate(14, 50, 40)");
// Pole
skier.append('line')
.attr("x1", middleW - 100)
.attr("y1", middleH + 110)
.attr("x2", middleW - 10)
.attr("y2", middleH + 136)
.attr('stroke', 'black')
.style("stroke-width", 4);
// Skis
skier.append('line')
.attr("x1", middleW - 90)
.attr("y1", middleH + 170)
.attr("x2", middleW + 5)
.attr("y2", middleH + 190)
.attr('stroke', 'black')
.style("stroke-width", 4);
// Path generator of beanie
let arcBeanie = d3.arc()
.innerRadius(0)
.outerRadius(12)
.startAngle(0)
.endAngle(Math.PI);
skier.append('path')
.attr('d', arcBeanie)
.attr('fill', 'red')
.attr('stroke', 'black')
.style("stroke-width", 4)
.attr("transform", "translate(436, 334) rotate(-90)");
arcBeanie(); //
// Path generator of face
let arcFace = d3.arc()
.innerRadius(0)
.outerRadius(12)
.startAngle(0)
.endAngle(Math.PI);
skier.append('path')
.attr('d', arcFace)
.attr('fill', 'white')
.attr('stroke', 'black')
.style("stroke-width", 4)
.attr("transform", "translate(436, 334) rotate(90)");
arcFace(); //
// Legs
let arcLegs = d3.arc()
.innerRadius(22)
.outerRadius(24)
.startAngle(0)
.endAngle(Math.PI - 1.5);
skier.append('path')
.attr('d', arcLegs)
.attr('stroke', 'black')
.style("stroke-width", 4)
.attr("transform", "translate(415, 423) rotate(14)");
arcLegs(); //
// Arms
let arcArms = d3.arc()
.innerRadius(0.25)
.outerRadius(1)
.startAngle(0)
.endAngle(Math.PI - 1.4);
skier.append('path')
.attr('d', arcLegs)
.attr('stroke', 'black')
.style("stroke-width", 4)
.attr("transform", "translate(450, 360) rotate(170)");
arcLegs(); //
d3.selectAll("g")
.transition()
.attr('transform', 'translate(300,0) rotate(20, 300, 0)')
.attr('height', 600)
.style('fill', 'teal')
.duration(5000)
d3.select("#nAngle").on("input", function() {
update(+this.value);
});
// Initial starting angle of the skier
update(0);
// update the element
function update(nAngle) {
// adjust the text on the range slider
// d3.select("#nAngle-value").text(nAngle);
// d3.select("#nAngle").property("value", nAngle);
// rotate the skier
svg.select("g")
.attr("transform", "rotate("+nAngle+")");
}
</script>
</html>
As I am learning d3js I am trying to make a small program where there are (planets in solar system) as texts in the HTML DOM and there are ellipses(rings) around a circle (sun). I need to be able to drag the text and as they are dropped it should identify on which ellipse and append into the group. For now, it does not need to check if the location is correct just want to append a circle when the text gets dropped into the circle.
I looked at some Jquery UI and other samples, but I am not having much luck on this issue.
var svg = d3.select('body')
.append("svg")
.attr("id", "program")
.attr("height", 500)
.attr("width", 500);
var sun = svg.append("circle")
.attr("cx", 250)
.attr("cy", 250)
.attr("r", 25)
.attr("fill", "orange");
var mercg = svg
.append("g")
.attr("id", "mercury")
.append("ellipse")
.attr("class", "droppable")
.attr("cx", 250)
.attr("cy", 250)
.attr("rx", 55)
.attr("ry", 45)
.attr("fill", "none")
.attr("stroke", "black");
var venus = svg
.append("g")
.attr("id", "venus")
.append("ellipse")
.attr("class", "droppable")
.attr("cx", 250)
.attr("cy", 250)
.attr("rx", 85)
.attr("ry", 65)
.attr("fill", "none")
.attr("stroke", "black");
var drag = d3.behavior.drag()
.on("drag", dragged)
.on("dragend", dragend);
function dragged(d) {
d3.select(this).attr("cx", d3.event.x).attr("cy", d3.event.y);
}
function dragend(d) {
// Here, How do i find on what ring the item was dropped?
// I want the circle to be on the selected ring group
}
var ex = svg.append("circle")
.attr("transform", 'translate(0,0)')
.attr("cx", 279)
.attr("cy", 212)
.attr("r", 10)
.attr("fill", "blue")
.call(drag);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="mySidenav" class="sidenav">
<ul>
<li draggable="true" ondragstart="drag()" class="draggable"> Mercury</li>
<li draggable="true" class="draggable">Venus </li>
</ul>
</div>
In the end I am looking for something that looks like the image produced at the end.
My solution
You can drag-n-drop html to svg and find drop target. Now you can add if statement to detect if target is ellipse. Also you can add additional ellipse to each existing ellipses with opacity 0 and wider stroke-width parameter to avoid pixel hunting.
update: new fiddle you can drag-n-drop HTML elements to svg orbits and new circle will append (if you drop planet name on its orbit)
I'm trying to get an element with an appended image to transition using the d3.js library; I had successfully achieved this just using a plain circle that transitioned nicely around the screen but now that I've added a png the transition doesn't happen - the png does appear though when the page is refreshed, it just won't move like it did before! My code is below.. your help is appreciated!
<script>
var data = [60, 120, 40, 710, 560, 850];
var data1 = data[0];
var canvas = d3.select("body").append("svg")
.attr("width", 2000)
.attr("height", 2000);
var imgs = canvas.append("image")
.attr("xlink:href", "AWT-Bus.png")
.attr("x", "60")
.attr("y", "60")
.attr("width", "20")
.attr("height", "20")
.attr("cx", 50)
.attr("cy", 200)
.attr("r", 20)
;
imgs.transition()
.duration(data1*100)
.delay(2000)
.attr("cx", 200)
.transition()
.attr("cx", 50)
.attr("cy", 200)
.transition()
.attr("cx", 150)
.attr("cy", 300)
;
The attributes you are changing in your code (cx and cy) are applicable to circles which are described by the x and y co-ordinates of their center (cx and cy) plus the radius (r). This is why your circle example worked.
But images are described by their width, height and the x and y co-ordinates of the upper-left corner of the box (using x and y attributes as shown below).
Different svg elements have different attributes which describe their dimensions and their location on the page, so you need to be aware of the different attributes that each type of element has, perhaps using a reference such as https://developer.mozilla.org/en-US/docs/Web/SVG/Element. Then you can animate your svg element using transition as you have done in your code and changing the value of the appropriate attribute.
var canvas = d3.select("body").append("svg")
.attr("width", 2000)
.attr("height", 2000);
var imgs = canvas.append("image")
.attr("xlink:href", "AWT-Bus.png")
.attr("x", "60")
.attr("y", "60")
.attr("width", "20")
.attr("height", "20");
imgs.transition()
.duration(2000)
.delay(1000)
.attr("x", 200)
.transition()
.attr("x", 50)
.attr("y", 200)
.transition()
.attr("x", 150)
.attr("y", 300);
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
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.