Style D3 chart with CSS - css

Aiming to keep good separation of concerns between content and styling in my code, while acknowledging that creating charts with D3 blurs the lines a little, I am trying to figure out how to fully control the styling of a D3 chart with CSS. For example, consider the following where I create my SVG:
d3.select(container).append('svg')
.attr('id', 'mySvg')
.attr('width', window.innerWidth)
.attr('height', window.innerHeight).
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
Or where I create a scale:
d3.scaleTime().domain(d3.extent(myData)).range([ 0, this.width ]);
In both cases I am referencing style values in the code, i.e. in order to create my SVG chart I have to hardcode style-related values.
Is it possible to do all of this styling etc. with CSS?

const link = gLink.selectAll('path')
.data(links, d => d.target.id);
const linkEnter = link.enter().append('path')
.attr('stroke-width', function (d) {
return 10;
}
Try to provide Stroke width for the lines to increase the thickness

You can use CSS as much as you are able. The demands of a interactive chart sometimes don't make it easy though. The simplest solution, I find is:
d3.select(container).append('svg')
.attr('id', 'mySvg')
.classed('chart', true)
Now the svg element will have the class, .chart.

Related

Highcharts: How to add another(custom) label/ legend/ something else to the top right of the graph?

In my highcharts, I already have a legend at the bottom left of the graph but want to add a custom status indicator for the entire graph. It's going to be a colored oval with a colored dot and a status on it like in the picture.
Should I be trying to use a custom legend or something else? I don't need to hide/show abiliity of the legend so maybe I'm thinking a label is more appropriate but it seems like those only go in the graph. I want something on the top right of the graph. I'm looking through the API to see what else is available but haven't found one that suites my needs.
edit-
seems like I might have to do "chart.renderer.text" but I'm not sure how to convert and make it work in typescript http://jsfiddle.net/phpdeveloperrahul/HW7Rm/
function (chart) {
var point = chart.series[0].data[8],
text = chart.renderer.text(
'Max',
point.plotX + chart.plotLeft + 10,
point.plotY + chart.plotTop - 10
).attr({
zIndex: 5
}).add(),
box = text.getBBox();
chart.renderer.rect(box.x - 5, box.y - 5, box.width + 10, box.height + 10, 5)
.attr({
fill: '#FFFFEF',
stroke: 'gray',
'stroke-width': 1,
zIndex: 4
})
.add();
});
The best way to add a custom element inside the legend is to use the legend.labelFormatter.
events: {
render() {
let chart = this,
legendAttr = chart.legend.box.getBBox(),
padding = 5;
chart.plus = chart.renderer.text('| +', chart.legend.box.parentGroup.alignAttr.translateX + legendAttr.width + padding, chart.spacingBox.height + padding / 2).attr({
cursor: 'pointer',
}).css({
fontSize: 20
}).on(
"click",
function() {
alert('plus was clicked')
}
).add()
}
}
API References:
https://api.highcharts.com/highcharts/legend.labelFormatter,
Demo: https://jsfiddle.net/BlackLabel/yu7qm9jx/1/
I decided to just get rid of the title and add custom css to the top of it.

How would I make a color relative to images in the background?

For example, if you go to Twitter and click on an image, you can see they have a nice color that is close to what you see on the image. I tried looking up ways to achieve this as well as trying to figure it out on my own but no luck. I'm not sure if there's a color: relative property or not.
if you want to use the a colour that exists in your image and set it as a background colour you need to use the canvas element in the following manner:
HTML (this is your image)
<img src="multicolour.jpg" id="mainImage">
JS
window.onload = function() {
// get the body element to set background (this can change dependending of your needs)
let body = document.getElementsByTagName("body")
// get references to the image element that contains the picture you want to match with background
let referenceImage = document.getElementById("mainImage");
// create a canvas element (but don't add it to the page)
let canvas = document.createElement("canvas");
// make the canvas size the same as your image
canvas.width = referenceImage.offsetWidth
canvas.height = referenceImage.offsetHeight
// create the canvas context
let context = canvas.getContext('2d')
// usage your image reference to draw the image in the canvas
context.drawImage(referenceImage,0,0);
// select a random X and Y coordinates inside the drawn image in the canvas
// (you don't have to do this one, but I did to demonstrate the code)
let randomX = Math.floor(Math.random() * (referenceImage.offsetWidth - 1) + 1)
let randomY = Math.floor(Math.random() * (referenceImage.offsetHeight - 1) + 1)
// THIS IS THE MOST IMPORTANT LINE
// getImageData takes 4 arguments: coord x, coord y, sample size w, and sample size h.
// in our case the sample size is going to be of 1 pixel so it retrieves only 1 color
// the method gives you the data object which constains and array with the r, b, g colour data from the selected pixel
let color = context.getImageData(randomX, randomY, 1, 1).data
// use the data to dynamically add a background color extracted from your image
body[0].style.backgroundColor = `rgb(${color[0]},${color[1]},${color[2]})`
}
here is a gif of the code working... hopefully this helps
UPDATE
Here is the code to select two random points and create a css3 background gradient
window.onload = function() {
// get the body element to set background (this can change dependending of your needs)
let body = document.getElementsByTagName("body")
// get references to the image element that contains the picture you want to match with background
let referenceImage = document.getElementById("mainImage");
// create a canvas element (but don't add it to the page)
let canvas = document.createElement("canvas");
// make the canvas size the same as your image
canvas.width = referenceImage.offsetWidth
canvas.height = referenceImage.offsetHeight
// create the canvas context
let context = canvas.getContext('2d')
// usage your image reference to draw the image in the canvas
context.drawImage(referenceImage,0,0);
// select a random X and Y coordinates inside the drawn image in the canvas
// (you don't have to do this one, but I did to demonstrate the code)
let randomX = Math.floor(Math.random() * (referenceImage.offsetWidth - 1) + 1)
let randomY = Math.floor(Math.random() * (referenceImage.offsetHeight - 1) + 1)
// THIS IS THE MOST IMPORTANT LINE
// getImageData takes 4 arguments: coord x, coord y, sample size w, and sample size h.
// in our case the sample size is going to be of 1 pixel so it retrieves only 1 color
// the method gives you the data object which constains and array with the r, b, g colour data from the selected pixel
let colorOne = context.getImageData(randomX, randomY, 1, 1).data
// THE SAME TO OBTAIN ANOTHER pixel data
let randomX2 = Math.floor(Math.random() * (referenceImage.offsetWidth - 1) + 1)
let randomY2 = Math.floor(Math.random() * (referenceImage.offsetHeight - 1) + 1)
let colorTwo = context.getImageData(randomX2, randomY2, 1, 1).data
// use the data to dynamically add a background color extracted from your image
//body[0].style.backgroundColor = `rgb(${allColors[0]},${allColors[1]},${allColors[2]})`
body[0].style.backgroundImage = `linear-gradient(to right, rgb(${colorOne[0]},${colorOne[1]},${colorOne[2]}),rgb(${colorTwo[0]},${colorTwo[1]},${colorTwo[2]}))`;
}
The following are your options.
1. Use an svg.
As far as I know there's no way to have javascript figure out what color is being used in a png and set it as a background color. But you can work the other way around. You can have javascript set the background color and an svg image to be the same color.
See this stackoverflow answer to learn more about modifying svgs with javascript.
2. Use a custom font.
There are fonts out there that provide a bunch of icons instead of letters, you can also create your own font if you feel so inclined to do so. With css you just have to set the font-color of that icon to be the same as the background-color of your other element.
Font Awesome provides a bunch of useful custom icons. If the image you need to use happens to be similar to one of theirs, you can just go with them.
3. Use canvas
If you really want to spend the time to code it up you can use a html <canvas/> element and put the image into it. From there you can inspect certain details about the image like its color, then apply that color to other elements. I won't go into too much detail about using this method as it seems like it's probably overkill for what you're trying to do, but you can read up more about from this stackoverflow answer.
4. Just live with it.
Not a fun solution, but this is usually the option I go with. You simply have to hard-code the color of the image into your css and live with it. If you ever need to modify the color of the image, you have to remember to update your css also.

Use CSS to move charts close to each other

I have two charts. One is bigger than the other. The bigger one consist of two labels called a and b. The small one consist of label c and d. Code for both charts are similar as the following.
var chart = c3.generate({
data: {
columns: [
['Rra', 3880],
['b', 50],
],
type : 'donut'
},
});
var chart = c3.generat
size: {
height: 200,
width: 450
},
data: {
columns: [
['c', 50],
['d', 50],
],
},
});
I was wondering if it is possible to move the small chart between the empty space in the big chart. So they end up looking like two layered charts. I did inspect element on the small chart and tried to use css to tweak the transform="translate(225,83)" to different values. However i noticed when the small chart got close to the bigger chart parts of it disappeared.
I am not expert in css or charting libraries and would love inputs on how to achieve this. Here is my FIDDLE
Although I do agree with YourConscious's answer (using the d3 plugin to create what you want instead of using hacky CSS), here is the CSS method I have come up with:
.chart_container{
position:relative;
}
#chart1{
position:absolute !important;
top:50%;
left:50%;
transform:translate(-50%, -50%);
}
You can see it in action in this Fiddle
The magic is wrapping both charts in a container div, setting it to position:relative; so you can position your smaller chart off of it. Then give your smaller chart the above styles to always position it in the middle of the larger chart.
The !important is there because it seems the plugin is putting an inline style of position:relative
You could probably do it.. with position:absolute; and nesting. But I wouldn't really recommend it and will likely not be that stable (z-index issues, mobile, updating it later on, etc).
I think that library has direct documentation and capabilities to create multiple layers / rings. Why not just do the right way?
Exaggerated example (5 layers / rings)
var dataset = {
apples: [53245, 28479, 19697, 24037, 40245],
oranges: [53245, 28479, 19697, 24037, 40245],
lemons: [53245, 28479, 19697, 24037, 40245],
pears: [53245, 28479, 19697, 24037, 40245],
pineapples: [53245, 28479, 19697, 24037, 40245],
};
var width = 460,
height = 300,
cwidth = 25;
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var gs = svg.selectAll("g").data(d3.values(dataset)).enter().append("g");
var path = gs.selectAll("path")
.data(function(d) { return pie(d); })
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", function(d, i, j) { return arc.innerRadius(10+cwidth*j).outerRadius(cwidth*(j+1))(d); });
J Fiddler.
Also, this might be a good resource:

Adding an image to a gRaphaëlJS pie chart

I am using gRaphaëlJS pie chart to create some color "buttons" for a color picker.
What I want:
When a pie chart ("button") is clicked I want an image to appear in the middle of that pie chart to indicate to the user that this button/color is selected.
Problem is: I'm failing in adding that image.
What have I tried:
gRaphaëlJS puts a "cover" over each pie chart so I tried to add a paletteObj.covers[0][0].setAttribute('class', palette + ' sliderPalette'); to the cover SVG element.
Tried to add paletteObj.series.items[i].node.setAttribute('style', "background:url('images/selected01.png')"); to the actual pie chart ("button") SVG element.
I tried to use jQuery to dynamically add a CSS style to my button as so:
$('.' + palette).css('background', "url('images/selected01.png')");
$('.' + palette).css('background-repeat', "no-repeat");
$('.' + palette).css('background-position', "center");
$('.' + palette).css('background-attachment', "fixed");
$('.' + palette).css('background-color', "transparent");
I tried to simply use good old CSS:
.sliderPalette { background: url('images/selected01.png'); }
I tried what was written here by robertc about <defs> and <pattern>
Why can't I add a BG image like to any other DOM element?
My code can be found here: http://jsfiddle.net/QBVKy/4/
I appreciate any help on this.
D.
P.S - could anyone with enough reputation (1.5K) please add a tag for RaphaëlJS? I see that snap.svg has one :-)

Set canvas element as background of a div element

We can use canvas for drawing custom shapes.
I need to draw my shape dynamically as a canvas item and place it for a div background item.
My pages generates run time and they aren't static html code so i can't use tricky methods.
What's your idea?
Regards
Looks like you searching for toDataURL().
UPD:
Here a usage exaple:
dataUrl = your_canvas.toDataURL();
your_div.style.background='url('+dataUrl+')'
Live demo on jsFiddle
Sounds like you need canvas2image: https://github.com/hongru/canvas2image
You can create a canvas and then get the contents as a png:
var canvas = document.createElement("canvas");
....do stuff here...
var img = Canvas2Image.convertToPNG(canvas, canvas.width, canvas.height);
Then all you need to do is use the png as a background image:
document.body.style.background = "url(" + img.src + ")";
Please correct me if any of this is wrong.

Resources