I am using d3 to draw a grid of rectangles on a page.
There is an array that determines the color of each cell in the grid. Sometimes, elements of this array are updated to change their color.
Is there a d3 pattern or method to change only specific elements? I have a render function that re-renders the entire grid in d3, but I don't want to iterate over (and change the fill of) every rect element when only a few cells change color, as there could be thousands of cells.
I just checked the source and there doesn't seem to be. If performance was an issue, what I would do on every .enter call, is track any changes myself by setting a custom .attr('data-lastColor'). On every .data update call after that, I would do
.data(dataSet)
.attr('color', function(d){
var theElement = d3(this);
var newColor;
//set the new color here, under whatever conditions you want
if (foo==bar) {
newColor = 'green'
} else {
newColor = 'red'
}
if theElement.attr('data-lastColor') != newColor {
theElement.attr('color') = newColor;
theElement.attr('data-lastColor') = newColor;
} else {
//Don't do anything if the new color is the same as this color, saving valuable computing time
}
})
Sorry if the answer was a bit rough around the edges, but it should do what you want :)
Related
I created a sunburst chart with Highcharts. I would like a way to highlight a level, ideally by adjusting the color saturation, without affecting the color of proceeding levels.
What I've tried so far:
colorVariation(API docs)
my code example
Initially, this looked like what I needed, it adjusts the color brightness of a given level, either darkening or lightening it. Unfortunately, it only takes a to parameter, and no equivalent from, the purpose of colorVariation seems only to graduate the color from the parent color to the given brightness modifier value.
color(API docs)
my code example
color can be used to change the color of a given level, this doesn't quite meet the criteria of what I wanted as it changes each section on a level to the same color. It also causes the child levels to inherit the new color, as seen in the example. Setting colorByPoint doesn't fix this issue either as the child level will cycle through all colors rather than matching the colors set in the level 1 parent.
What I'm trying to achieve:
You can use internal Highcharts brighten method for brightening colors. Below, you can find an example of how to achieve the wanted result in your case:
chart: {
height: "600px",
events: {
load: function() {
Highcharts.each(this.series[0].points, function(p) {
if (p.id[0] === '1' || p.id[0] === '3') {
p.graphic.attr({
fill: Highcharts.color(p.color).brighten(0.3).get()
});
}
})
}
}
},
Live demo: http://jsfiddle.net/BlackLabel/de3mp4tq/
I inherited a project that was using d3.js and one of the charts was a line chart; I had to make a lot of changes to it, one being adding grid lines, I did this like so:
grid_wrap = plot.append('g').classed('grid-wrapper', true);
//......
chart = function() {
//.....
valueScale.domain([0, settings.value_scale_max]).range([plot_height, 0]);
grid_wrap.append("g")
.attr("class", "grid")
.attr("width", grid_width)
.call(make_y_axis(valueScale)
.tickSize(-grid_width)
.tickFormat("")
);
//.....
}
Note the chart function above gets called on a redraw.
function make_y_axis(valueScale) {
return d3.axisLeft()
.scale(valueScale)
.ticks(5);
}
Now, this draws the grid lines fine, however whenever the window is resized it uses a resize event trigger to redraw the graphs, however whilst everything else redraws correctly my grid lines instead get duplicated over and over ending up with several .grid elements.
I checked out how the other code is handling it and I understand it this way:
There are also these threshold elements on the graph and they are built this way:
thresholds = plot.append('g').classed('thresholds', true);
chart = function() {
//.....
valueScale.domain([0, settings.value_scale_max]).range([plot_height, 0]);
thresholds = plot.select('.thresholds').selectAll('.threshold').data(chart.Thresholds);
Uses data to populate the threshold elements.
new_thresholds = thresholds.enter().append('g').attr('class', 'threshold');
Now, as I understand it, thresholds won't contain any elements on the first draw, but on redraws will contain the already existing elements.
new_thresholds will then work on this data and add needed new .threshold elements to match the amount needed in the dataset since we are using the enter function here.
new_thresholds.append('rect').classed('threshold-rect', true).attr('x', 0).attr('y', 0).attr('width', plot_width);
Adding elements to our newly created elements.
thresholds.exit().remove();
Then as I understand it this removes any extra elements that are too many compared to the dataset we provided?
//.....
}
So I guess what I am asking is how can I achieve the same thing with the grid lines since it's not working on a dataset?
I was overthinking it, all I had to do was add this:
grid_wrap.selectAll('.grid').remove();
above the below code...
grid_wrap.append("g")
.attr("class", "grid")
.attr("width", grid_width)
.call(make_y_axis(valueScale)
.tickSize(-grid_width)
.tickFormat("")
);
This makes sure any gridlines are removed before and hence when they get created it only ends up with the one being there.
I am working on a visualization tool that uses a svg image of the brain. Now this svg has paths that are filled with a color. I want to loop over all these paths to set the fill color to white, but for some reason I cannot get the element.
The project can be seen here. The svg is inside a div and I even assigned an identifier brain to the div. The svg itself has an id svg2. So far I've tried the following:
function clearBrainColors() {
var brain = d3.select("#svg2");
console.log(brain);
var paths = brain.selectAll("path");
console.log(paths.length);
brain.selectAll('path').each(function(d,i) { console.log(this); });
}
But it outputs null in the array[0] component of the selection and 0 with paths.length.
I've also tried to use lines such as
var brain = d3.select("#brain svg"); and var brain = d3.select("#brain svg#svg2"); but those do not work either.
So, how can I select the brain svg using d3?
Decided to put the svg inline as it apparently speeds things up.
The code I used to fill the svg is now:
$("#svg2").find("path").each(function(){
$(this).css({ fill: "#ff0000" });
});
You can try setTimeOut() , following example
setTimeOut(function() {
var brain = d3.select("#svg2");
console.log(brain);
}, 1000);
this could be the svg is generate on the spot, d3 unable get at that moment.
Hope this help :)
As my cursor moves, I grab the two closest points IF it is not already on a plotted point.
I want to be able to make these two closest points light up (i.e. change colour to something orange or something), and then go back to normal once the cursor leaves the scope of the graph. How do I go about implementing this?
placeholder.bind("plothover", function (event, pos, item) {
if (item){
local_x = item.datapoint[0].toFixed(2);
local_y = item.datapoint[1].toFixed(2);
if (!updateLegendTimeout){
updateLegendTimeout = setTimeout(updateLegend(local_x,local_y), 1000);
updateLegendTimeout = null;
}
}
else{
var closest_points_container = interpolate(plot,pos.x,pos.y);
//Code to make points glow goes here, they are contained in closest_points[0]
and closest_points[1].
}
Flot provides highlight and unhighlight methods on the plot object, as described in the Plot Methods section of the docs. Unfortunately that has the restriction that only one point may be highlighted at a time. That is baked-in; you can't change it without altering the source.
The work-around I would use is to add a second series showing only points, no lines, and set the point style to appear as a highlight, i.e. with translucency. This highlight series will start out empty; then when you want to highlight a point on your main series you can copy it to the highlight series and redraw.
I'm using Raphael.js to draw images to canvas. I need to be able to select certain image elements (this I can do) and make them look like they are selected (this is the problem).
Before Raphael.js I used regular Html5 canvas and simple rectangles. It was simple to delete selected rectangle and draw a new one with a different color to that same place.
But now that I'm using images, it's a different story. The image I'm using is here. It's a small gif.
So the question(s):
Is there a simple way to change color of a Raphael.js image-element programmatically?
Can I make an image-element to blink by changing its opacity?
Only requirement is that the selected element must be movable.
Code for drawing image when user clicks on canvas:
var NodeImage = RCanvas.image("../vci3/images/valaisin.gif", position.x, position.y, 30, 30);
NodeImage.toFront();
RSet.push(NodeImage);
NodeImage.node.id = 'lamp';
NodeImage.node.name = name;
NodeImage.click(function() {
console.log("Clicked on node " + NodeImage.node.name);
// Here should be the code that blinks or changes color or does something else
});
Is this completely bad idea? Is there a better way to achieve my goal?
i would suggest granting the image with an opacity of some level, and assign a value of 1 to it upon click:
NodeImage.attr('opacity', 0.6);
// ...
NodeImage.click(function() {
this.attr('opacity', 1);
});
of course, you will probably want to manage the shape's selected state, to switch the selected style off later on. in fact, you'll want to manage all selectable shapes in the same manner, so let's do that:
// keep all selectable shapes in a group to easily manage them
var selectableShapesArray = [NodeImage, otherNodeImage, anotherSelectableShape];
// define the behavior for shape click event
var clickHandler = function() {
for (var i in selectableShapesArray) {
var image = selectableShapesArray[i];
if (image.selected) {
image.attr('opacity', .6);
image.selected = false;
break;
}
}
this.attr('opacity', 1);
this.selected = true;
}
// attach this behavior as a click handler to each shape
for (var i in selectableShapesArray) {
var shape = selectableShapesArray[i];
shape.click(clickHandler);
}