heavysixer / d4 Goto Github PK
View Code? Open in Web Editor NEWA friendly reusable charts DSL for D3
License: MIT License
A friendly reusable charts DSL for D3
License: MIT License
The transition API needs to be integrated into the various features.
Hello again! Implementation-wise this is not too different from #14, but I'm starting to realize that the implications reach far beyond browserify. One amazing and under-used feature of d3 is its ability to run on node, which means you can pre-render charts on the server. Would be excellent if d4 could do the same.
I don't think there's anything major blocking it except code structure and the dependency on a browser global, as discussed in the other issue, as d3 is fully node-compatible. Thoughts @heavysixer ?
Every scale created by D4 has nice()
called on it
Line 63 in 8136c16
Since there is no way to un-nice a d3 scale (that I know of) there should be a way to turn this off (or prevent it from happening entirely)
Hi Mark -
Would it be possible for you to add an appropriate license to D4 here on Github?
Thanks!
Distribution files are stored in the root directory, but the bower.json file references dist/d4.js
Looks like d4 variable isn't available inside subsequent closures after the first when compiling with Browserify. As a quick-fix, I declared d4 variable outside all closures. Thoughts?
var d4;
(function() {
'use strict';
var root = this;
var breaker = {};
// Create a safe reference to the d4 object.
d4 = function(obj) {
if (obj instanceof d4) {
return obj;
}
if (!(this instanceof d4)) {
return new d4(obj);
}
this.d4Wrapped = obj;
};
...
We've completely moved our client-side dependencies from Bower to npm and use Browserify for dependency management. Browserify allows you to include npm modules on the client. It's great.
There are ways to include Bower modules within Browserify. Debowerify works sometimes but can cause conflicts depending on what other transforms you have.
For reference for others coming here, this is a way to get d4 workings within Browserify:
package.json:
"browser": {
"bootstrap": "./src/javascript/vendor/bootstrap.js",
"d4": "./src/javascript/vendor/d4.js",
},
"browserify": {
"transform": [
"browserify-shim",
"coffeeify",
]
},
"browserify-shim": {
"bootstrap": {
"exports": "bootstrap",
"depends": [
"jquery:$"
]
},
"d4": {
"exports": "d4",
"depends": [
"d3:d3"
]
},
Then you can require it in your modules:
d4 = require('d4')
But it's really nice to just be able to do this and be done:
npm install d4 --save
setting the x.min(min) or x.max(max) has no effect on waterfall charts.
see this example: http://jsfiddle.net/os96mvc0/
Is there a way to assign the stacking order of elements?
So far i've had to mod the source code or call a send to back routine in an afterRender
function
Is there a way to change the figure displayed on the bars of a column chart? E.g. if my data object looks like:
var dataObj = [
{ x : '2010', y : 5 },
{ x : '2011', y : 15 },
{ x : '2012', y : 20 },
{ x : '2013', y : 0 },
{ x : '2014', y : 0 },
];
Then instead of showing "0" for x = 2013/2014 I want to show "-" on top. Possible?
It looks like the y
attributes are correct, but their height
attribute is not because it is set by the chart y()
instead of the custom scale set to it.
Possibly due to this line? https://github.com/heavysixer/d4/blob/master/src/features/grouped-column-series.js#L42
I am creating something like chart's constructor using d4. And now i need a lot of live updates features. Am i need to recreate all chart with d3, each time when i change something, or play with native css\svg data? May be it's possible to make some setter's like in https://github.com/curran/model ? It could be very useful for dynamic charts.
What do you think?
Thanks.
We use margin to define the space between the edge of the chart and the next closest item: the credit line the legend, the axis etc.
We use padding to define the space between the margin and the chart area.
We also have a method of extraPadding
so that we can add or subtract from the default padding without modifying it.
Hey,
Tried using this for getting tooltips on the column bars but didnt work. How should I proceed?
d4.charts.column()
,mixout('yAxis')
.using('bars', function(bar) {
// Note: d4 proxies the "on" function to d3, so it will work just like
// it does in d3
bar.on('mouseover', function(d) {
var title = 'X: ' + d.x + '
' + 'Y: ' + d.y;
$(this).tooltip({
container: 'body',
placement: 'auto',
html: true,
title: title
});
});
I already have the bootstrap lib included.
If I missing [week,hour,0] data,only provide non-zero data, such as
[[2,3,1],[3,3,3],[3,4,1],[4,1,1],[4,3,1],[5,6,2],[6,1,2],[7,1,2],[7,6,1],[8,1,1],[8,3,1],[9,1,6],[9,3,1],[9,5,1],[10,1,1],[11,5,4],[14,3,1],[17,5,1],[19,1,1],[19,4,1],[19,6,1],[20,2,1],[20,6,1],[21,4,1]]
the punchcard graph as following
When you use beforeRender
to filter data and plot different features in the same chart, the classes applied to the series containers are linked to the index in relation to the filtered data not in the original data.
For instance both the bars and the line are classed series0
in this http://visible.io/charts/column/multi-dimension.html
We've been binding an index to the data and accessing it in the afterRender
which is less than ideal
line.afterRender(function() {
this.container.selectAll(".lines g.line")
.each(function(d,i) {
d3.select(this).classed("mixed-series-" + d.index, true);
});
})
@nsonnad just noticed that the refactoring you did to the arc series seems to have broken the transitions between pie slices. If you notice the donut chart example now snaps to the next location where it used to animate nicely between states. This is a minor defect but I worry that it is a symptom of a larger problem. I've not looked at the issue yet it might be minor, I just thought i'd run it by you first.
http://visible.io/charts/donut/basic.html
Please note that the text labels still nicely transition between states.
It looks like you've got transitions built in based on this example:
http://visible.io/charts/donut/basic.html
I tried to recreate this with the stacked column charts with no luck. Not only is the transition instant, the mixins don't follow.
What approach would I take to make this transition nicely as in the donut chart example?
'use strict';
$(document).ready(function(){
var generateData = function() {
var data = [
{ year: '2010', unitsSold: 0, salesman : 'Bob' },
{ year: '2011', unitsSold: 0, salesman : 'Bob' },
{ year: '2012', unitsSold: 0, salesman : 'Bob' },
{ year: '2013', unitsSold: 0, salesman : 'Bob' },
{ year: '2014', unitsSold: 0, salesman : 'Bob' },
{ year: '2010', unitsSold: 0, salesman : 'Gina' },
{ year: '2011', unitsSold: 0, salesman : 'Gina' },
{ year: '2012', unitsSold: 0, salesman : 'Gina' },
{ year: '2013', unitsSold: 0, salesman : 'Gina' },
{ year: '2014', unitsSold: 0, salesman : 'Gina' },
{ year: '2010', unitsSold: 0, salesman : 'Average' },
{ year: '2011', unitsSold: 0, salesman : 'Average' },
{ year: '2012', unitsSold: 0, salesman : 'Average' },
{ year: '2013', unitsSold: 0, salesman : 'Average' },
{ year: '2014', unitsSold: 0, salesman : 'Average' }
]
_.each(data, function(obj){
obj.unitsSold = _.random(-500, 500)
})
return data;
}
var chart = d4.charts.stackedColumn()
.outerWidth($('#example').width())
.x(function(x){ return x.key('year') })
.y(function(y){ return y.key('unitsSold') })
.mixin({
'name' : 'zeroLine',
'feature' : d4.features.referenceLine
})
.using('zeroLine', function(zero) {
zero
.x1(0)
.x2(function(){ return this.width })
.y1(function(){ return this.y(0) })
.y2(function(){ return this.y(0) });
});
var redraw = function() {
var data = generateData()
var parsedData = d4.parsers.nestedStack()
.x('year')
.y('salesman')
.value('unitsSold')(data);
d3.select('#example')
.datum(parsedData.data)
.call(chart);
};
(function loop() {
redraw();
setTimeout(loop, 3000);
})();
});
(this screenshot taken after several cycles)
(btw, I love the approach d4 is taking. Really excited to have found this)
There seems to be a disconnect between when the order of execution of the rendering cycle.
For example consider this example where bars is rendered before the labels:
var total = 0;
chart.using('bars', function(bars){
bars.x(function(){
total+=1;
});
})
.using('labels', function(labels){
labels.x(function(){
// => 0 instead of 1;
console.log(total);
});
});
in this example outerWidth will return the wrong value because it applies margins which are then removed in the next step. Since outerWidth
and outerHeight
are dependent on the margin value we need to recalculate them once margin is updated.
var chart = d4.charts.donut()
.outerWidth($('#pie').width())
.margin({
left: 0,
top: 0,
right: 0,
bottom: 0
})
Trying to use d4 with reactjs::
Getting: d4.js:1172 Uncaught ReferenceError: d4 is not defined
import React from 'react';
import ReactDOM from 'react-dom';
import ReactFauxDOM from 'react-faux-dom';
import d3 from 'd3';
import d4 from 'd4';
class App extends React.Component {
render () {
const data =
[
{x: "For Investment", y: 13.27},
{x: "For Distribution", y: 8.66},
{x: "Outstanding Principal", y: 5.22},
{x: "Accrued Interest", y: 5}
];
const myChart = d4.charts.column().width(500)
const d = d3.select(ReactFauxDOM.createElement('div'))
.datum(data).call(myChart);
return d.node().toReact()
}
}
export default App;
In case of this bug https://bugzilla.mozilla.org/show_bug.cgi?id=612118 i recommend to replace
js var axisBB = axis.node().getBBox();
to
try {
axisBB = node.node.getBBox();
} catch (err) {
axisBB = {
x: node.clientLeft,
y: node.clientTop,
width: node.clientWidth,
height: node.clientHeight
};
}
what do you think about it?
I've been working on trying to figure out the best way to allow for a left axis and a right axis on different scales. (and thus series on different scales) Like this:
At the moment d4 provides no way to override the chart-wide scale on a feature by feature basis.
So far I've tried the following,
alt_scale
to the yAxis feature's accessors with a default of null
yAxis.render
from scope.scale(this.y)
to scope.scale(scope.alt_scale() ? scope.alt_scale() : this.y);
but there is no way to access a custom scale (that I can see) from inside of a feature (such as lineSeries
) since the accessor is called in the context of the chart not the context of the feature. (e.g. d4.functor(scope.accessors.y).bind(this)
)
If you want to keep this functionality, perhaps it's better adding alt_y
on the chart
object instead—a clone of chart.y
. Then there can be alt_y
accessors on each feature and logic on when to use it in the render based on a use_alt_y
accessor boolean on the feature. A similar thing could also be added to the x
methods.
Of course once there are two axes...why not three, four, etc? That type of functionality would require the chart.x
and chart.y
methods to return arrays of scales and features to have something like y_scale_index
(defaulting to 0). It could also really mess up the API.
What are your thoughts on how to achieve this @heavysixer?
Consider the proposed approach in this SO topic.
http://stackoverflow.com/questions/16265123/resize-svg-when-window-is-resized-in-d3-js
Is it normal, that tick is not centred in the middle of the columns?
You can have a look here: http://jsbin.com/ciyihi/2/edit
Where can it be changed?
add line for punch-card like github will be great.
such as FYI
Hello. I'm using d4 and my graph missing one bar:
Still it's present in data model:
Code:
for(var i = window.data.length - 1; i >= 0; i --){
graphData.push({
x: i + 1,
y: window.data[i].count
});
}
var columnChart = d4.charts.column().outerWidth(900);
d3.select('#graph').datum(graphData).call(columnChart);
Any help is appreciated ;)
Ordinal rangePoints are ignored when being applied in this manner:
.x(function(x){
x
.domain(data)
.rangePoints([0, chart.width()],1);
// this produces the correct range but is reverted when the chart is rendered.
console.log(x.rangePoints([0, chart.width()],1).range())
})
More than likely this is also a problem with other scale setters as well.
You can see an example of the reverted scale range using this simple feature:
d4.feature('pathSeries', function(name) {
return {
accessors: {
classes: function(){
return 'path';
}
},
render: function(scope, data, selection) {
selection.append('g').attr('class', name);
var group = selection.select('.' + name).selectAll('g').data(data);
group.exit().remove();
group.enter().append('g');
var path = group.selectAll('path')
.data(function(d){
return d.values;
});
path.enter().append('path')
.attr('class', d4.functor(scope.accessors.classes).bind(this))
.attr('transform', function(d) {
// produces the wrong value
console.log(this.x(d))
this.x.rangePoints([0, this.width],1);
// produces the correct value
console.log(this.x(d))
return 'translate(' + this.x(d) + ',' + (this.height / 2) + ')';
}.bind(this))
.attr('d', d3.svg.symbol().type(String).size(this.height));
}
};
}).call(this);
This is probably due to the fact that the label series is meant to be used by lots of different chart types. What looks good for a stacked column in regards to handling negative values does not look good for the line series.
http://visible.io/charts/line/line-and-dot.html
Is it by Design? I can pass empty data to any type of chart, but when i try it with stacked i got d3 error.
Bin: http://jsbin.com/topeqiva/15/
The fix to #32 disabled auto-nicing. The (undocumented) way of nicing scales I found is:
chart.using('lineSeries', function(line) {
line.beforeRender(function(chart) {
this.y.nice();
});
});
I'd appreciate if this could be done declaratively, like setting the Key for the Axis.
Should I take the time to implement this and send a pull request, or should I just add the workaround to the documentation?
When I try to run the sample script on my machine. I get this error:
d4.js:95 Uncaught Error: [d4] The scale type: "ordinal" is unrecognized. D4 only supports these scale types: time, time.utc
When I log supportedScales in line 175 (function validateScale); d3.scale is undefined.
I load d3.js first, then d4.js.
Any idea why this happens?
if the order in punch-card data is not order by week,hour
,the graph is wrong.
example data:
[[0,0,0],[0,1,0],[0,2,0],[0,3,0],[0,4,0],[0,5,0],[0,6,0],[1,0,0],[1,1,0],[1,2,0],[1,3,0],[1,4,0],[1,5,0],[1,6,0],[2,0,0],[2,1,0],[2,2,0],[2,3,1],[2,4,0],[2,5,0],[2,6,0],[3,0,0],[3,1,0],[3,2,0],[3,3,3],[3,4,1],[3,5,0],[3,6,0],[4,0,0],[4,1,1],[4,2,0],[4,3,1],[4,4,0],[4,5,0],[4,6,0],[5,0,0],[5,1,0],[5,2,0],[5,3,0],[5,4,0],[5,5,0],[5,6,2],[6,0,0],[6,1,2],[6,2,0],[6,3,0],[6,4,0],[6,5,0],[6,6,0],[7,0,0],[7,1,2],[7,2,0],[7,3,0],[7,4,0],[7,5,0],[7,6,1],[8,0,0],[8,1,1],[8,2,0],[8,3,1],[8,4,0],[8,5,0],[8,6,0],[9,0,0],[9,1,6],[9,2,0],[9,3,1],[9,4,0],[9,5,1],[9,6,0],[10,0,0],[10,1,1],[10,2,0],[10,3,0],[10,4,0],[10,5,0],[10,6,0],[11,0,0],[11,1,0],[11,2,0],[11,3,0],[11,4,0],[11,5,4],[11,6,0],[12,0,0],[12,1,0],[12,2,0],[12,3,0],[12,4,0],[12,5,0],[12,6,0],[13,0,0],[13,1,0],[13,2,0],[13,3,0],[13,4,0],[13,5,0],[13,6,0],[14,0,0],[14,1,0],[14,2,0],[14,3,1],[14,4,0],[14,5,0],[14,6,0],[15,0,0],[15,1,0],[15,2,0],[15,3,0],[15,4,0],[15,5,0],[15,6,0],[16,0,0],[16,1,0],[16,2,0],[16,3,0],[16,4,0],[16,5,0],[16,6,0],[17,0,0],[17,1,0],[17,2,0],[17,3,0],[17,4,0],[17,5,1],[17,6,0],[18,0,0],[18,1,0],[18,2,0],[18,3,0],[18,4,0],[18,5,0],[18,6,0],[19,0,0],[19,1,1],[19,2,0],[19,3,0],[19,4,1],[19,5,0],[19,6,1],[20,0,0],[20,1,0],[20,2,1],[20,3,0],[20,4,0],[20,5,0],[20,6,1],[21,0,0],[21,1,0],[21,2,0],[21,3,0],[21,4,1],[21,5,0],[21,6,0],[22,0,0],[22,1,0],[22,2,0],[22,3,0],[22,4,0],[22,5,0],[22,6,0],[23,0,0],[23,1,0],[23,2,0],[23,3,0],[23,4,0],[23,5,0],[23,6,0]]
Here D4 created svg element with chart
id.
https://github.com/heavysixer/d4/blob/master/src/base.js#L354
If you want to have several charts - you will get id conflicts.
May be it would be better to remove id form there?
Something like this is the proposed API:
// create a chart
var columnChart = d4.baseChart()
// mix in a feature to the chart
.mixin([{name: 'bars', feature: d4.features.stacked-shapes-series}])
// when using the feature apply a filter to the rects.
.using('bars',function(bar){
bar.svgFilter('feGaussianBlur', function(filter){
filter
.in("SourceGraphic")
.stdDeviation("5");
});
});
Thoughts?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.