commit
927d2c12d1
@ -0,0 +1,6 @@
|
|||||||
|
# D3 Notes
|
||||||
|
|
||||||
|
## Lessons
|
||||||
|
|
||||||
|
1. [SVG](/SVG.md)
|
||||||
|
1. [Advanced D3](/advanced.md)
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
# Summary
|
||||||
|
|
||||||
|
* [SVG](/SVG.md)
|
||||||
|
* [Advanced](/advanced.md)
|
||||||
@ -0,0 +1,173 @@
|
|||||||
|
# SVG
|
||||||
|
|
||||||
|
## Base tag
|
||||||
|
|
||||||
|
Everything goes inside here
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<svg></svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Positioning
|
||||||
|
|
||||||
|
SVG tag is an inline element
|
||||||
|
|
||||||
|
- (0,0) - x = 0, y = 0
|
||||||
|
- starts at top left
|
||||||
|
- y increases vertically down
|
||||||
|
- can use negative values
|
||||||
|
- -x goes left
|
||||||
|
- -y goes up
|
||||||
|
|
||||||
|
## Styling
|
||||||
|
|
||||||
|
Can be done with attributes:
|
||||||
|
|
||||||
|
- `fill=red` or `fill=#ff0000` fill color
|
||||||
|
- `stroke=red` or `stroke=#ff0000` stroke color
|
||||||
|
- `stroke-width=4` width of stroke
|
||||||
|
- `fill-opacity=0.5` transparency of fill
|
||||||
|
- `stroke-opacity=0.5` transparency of stroke
|
||||||
|
- `transform = "translate(2,3)"`
|
||||||
|
- `transform = "scale(2.1)"`
|
||||||
|
- `transform = "rotate(45)"`
|
||||||
|
|
||||||
|
|
||||||
|
Can also be done with CSS:
|
||||||
|
|
||||||
|
```css
|
||||||
|
circle {
|
||||||
|
fill:red;
|
||||||
|
stroke:blue;
|
||||||
|
stroke-width:3;
|
||||||
|
fill-opacity:0.5;
|
||||||
|
stroke-opacity:0.1;
|
||||||
|
transform:rotate(45deg) scale(0.4) translate(155px, 1px);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Circle
|
||||||
|
|
||||||
|
- attributes
|
||||||
|
- `r` radius
|
||||||
|
- `cx` x position
|
||||||
|
- `cy` y position
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<circle r="50" cx="200" cy="300"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Line
|
||||||
|
|
||||||
|
- attributes
|
||||||
|
- `x1` starting x position
|
||||||
|
- `y1` starting y position
|
||||||
|
- `x2` ending x position
|
||||||
|
- `y2` ending y position
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<line x1="0" y1="0" x2="100" y2="100"/> <!-- no stroke, so invisible -->
|
||||||
|
<line x1="0" y1="0" x2="100" y2="100" stroke="purple"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rectangle
|
||||||
|
|
||||||
|
- attributes
|
||||||
|
- `x` x position of top left
|
||||||
|
- `y` y position of top left
|
||||||
|
- `width` width
|
||||||
|
- `height` height
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<rect x="50" y="20" width="150" height="150"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ellipse
|
||||||
|
|
||||||
|
- attributes
|
||||||
|
- `cx` x position
|
||||||
|
- `cy` y position
|
||||||
|
- `rx` x radius
|
||||||
|
- `ry` y radius
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<ellipse cx="200" cy="80" rx="100" ry="50"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Polygon
|
||||||
|
|
||||||
|
- attributes
|
||||||
|
- `points` set of coordinate pairs
|
||||||
|
- each pair is of the form x,y
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<polygon points="200,10 250,190 160,210" />
|
||||||
|
```
|
||||||
|
|
||||||
|
## Polyline
|
||||||
|
|
||||||
|
A series of connected lines. Can have a fill like a polygon, but won't automatically rejoin itself
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<polyline points="20,20 40,25 60,40 80,120 120,140 200,180" stroke="blue" fill="none"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Path
|
||||||
|
|
||||||
|
https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
|
||||||
|
|
||||||
|
- attributes
|
||||||
|
- `d` a set of drawing commands
|
||||||
|
- M = moveto
|
||||||
|
- L = lineto
|
||||||
|
- C = curveto
|
||||||
|
- C x1 y1, x2 y2, x y
|
||||||
|
- first pair is first control point
|
||||||
|
- second pair is second control point
|
||||||
|
- last pair is final ending point of curve
|
||||||
|
- S = smooth curveto
|
||||||
|
- S x2 y2, x y
|
||||||
|
- follows another S or C command
|
||||||
|
- uses x2 y2 of previous S or C command
|
||||||
|
- Q = quadratic Bézier curve
|
||||||
|
- Q x1 y1, x y
|
||||||
|
- uses one control point for start and end controls (x1, y1)
|
||||||
|
- T = smooth quadratic Bézier curveto
|
||||||
|
- T x y
|
||||||
|
- strings together multiple quadratic lines
|
||||||
|
- Z = closepath
|
||||||
|
- **Note:** All of the commands above can also be expressed with lower letters. Capital letters means absolutely positioned, lower cases means relatively positioned.
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<path d="M150 0 L75 200 L225 200 Z" />
|
||||||
|
<path d="M0 70 C 0 120, 50 120, 50 70 S 100 20, 100 70" stroke="black" fill="transparent"/>
|
||||||
|
<path d="M0 100 Q 50 50, 100 100 T 200 100 Z" stroke="black" fill="transparent"/>
|
||||||
|
``
|
||||||
|
|
||||||
|
## Text
|
||||||
|
|
||||||
|
Content of tag is the text
|
||||||
|
|
||||||
|
- attributes
|
||||||
|
- `x` x position of top left
|
||||||
|
- `y` y position of top left
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<text x="0" y="15">I love SVG!</text>
|
||||||
|
```
|
||||||
|
|
||||||
|
Can use font-family and font-size CSS styling
|
||||||
|
|
||||||
|
## Group
|
||||||
|
|
||||||
|
- no special attributes, so use transform
|
||||||
|
- can put multiple elements inside it.
|
||||||
|
- positioning and styling apply to children
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<g></g>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
https://developer.mozilla.org/en-US/docs/Web/SVG/Element
|
||||||
@ -0,0 +1,308 @@
|
|||||||
|
# D3.js
|
||||||
|
|
||||||
|
## Basics
|
||||||
|
|
||||||
|
### Selection
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
d3.select('#some-id') //like document.querySelector()
|
||||||
|
d3.selectAll('.some-class') //like document.querySelectorAll()
|
||||||
|
d3.select('main').selectAll('span'); //can chain to select ancestors
|
||||||
|
```
|
||||||
|
|
||||||
|
### .style()
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
d3.select('div').style('color', 'orange'); //sets the style for an element
|
||||||
|
d3.select('div').style('color', 'orange').style('font-size': '20px'); //will return the selection for chaining
|
||||||
|
```
|
||||||
|
|
||||||
|
### .attr()
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
d3.select('div').attr('anExampleAttribute', 'someValue'); //adds/changes an attribute on an selection
|
||||||
|
```
|
||||||
|
|
||||||
|
### .classed()
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
d3.selectAll('.house').classed('house'); // returns true if all elements in selection contain the chosen class
|
||||||
|
d3.selectAll('div').classed('frog', true); //adds the class and returns the selection
|
||||||
|
d3.selectAll('div').classed('frog', false); //removes the class and returns the selection
|
||||||
|
```
|
||||||
|
|
||||||
|
### .append()
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
d3.selectAll('div').append('span'); //append html to a selection and return appended element
|
||||||
|
```
|
||||||
|
|
||||||
|
### .html()
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
d3.selectAll('div').html('<span>hi</span>'); //change the inner html of an element
|
||||||
|
```
|
||||||
|
|
||||||
|
### .text()
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
d3.selectAll('div').text('hi'); //set the content of the selection to the exact text (escapes html)
|
||||||
|
```
|
||||||
|
|
||||||
|
## AJAX
|
||||||
|
|
||||||
|
Named based off of what kind of data they accept
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
d3.json('path', function(error, data){});
|
||||||
|
d3.csv('path', function(error, data){});
|
||||||
|
d3.tsv('path', function(error, data){});
|
||||||
|
d3.xml('path', function(error, data){});
|
||||||
|
d3.html('path', function(error, data){});
|
||||||
|
d3.text('path', function(error, data){});
|
||||||
|
|
||||||
|
//make a post
|
||||||
|
d3.request('/runs') //make a request to the server
|
||||||
|
.header("Content-Type", "application/json") //tell the server we're sending JSON data
|
||||||
|
.post(
|
||||||
|
//must turn data object into string
|
||||||
|
JSON.stringify(runObject),
|
||||||
|
function(){ //callback
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
//send delete
|
||||||
|
d3.request('/runs/'+d.id)
|
||||||
|
.header("Content-Type", "application/json") //we're sending data
|
||||||
|
.send('DELETE', function(){}); //send a DELETE request
|
||||||
|
|
||||||
|
//send update
|
||||||
|
d3.request('/runs/'+d.id)
|
||||||
|
.header("Content-Type","application/json") //we're sending JSON
|
||||||
|
.send('PUT', JSON.stringify(d), function(){});//pass alterted 'd' object to API
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data binding
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
d3.select('svg').selectAll('circle')//make a "ghost call" to all circles, even if there are none already. Make sure to select the svg, or appended circles will attach to html element
|
||||||
|
.data(dataArray) //joins each element in dataArray to an element in the selection
|
||||||
|
.enter() //returns the sub section of dataArray that has not been matched with DOM elements
|
||||||
|
.append('circle'); //creates a DOM element for each of the remaining dataArray elements
|
||||||
|
```
|
||||||
|
|
||||||
|
once data has been bound to elements, you can call something like:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
d3.selectAll('circle').attr('r', function(d,i){ //d is data for the current element, i is the index of that element in the array
|
||||||
|
//callback will be executed for each DOM element
|
||||||
|
//return value is how each value will be set
|
||||||
|
return d.value * 2 //takes value property of d (data), multiplies it by two and sets the radius to that
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Can remove elements:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
d3.selectAll('circle')//make a "ghost call" to all circles, even if there are none already
|
||||||
|
.data(dataArray) //joins each element in dataArray to an element in the selection
|
||||||
|
.exit() //returns the sub section of DOM elements that has not been matched with dataArray elements
|
||||||
|
.remove(); //removes those elements
|
||||||
|
```
|
||||||
|
|
||||||
|
To bind data to elements by something other than index:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
.data(data, function(d){
|
||||||
|
//match data based on d.id, not index
|
||||||
|
return d.id
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Linear Scale
|
||||||
|
|
||||||
|
A scale will map a data value to a visual value.
|
||||||
|
|
||||||
|
1. Create a scale. There are many types. Here we'll use a linear scale
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var yScale = d3.scaleLinear();
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Set up a visual range
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
yScale.range([height,0]);
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Add the domain
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
yScale.domain(yDomain);
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Can check range and domain after initialization
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
yScale.range();
|
||||||
|
yScale.domain();
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Can now pass a data value into the scale to get a visual value
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
yScale(361); //returns the visual value that maps to this data value
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Can go the opposite way
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
yScale.invert(800); //returns the data value that maps to this visual value
|
||||||
|
```
|
||||||
|
|
||||||
|
1. If data min/max of a data set (called the "domain") are not found, you can find them:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var yMax = d3.max(data, function(element){
|
||||||
|
return parseInt(element.TMAX);
|
||||||
|
})
|
||||||
|
var yMin = d3.min(data, function(element){
|
||||||
|
return parseInt(element.TMAX);
|
||||||
|
})
|
||||||
|
|
||||||
|
var yDomain = [yMin, yMax];
|
||||||
|
```
|
||||||
|
|
||||||
|
- Can combine this into one call if max/min come from same element:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var yDomain = d3.extent(data, function(element){
|
||||||
|
return parseInt(element.TMAX);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Time Scale
|
||||||
|
|
||||||
|
1. Create the scale
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var xScale = d3.scaleTime();
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Set up the visual range
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
xScale.range([0, width]);
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Set up the time range
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
xScale.domain([new Date('2016-1-1'), new Date('2017-1-1')]);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dealing with alternate date formats
|
||||||
|
|
||||||
|
Date formatting options: https://github.com/d3/d3-time-format#locale_format
|
||||||
|
|
||||||
|
To parse an alternate format into a date object
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var parseTime = d3.timeParse("%Y%m%d");
|
||||||
|
parseTime('20160101') //returns a date object
|
||||||
|
```
|
||||||
|
|
||||||
|
To create an alternately formated string from a date object
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var formatTime = d3.timeFormat("%Y%m%d");
|
||||||
|
formatTime(new Date()); //returns a string in the above format
|
||||||
|
```
|
||||||
|
|
||||||
|
## Axes
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var leftAxis = d3.axisLeft(yScale); //create a left axis based on the yScale
|
||||||
|
d3.select('svg')
|
||||||
|
.append('g') //append a group element
|
||||||
|
.call(leftAxis); //apply the axis to it
|
||||||
|
```
|
||||||
|
|
||||||
|
Different types of axes: https://github.com/d3/d3-axis#axisTop
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
select.on('mouseenter', function(data, index){
|
||||||
|
d3.select(this); //select just element that was hovered
|
||||||
|
console.log(d3.event); //the event object
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
click, mouseenter and mouseleave are common
|
||||||
|
|
||||||
|
use `d3.event.stopPropagation();` when events conflict
|
||||||
|
|
||||||
|
## Behaviors
|
||||||
|
|
||||||
|
### Dragging
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
//create the behavior
|
||||||
|
var drag = d3.drag()
|
||||||
|
.on('start', dragStart)
|
||||||
|
.on('drag', drag)
|
||||||
|
.on('end', dragEnd);
|
||||||
|
//...
|
||||||
|
//apply it to a selection
|
||||||
|
d3.selectAll('circle').call(drag);
|
||||||
|
//....
|
||||||
|
//define callbacks
|
||||||
|
function dragStart(d){ //d is the data for the dragged object
|
||||||
|
d3.select(this); //the visual object
|
||||||
|
d3.event.x; //x position of cursor
|
||||||
|
d3.event.y; //y position of cursor
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can use the xScale.invert and yScale.invert to get data from d3.event.x and d3.event.y
|
||||||
|
|
||||||
|
### Zooming
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
//previously defined: var xAxis = d3.axisBottom(xScale);
|
||||||
|
//previously defined: var yAxis = d3.axisLeft(yScale);
|
||||||
|
//previously defined: d3.select('svg').append('g').attr('id', 'x-axis').attr('transform', 'translate(0,' + HEIGHT + ')').call(xAxis);
|
||||||
|
//previously defined: d3.select('svg').append('g').attr('id', 'y-axis').call(yAxis); //y axis is good as it is
|
||||||
|
var zoomCallback = function(){
|
||||||
|
lastTransform = d3.event.transform; //save the transform for later inversion with clicks
|
||||||
|
d3.select('#points').attr("transform", d3.event.transform); //apply transform to g element containing circles
|
||||||
|
//recalculate the axes
|
||||||
|
d3.select('#x-axis').call(xAxis.scale(d3.event.transform.rescaleX(xScale)));
|
||||||
|
d3.select('#y-axis').call(yAxis.scale(d3.event.transform.rescaleY(yScale)));
|
||||||
|
}
|
||||||
|
var zoom = d3.zoom().on('zoom', zoomCallback);
|
||||||
|
d3.select('svg').call(zoom);
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to recalculate new mouse position after transform, use the last saved event transform's invert methods
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var lastTransform = null;
|
||||||
|
d3.select('svg').on('click', function(d){
|
||||||
|
|
||||||
|
//d3.event contains data for click event
|
||||||
|
var x = d3.event.offsetX; //use offset to get point within svg container
|
||||||
|
var y = d3.event.offsetY;
|
||||||
|
|
||||||
|
if(lastTransform !== null){
|
||||||
|
x = lastTransform.invertX(d3.event.offsetX); //use offset to get point within svg container
|
||||||
|
y = lastTransform.invertY(d3.event.offsetY);
|
||||||
|
}
|
||||||
|
//...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Basic Layouts
|
||||||
|
- https://github.com/d3/d3/wiki/Plugins
|
||||||
|
- http://c3js.org/
|
||||||
Loading…
Reference in new issue