I am building an application in D3.js where I render several index cards (svg's) as one SVG. The user should be able to rotate those cards. For test purposes I used the drag behaviour for rotation
dragListener = d3.behavior.drag().on("dragstart", function(d){
originX = parseInt(d3.select(this).select("image").attr("x"));
originY = parseInt(d3.select(this).select("image").attr("y"));
})
.on("drag", function(d){
counter++;
if(counter > 360){
counter = 0;
}
d3.select(this).attr("transform",
"rotate("+counter.toString()+","+(originX+150).toString()+","+(originY+90).toString()+")");
});
which I provide to all index cards
var grp = svg.selectAll("g").data(arr);
var card =
grp.enter().append("g")
//.attr("transform", function(d){ return "rotate(30,"+(d.x+150)+","+(d.y+90)+")";})
.call(dragListener);
card.append("svg:image")
.attr("xlink:href", "http://images.clipartlogo.com/files/images/22/227702/index-card_p")
.attr("x", function(d){return d.x;})
.attr("y", function(d){return d.y;})
.attr("width", "300")
.attr("height", "180");
The problem is, the more cards are already rotated the lower the performance when the user rotates a card . The FPS are around 8 in Chrome when all cards are rotated.
Here is the example: http://jsfiddle.net/45hdjkk0/6/
You can give all cards an initial rotation by removing the comments in line 32.
Am I doing something wrong or did I choose the wrong approach?
UPDATE:
After testing the same example on the Internet Explorer and on Firefox I have found out that Internet Explorer is the fastest among those three. Firefox is also very fast compared to Chrome. Is this a Chrome issue?
Rotating any bitmap is computationally expensive. I ran your fiddle in Firefox and Chrome. Though Firefox wasn't too bad, I did see some of the frame-rate slowdown in Chrome you spoke of. There may not be a good solution if it has to be a png. If it doesn't have to be png, you will get great performance rotating an svg composed of your combined graphics elements.
Edit: It looks like you really do want to rotate index cards, so I think you are in luck. To illustrate what I'm talking about, comment out line 36 and then uncomment line 32. Then see the rotation performance without the bitmap. Then simply add in a few .attr("path", ...) lines to reproduce your index card and I bet the performance will rock.
Related
I'm plotting thousands of SVG circle elements on chrome with d3, and am running into weird rendering issues. It seems that paths are being drawn between the circles. These elements have mouseover events, and when mousing over them, that small portion of the screen redraws and removes the weird paths from that area, but after mouseout, it goes back to the weird lines.
The expected behavior works on Safari, but nothing is drawn at all in Firefox.
I've had a hard time figuring out how to get this to work on Chrome. I previously made a smaller instance of the behavior disappear by removing the 'crispEdges' attribute in the circle's CSS, but that is not working anymore. Also, I have been able to successfully render these points in Chrome before- not sure why it's starting to act like this now.
Anyone have any ideas about why this is happening and what I can do to get this working on Chrome? Any input is greatly appreciated!
==============
Chrome Version: 56.0.2924.87
Safari Version: 10.0
JS code for drawing circles:
var xScale = d3.scaleLinear()
.domain([0, xMax])
.range([0, width - margin.right]);
var yScale = d3.scaleLinear()
.domain([0, yMax])
.range([height - margin.top, 0]);
var x = function(d) { return xScale(d.x); }
var y = function(d) { return yScale(d.y); }
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", x)
.attr("cy", y)
.style("fill", colors)
.on("mouseover", function(d,i){
tooltip.html(toolTipHTML(d))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
return tooltip.style("visibility", "visible");
})
.on("mouseout", function(d){
return tooltip.style("visibility", "hidden");
});
CSS:
circle {
fill: black;
stroke: none;
shape-rendering: auto;
}
In Chrome:
In Safari:
===========
EDIT: Small demo on Plunker
I was able to reproduce the issue once on plunkr, but then after refresh, it went away. I tried to add more data to get it to reproduce again, but received a '400- Bad Request' response from plunkr. Sometimes when you zoom in on the circles, the random 'extraneous lines' show up, but they go away randomly. Anyways, here's the code if it helps. The original issue I've explained above is still an issue, even after refreshing multiple times. Any thoughts on how to move forward with this is greatly appreciated!
==============
EDIT 2/9: This may potentially be a hardware issue.
I'm using a Macbook Air, early 2015; running macOS Sierra 10.12.
As I mentioned in the comments, I recorded a short video of the strange behavior that happens when I zoom into the Plunker demo, in case it gives a better description of the issue than I've been able to give with words alone:
https://www.youtube.com/watch?v=saAm6Rim0zw&feature=youtu.be
For anyone following this post, as of about 2 days ago, a bug was reported on Chromium by someone else with the issue (Chrome on Mac):
Chromium Bug Report
I've generated a D3 visualization (a force directed graph) that requires zooming and panning. I've got 2 problems however when it comes to zooming, and I can't find any decent examples on how I might overcome these problems:
The first problem is I've followed all the examples I can find about zooming, which involves adding groupings and adding a rectangle to ensure that the entire area is zoomeable. If I style the rectangle a slightly opaque blue then I get SVG that looks like this when I zoom out:
The problem with this is that I can zoom in/out absolutely fine while I've got my mouse over the blue rectangle area. The problem is I want this to be fully opaque, which means that when I zoom right out, it's very easy to place the cursor outside of this box and then you're unable to zoom in. Is there a way I can make the SVG itself zoomeable or pick up on these events?
This is how I go about generating the various layers and the zoomed function:
function zoomed() {
group2.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
svg = d3.select(target)
.attr("pointer-events", "all")
.append("svg");
group = svg.append('svg:g')
.call(d3.behavior.zoom().on('zoom', zoomed))
.on("dblclick.zoom", null);
group2 = group.append("g");
rect = group2.append('svg:rect')
.style("opacity", 0.3)
.attr('width', width)
.attr('height', height);
The second problem I have is that I'm trying to automatically size my text based on this http://bl.ocks.org/mbostock/1846692 example. When I've tried this however I seem to be getting text that renders really poorly. It seems to suffer from:
Being difficult to read
Not appearing contained within the circle
Being so small the entire thing compresses (image 2)
var texts = planets.append("text")
.text(function(d) { return d.name; })
.style("font-size", "24px") // initial guess
.style("font-size", function(d) {
return Math.min( 2 * d.size, (2 * d.size - 8) / this.getComputedTextLength() * 24) + "px";
})
.attr("dx", function(d) { return -d.size; })
.attr("dy", ".35em")
.style("fill", "white");
I thought that SVG would just handle this, I understand that some of the font-sizes can come out small, but if you zoom in should that not all sort itself out?
I've got a JSFiddle http://jsfiddle.net/IPWright83/vo7Lpefs/22/ to demonstrate.
I've not yet managed to work out a resolution to my first issue (regarding the zooming box) however I did manage to track down the text rendering issue.
This was actually because the each circle/node had a stroke property to provide the white border. This was also applying to the text element, and when the font was very small the stroke was much larger than the overall fill of the text. Removing the stroke from the text elements ensured that they rendered even when very small.
I have a d3 visualisation on a web page that shows a collection of plot points on a chart.
It's all very simple really with just two axis and sometimes maybe 30 points to plot. As I render these plots I perform a transition on the r attributes of the SVG circles. The code that does this is:
g.append("svg:circle")
.attr("class", "plot-circle")
.attr("cx", xCo)
.attr("cy", yCo)
.attr("r", 0)
.transition()
.delay(function() { return delayInput * 100; })
.duration(plotExplosionDuration)
.ease(Math.sqrt)
.attr("r", 6);
All that occurs is that the circles are initially set to r=0 which means they aren't rendered at all. Then I start a transition on the appended circle to take this radius up to 6.
The problem is that it appears on some machines these transitions don't stop at r=6 and some plots end up being much bigger than the value set after the transition.
I simply cannot duplicate this on my main development machine (PC), my iPad nor my MacBook Pro which leads me to think it might be performance or machine load causing this?
has anyone got any ideas on how to ensure the transition stops at the defined final r value?
I have a zoomable map of the world with a single point on it (in reality there are multiple points from a separate resource but I have simplified it). The block is here.
When I try and zoom in it jumps to a certain scale and then usually doesn't allow any more zooming movements. I have experimented with various different values for the transition, scale and scaleExtent taken from this example and this one (with the latter being very close to what I want overall) but nothing has worked. It seems to get quite close to the actual size at height/6 for minimum zoom but still behaves badly.
I suspect the main problem is with scaleExtent. I actually want the minimum zoom to be the size of the map and so it isn't possible to pan around unless zoomed in.
The other problem is, as you can see in the bl.ock that the circle disappears when you zoom. I want the circle to maintain position and size (so it doesn't get bigger when I zoom).
Can any one help with
The zoom problem on the map, so the map minimum zoom is the actual size map and I can zoom in to about 6x that
Preventing the map from panning unless zoomed in
Maintaining the size and position of the circle on the map
I've put an example of what I think you're after on this bl.ock which is based on the first example you pointed to. It looks as though this line .scaleExtent([height, height*6]) is limiting the scale (well that the purpose of it) to something that was incompatible with your expectations and the initial scale you set, so when you zoom in past a certain level (in this case height) you get stick between height and height * 6.
If you set your minimum zoom and your initial zoom I think you'll get around some of your issues.
The issue with the dots was that they weren't referenced in the redraw function, so when you zoomed d3 / the browser didn't know what to do with them. In my example I've put the following snippet in to address this:
g.selectAll("circle")
.attr("cx", function (d,i) { return projection(d)[0]; })
.attr("cy", function (d,i) { return projection(d)[1]; })
.attr("r", "10px")
.style("fill", "red");
I'm attempting to integrate AngularJS with d3 for dragging and resizing. I've managed to create a rect object that is draggable in an SVG element, and resizable using resize handles. The resize handles work as they should, but resizing is choppy when I try to resize in the north or east direction. I created the following Plunk as a demo of the issue: http://plnkr.co/tG19vpyyw0OHMetLOu2U. (I've simplified it to show the issue I've run into, so there's only one resize handle.)
Dragging works as it should, and resizing in the west and south directions works as well (not shown in the demo).
Figured I'd ask the community and see if anyone had run into this before. Thank you all.
The problem is that you're modifying the rect element itself and the enclosing g element. There's a very short delay between setting the size of the rect and the position of the g simply because this has to be done with two separate commands. During this delay, the cursor position relative the the drag rectangle changes, firing a new drag event with values that correspond to the inconsistent intermediate state. This is fixed immediately afterwards (as soon as the attributes of both elements have been adjusted) and a new drag event is fired that fixes the inconsistency, but it is noticeable as a flicker.
The easiest way to fix this is to change both size and position for the rect and nothing for the g element. This means adjusting the position of the drag rectangle as well and makes the code less nice, but avoids the timing/inconsistency problem.
So myrect becomes
var myRect = d3.select(document.createElementNS("http://www.w3.org/2000/svg", "rect"))
.attr("data-ng-width", "{{square.w}}")
.attr("data-ng-height", "{{square.h}}")
.attr("stroke", "yellow")
.attr("stroke-width", 3)
.attr("fill-opacity", 0)
.attr("data-ng-x", "{{square.x}}")
.attr("data-ng-y", "{{square.y}}");
and resizer
var resizer = myGroup.append("rect")
.attr("width", 5)
.attr("height", 5)
.attr("stroke", "blue")
.attr("stroke-width", 1)
.attr("fill-opacity", 0)
.attr("cursor", "nw-resize")
.attr("x", "{{square.x-2.5}}")
.attr("y", "{{square.y-2.5}}")
.call(nresize);
I've updated your code with this solution here.