Thursday, 6 February 2014

Attributes in d3.js

The following post is a portion of the D3 Tips and Tricks book which is free to download. To use this post in context, consider it with the others in the blog or just download the the book as a pdf / epub or mobi .
----------------------------------------------------------

d3.js Attributes

At the start of writing this section I was faced with the question “What’s an attribute?”. But a reasonable answer has eluded me, so I will make the assumption that the answer will be something of a compromise :-). I like to think that an attribute of an element is something that is a characteristic of the object without defining it, and/or it may affect the object’s position or orientation on the page. There could be a strong argument to say that the following section on styles could be seen to cross-over into attributes and I agree. However, for the purposes of providing a description of the syntax and effects, I’m happy with the following list :-).
Because not all attributes are applicable to all elements, there will be a bit of variation in the type of shapes we deal with in the description below, but there won’t be any that are different to those that we’ve already looked at. There will be some repetition with recurring information from the elements section. This is intentional to hopefully allow each section to exist in its own right.

xy

The x and y attributes are used to designate a position on the web page that is set from the top, left hand corner of the web page. Using the x and y attributes places the anchor points for these elements at a specified location. Of the elements that we have examined thus far, the rectangle element and the text element have anchor points to allow them to be positioned.
For example the following is a code section required to draw a rectangle (using only the required attributes) in conjunction with the HTML file outlined at the start of this chapter;
holder.append("rect")       // attach a rectangle
    .attr("x", 100)         // position the left of the rectangle
    .attr("y", 50)          // position the top of the rectangle
    .attr("height", 100)    // set the height
    .attr("width", 200);    // set the width
This will produce a rectangle as follows; 
Rectangle with x,y at 100,50
The top left corner of the rectangle is specified using x and y at 100 and 50 respectively.

x1x2y1y2

The x1x2y1 and y2 attributes are used to designate the position of two points on a web page that are set from the top, left hand corner of the web page. These two points are connected with a line as part of the lineelement.
The attributes are described as follows;
  • x1: The x position of the first end of the line as measured from the left of the screen.
  • y1: The y position of the first end of the line as measured from the top of the screen.
  • x2: The x position of the second end of the line as measured from the left of the screen.
  • y2: The y position of the second end of the line as measured from the top of the screen.
The following is an example of the code section required to draw a line in conjunction with the HTML file outlined at the start of this chapter. The attributes connect the point 100,50 (x1y1) with 300,150 (x2y2);
holder.append("line")          // attach a line
    .style("stroke", "black")  // colour the line
    .attr("x1", 100)     // x1 position of the first end of the line
    .attr("y1", 50)      // y1 position of the first end of the line
    .attr("x2", 300)     // x2 position of the second end of the line
    .attr("y2", 150);    // y2 position of the second end of the line
This will produce a line as follows;
Line
The line extends from the point 100,50 to 300,150.

points

The points attribute is used to set a series of points which are subsequently connected with a line and / or which may form the bounds of a shape. These are specifically associated with the polyline and polygonelements. Like the xy and x1x2y1y2 attributes, the coordinates are set from the top, left hand corner of the web page.
The data for the points is entered as a sequence of x,y points in the following format;
    .attr("points", "100,50, 200,150, 300,50"); 
Where 100,50 is the first x,y point then 200,150 is the second. Now is probably the best time to mention that the d3.js wiki makes the point that “it is typically more convenient and flexible to use the d3.svg.line path generator in conjunction with a path element” when describing complex shapes. So while drawing a polyline or polygon using this method may be possible, bear in mind that depending on your application, there may be a better way.
The following is an example of the code section required to draw a polyline in conjunction with the HTML file outlined at the start of this chapter. The additional style declarations are included to illustrate the shape better. The points values can be compared with the subsequent image.
holder.append("polyline")      // attach a polyline
    .style("stroke", "black")  // colour the line
    .style("fill", "none")     // remove any fill colour
    .attr("points", "100,50, 200,150, 300,50");  // x,y points
This will produce a polyline as follows;
Polyline using points attribute
The polyline extends from the point 100,50 to 200,150 to 300,50.

cxcy

The cxcy attributes are associated with the circle and ellipse elements and designate the centre of each shape. The coordinates are set from the top, left hand corner of the web page.
  • cx: The position of the centre of the element in the x axis measured from the left side of the screen.
  • cy: The position of the centre of the element in the y axis measured from the top of the screen.
The following is an example of the code section required to draw an ellipse in conjunction with the HTML file outlined at the start of this chapter. In it the centre of the ellipse is set by cxcy as 200, 100.
holder.append("ellipse")       // attach an ellipse
    .attr("cx", 200)           // position the x-centre
    .attr("cy", 100)           // position the y-centre
    .attr("rx", 100)           // set the x radius
    .attr("ry", 50);           // set the y radius
This will produce an ellipse as follows;
Ellipse with Centre at 200, 100
The centre of the ellipse is at x = 200 and y = 100 and the radius is 50 pixels vertically and 100 pixels horizontally.

r

The r attribute determines the radius of a circle element from the cxcy position (the centre of the circle) to the perimeter of the circle.
The following is an example of the code section required to draw a circle in conjunction with the HTML file outlined at the start of this chapter;
holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-center
    .attr("cy", 100)           // position the y-center
    .attr("r", 50);            // set the radius
This will produce a circle with a radius of 50 pixels as follows;
Circle with Radius of 50 Pixels
The centre of the circle is at x = 200 and y = 100 and the radius is 50 pixels.

rxry

The rxry attributes are associated with the ellipse element and designates the radius in the x direction (rx) and the radius in the y direction (ry).
  • rx: The radius of the ellipse in the x dimension from the cxcy position to the perimeter of the ellipse.
  • ry: The radius of the ellipse in the y dimension from the cxcy position to the perimeter of the ellipse.
The following is an example of the code section required to draw an ellipse in conjunction with the HTML file outlined at the start of this chapter. In it, the centre of the ellipse is set by cxcy as 200, 100 and the radius in the x direction (rx) is 100 pixels and the radius in the y direction (ry) is 50 pixels.
holder.append("ellipse")       // attach an ellipse
    .attr("cx", 200)           // position the x-centre
    .attr("cy", 100)           // position the y-centre
    .attr("rx", 100)           // set the x radius
    .attr("ry", 50);           // set the y radius
This will produce an ellipse as follows;
Ellipse with x Radius of 100 and y Radius of 50
The centre of the ellipse is at x = 200 and y = 100 and the radius is 50 pixels vertically and 100 pixels horizontally.

transform (translate(x,y), scale(k), rotate(a))

The transform attribute is a powerful one which allows us to change the properties of an element in several different ways.
  • translate: Where the element is moved by a relative value in the x,y direction.
  • scale: Where the element’s attributes are increased or reduced by a specified factor.
  • rotate: Where the element is rotated about its reference point by an angular value.
Without a degree of prior understanding, these transforms can appear to behave in unusual ways, but hopefully we’ll explain it sufficiently here so that you can appreciate the logic in the way they work.

TRANSFORM (TRANSLATE(X,Y))

The transform-translate attribute will take an elements position and adjust it based on a specified value(s) in the x,y directions.
The best way to illustrate this is with an example;
This is the code snippet from the HTML file outlined at the start of this chapter which draws a circle at the position 200,100 (cx,cy);
holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-center
    .attr("cy", 100)           // position the y-center
    .attr("r", 50);            // set the radius
This will produce a circle as follows;
Circle
If we add in a transform (translate(*x*,*y*)) attribute for values of x,y of 50,50 this will shift our circle by an additional 50 pixels in the x direction and 50 pixels in the y direction.
Here’s the code snippet that will draw our new circle;
holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-center
    .attr("cy", 100)           // position the y-center
    .attr("transform", "translate(50,50)") // translate the circle
    .attr("r", 50);            // set the radius
And here’s the resulting change;
Circle
The circle was positioned at the point 200,100 and then translated by 50 pixels in both axes to 250,150.
The original code snippet could in fact be written as follows;
holder.append("circle")        // attach a circle
    .attr("transform", "translate(200,100)") // translate the circle
    .attr("r", 50);            // set the radius
Since by default our starting position is 0,0 if we apply a translation of 200,100 we will end up at 200,100.

TRANSFORM (SCALE(K))

The translate-scale attribute will take an element’s attributes and scale them by a factor k.
Originally I thought that this attribute would affect the size of the element, but it affects more than that! As with the transform-translate attribute, the best way to illustrate this is with an example;
The following code snippet (in conjunction with the HTML file outlined at the start of this chapter) which draws a circle at the position 150,50 with a radius of 25 pixels;
holder.append("circle")     // attach a circle
    .attr("cx", 150)        // position the x-centre
    .attr("cy", 50)         // position the y-centre
    .attr("r", 25);         // set the radius
This will produce a circle as follows;
Circle
If we now introduce a transform-scale attribute with a scale of 2 we will see all three of the other attributes (cx,cy and r) scaled by a factor of two to 300, 100 and 50 respectively.
Here is the code;
holder.append("circle")             // attach a circle
    .attr("cx", 150)                // position the x-centre
    .attr("cy", 50)                 // position the y-centre
    .attr("r", 25)                  // set the radius
    .attr("transform", "scale(2)"); // scale the circle attributes
Which will produce a circle as follows;
Circle
In this example we can see that the position (cxcy) and the radius (r) have been scaled up by a factor of 2.

TRANSFORM (ROTATE(A))

The translate-rotate attribute will rotate an element and its attributes by a declared angle in degrees.
The ability to rotate elements is obviously a valuable tool. The transform-rotate attribute does a great job of it, but the key to making sure that you know exactly what will happen to an object is to remember where the anchor point is for the object and to ensure that the associated attributes are set appropriately. As with the transform translate & scale attributes, the best way to illustrate this is with an example;
The following is the code snippet (in conjunction with the HTML file outlined at the start of this chapter) which draws the text “Hello World” at the position 200,100 with the anchor point being the the middle of the text;
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("dy", ".35em")           // set offset y position
    .attr("text-anchor", "middle") // set anchor y justification
    .text("Hello World");          // define the text to display
This will produce text as follows;
Text: Anchored Middle-Centre
If we then apply a transform-rotate of 10 degrees as follows;
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("dy", ".35em")           // set offset y position
    .attr("text-anchor", "middle") // set anchor y justification
    .attr("transform", "rotate(10)")
    .text("Hello World");          // define the text to display
We will see the following on the screen;
Text: Anchored Middle-Centre: Transformed and Rotated
Obviously the text has been rotated, but hopefully you’ll have noticed that it’s also been displaced. This is because the transform-rotate attribute has been applied to both the text element (which has been rotated by 10 degrees) and the x,y attributes. If you imagine the origin point for the element being at 0,0, the centre, middle of the text element has been rotated about the point 0,0 by 10 degrees (hopefully slightly better explained in the following picture).
Text: All positioning Attributes Rotated
This could be seen as an impediment to getting things to move / change as you want to, but instead it’s an indication of a different way of doing things. The solution to this particular feature is to combine the transform-rotate with the transform-translate that we used earlier so that the code looks like this;
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("dy", ".35em")           // set offset y position
    .attr("text-anchor", "middle") // set anchor y justification
    .attr("transform", "translate(200,100) rotate(10)")
    .text("Hello World");          // define the text to display
And the image on the page looks like this;
Text: Rotated by 10 Degrees Anchored Middle-Centre
Which leads us to the final example for which is a combination of all three aspects of the transform attribute.
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("dy", ".35em")           // set offset y position
    .attr("text-anchor", "middle") // set anchor y justification
    .attr("transform", "translate(200,100) scale(2) rotate(10)")
    .text("Hello World");          // define the text to display
Text: Translated, Scaled and Rotated
Here we have a text element translated to its position on the page, rotated by 10 degrees about the centre of the text and scaled by a factor of two.

widthheight

width and height are required attributes of the rectangle element. width designates the width of the rectangle and height designates the height (If you’re wondering, I often struggle defining the obvious).
The following is an example of the code section required to draw a rectangle (using only the required attributes) in conjunction with the HTML file outlined at the start of this chapter;
holder.append("rect")       // attach a rectangle
    .attr("x", 100)         // position the left of the rectangle
    .attr("y", 50)          // position the top of the rectangle
    .attr("height", 100)    // set the height
    .attr("width", 200);    // set the width
This will produce a rectangle as follows;
Rectangle
The width of the triangle is 200 pixels and the height is 100 pixels.

text-anchor

The text-anchor attribute determines the justification of a text element
Text can have one of three text-anchor types;
  • start where the text is left justified.
  • middle where the text is centre justified.
  • end where the text is right justified.
The following is an example of code that will draw three separate lines of text with the three differenttext-anchor types in conjunction with the HTML file outlined at the start of this chapter;
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 50)            // set y position of bottom of text
    .attr("text-anchor", "start") // set anchor y justification
    .text("Hello World - start"); // define the text to display

holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("text-anchor", "middle") // set anchor y justification
    .text("Hello World - middle"); // define the text to display
    
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 150)           // set y position of bottom of text
    .attr("text-anchor", "end") // set anchor y justification
    .text("Hello World - end"); // define the text to display
This will produce an output as follows;
Text with Different text-anchor Attributes

dxdy

dx and dy are optional attributes that designate an offset of text elements from the anchor point in the x and y dimension . There are several different sets of units that can be used to designate the offset of the text from an anchor point. These include em which is a scalable unit, px (pixels), pt (points (kind of like pixels)) and %(percent (scalable and kind of like em))
We can demonstrate the offset effect by noting the difference in two examples.
The first is a simple projection of SVG text that aligns the text “Hello World” above and to the right of the anchor point at 200,100 (It does this in conjunction with the HTML file outlined at the start of this chapter.).
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text 
    .text("Hello World");     // define the text to display 
Which produces the following on the page;
Text with the Anchor at the Bottom Left Corner
The second example introduces the dx attribute setting the offset to 50 pixels. This adds another 50 pixels to the x dimension. We also introduce the dy attribute with an offset of .35em. This scalable unit allows the text to be set as a factor of the size of the text. In this case .35em will add half the height of the text to the y dimension placing the text so that it is exactly in the middle (vertically) of the 100 pixel line on the y dimension.
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text 
    .attr("dx", "50px")       // set offset x position
    .attr("dy", ".35em")      // set offset y position
    .text("Hello World");     // define the text to display 
Which produces the following on the page;
Text with 50 Pixel x Offset and Half Height y Offset
The text has been moved 50 pixels to the right and half the height of the text down the page.

textLength

The textLength attribute adjusts the length of the text to fit a specified value.
The following is a code snippet that prints the text “Hello World” above and to the right of the anchor point at 200,100 (It does this in conjunction with the HTML file outlined at the start of this chapter.). The addition of thetextLength attribute declaration in the code stretches the “Hello World” out so that it fills 150 pixels.
holder.append("text")          // append text
    .style("fill", "black")    // fill the text with the colour black
    .attr("x", 200)            // set x position of left side of text
    .attr("y", 100)            // set y position of bottom of text 
    .attr("textLength", "150") // set text length
    .text("Hello World");      // define the text to display 
Which produces the following on the page;
Text Stretched to 150 Pixels Wide
It is worth noting that while the text has been spread out, the individual letters remain un-stretched. Only the letter and word spacing has been adjusted. However, using the lengthAdjust attribute can change this.

lengthAdjust

The lengthAdjust attribute allows the textLength attribute to have the spacing of a text element controlled to be either spacing or spacingAndGlyphs;
  • spacing: In this option the letters remain the same size, but the spacing between the letters and words are adjusted.
  • spacingAndGlyphs: In this option the text is stretched or squeezed to fit.
The attribute can be best illustrated via an example. The following code snippet (which works in conjunction with the HTML file outlined at the start of this chapter) shows three versions of the text element. The top line is the standard text. The middle line is the textLength set to 150 and the lengthAdjust set to spacing (which is the default). The bottom line is the textLength set to 150 and the lengthAdjust set to spacingAndGlyphs.
holder.append("text")          // append text
    .style("fill", "black")    // fill the text with the colour black
    .attr("x", 200)            // set x position of left side of text
    .attr("y", 50)             // set y position of bottom of text 
    .text("Hello World");      // define the text to display 
    
holder.append("text")          // append text
    .style("fill", "black")    // fill the text with the colour black
    .attr("x", 200)            // set x position of left side of text
    .attr("y", 100)            // set y position of bottom of text 
    .attr("textLength", "150") // set text length
    .attr("lengthAdjust", "spacing")
    .text("Hello World");      // define the text to display 

holder.append("text")          // append text
    .style("fill", "black")    // fill the text with the colour black
    .attr("x", 200)            // set x position of left side of text
    .attr("y", 150)            // set y position of bottom of text 
    .attr("textLength", "150") // set text length
    .attr("lengthAdjust", "spacingAndGlyphs")
    .text("Hello World");      // define the text to display 
The image on the screen will look like the following;
Text Stretched in Three Ways
The image shows that the top line looks normal, the middle line has had the spaces increased to increase the length of the text and the bottom line has been stretched.


The description above (and heaps of other stuff) is in the D3 Tips and Tricks book that can be downloaded for free (or donate if you really want to :-)).

7 comments:

  1. Great post .. thanks for your time

    ReplyDelete
  2. Very Nice post. The examples are very helpful.
    Thank you

    ReplyDelete
  3. Very clearly written post ! Can't thank enough

    ReplyDelete
  4. Nice post. Hits some really good need to know things

    ReplyDelete
  5. Very nice post. Appreciate your time and effort.

    ReplyDelete
  6. excellent resource man, really appreciate it!

    ReplyDelete