From 927d2c12d1771e0e190a898b52762aee9d4f3a9c Mon Sep 17 00:00:00 2001 From: Matt Huntington Date: Thu, 7 Sep 2017 23:08:36 -0400 Subject: [PATCH] svg/advanced --- README.md | 6 + SUMMARY.md | 4 + SVG.md | 173 +++++++++++++++++++++++++++++ advanced.md | 308 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 491 insertions(+) create mode 100644 README.md create mode 100644 SUMMARY.md create mode 100644 SVG.md create mode 100644 advanced.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..3185a4a --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# D3 Notes + +## Lessons + +1. [SVG](/SVG.md) +1. [Advanced D3](/advanced.md) diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000..9ba5e30 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,4 @@ +# Summary + +* [SVG](/SVG.md) +* [Advanced](/advanced.md) diff --git a/SVG.md b/SVG.md new file mode 100644 index 0000000..7860ded --- /dev/null +++ b/SVG.md @@ -0,0 +1,173 @@ +# SVG + +## Base tag + +Everything goes inside here + +```xml + +``` + +## 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 + +``` + +## Line + +- attributes + - `x1` starting x position + - `y1` starting y position + - `x2` ending x position + - `y2` ending y position + +```xml + + +``` + +## Rectangle + +- attributes + - `x` x position of top left + - `y` y position of top left + - `width` width + - `height` height + +```xml + +``` + +## Ellipse + +- attributes + - `cx` x position + - `cy` y position + - `rx` x radius + - `ry` y radius + +```xml + +``` + +## Polygon + +- attributes + - `points` set of coordinate pairs + - each pair is of the form x,y + +```xml + +``` + +## Polyline + +A series of connected lines. Can have a fill like a polygon, but won't automatically rejoin itself + +```xml + +``` + +## 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 + + + +`` + +## Text + +Content of tag is the text + +- attributes + - `x` x position of top left + - `y` y position of top left + +```xml +I love SVG! +``` + +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 + +``` + +## Documentation + +https://developer.mozilla.org/en-US/docs/Web/SVG/Element diff --git a/advanced.md b/advanced.md new file mode 100644 index 0000000..9b68978 --- /dev/null +++ b/advanced.md @@ -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('hi'); //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/