14 KiB
SVG
This lesson covers how to create various SVG elements, the foundation of D3.js
Base tag
All svg elements go inside an <svg></svg> tag. Let's set this up:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
</head>
<body>
<svg></svg>
</body>
</html>
If we inspect this in our browser's dev tools, we'll see this:
Basic elements
We can draw elements in our <svg> element by adding a variety of predefined tags as child elements of the <svg>. This is just like in HTML where we add <div>, <a>, and <img> tags inside the <body> tag. There are many tags like <circle>, <rect>, and <line> that we'll explore in a bit. Here's just one example:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
</head>
<body>
<svg>
<circle><circle>
</svg>
</body>
</html>
Note that we can't see the circle because it doesn't have a radius:
We'll talk about this more later, but for now, if we want to see the circle, we can add a special attribute that all <circle> elements take:
<circle r=50><circle>
This tells the browser to give the circle a radius of 50px:
At the moment though, we only see the bottom right quarter of the <circle>. This is because the center of the <circle> is being drawn at the very upper left corner of the <svg>, and the rest of it is being clipped outside the <svg>. We can change this by changing the position of the circle, which we'll do next.
Positioning
- The
<svg>tag is an inline element, like an image (as opposed to a block element like a<div>) - Elements within the
<svg>are positioned similar to photoshop, with a set of coordinates which follow the form (x,y). This is different from HTML, where elements are laid out relative to each other-
e.g. for (10,15), x=10 and y=15
-
the point (0,0) is the top left of the
<svg>element -
as y values increase, the point moves vertically down the
<svg>element -
don't confuse this with a typical coordinate system that has 0,0 at the bottom left with a point moving up as y increases in value
-
we can use negative x/y values
- -x moves left
- -y moves up
-
Let's adjust the position of our circle in our previous section by adjusting cx and cy values:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
</head>
<body>
<svg>
<circle r=50 cx=50 cy=50><circle>
</svg>
</body>
</html>
Now we see the full circle:
Styling
Each tag inside an <svg> can be styled with various attributes:
fill=redorfill=#ff0000will alter fill colorstroke=redorstroke=#ff0000will alter stroke color. Stroke is a line that surrounds each elementstroke-width=4will adjust the width of the strokefill-opacity=0.5will adjust the transparency of the fill colorstroke-opacity=0.5will adjust the transparency of the stroke colortransform = "translate(2,3)"will translate the element by the given x,y valuestransform = "scale(2.1)"will scale the size of the element by the given proportion (e.g. 2.1 times a s big)transform = "rotate(45)"will rotate the element by the given number of degrees
Let's style the circle we positioned previously:
<circle r=50 cx=50 cy=50 fill=red stroke=blue stroke-width=5><circle>
Now we get this:
Note that the stroke in the image above is getting clipped. That's because the stroke is create outside the element. If we wanted to see the full stroke, we can resize the circle:
<circle r=45 cx=50 cy=50 fill=red stroke=blue stroke-width=5><circle>
Now we get:
Styling can also be done with CSS:
Create an external app.css file with the following contents:
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);
r:50px;
}
Reference the file in our html:
<head>
<link rel="stylesheet" href="app.css">
</head>
and remove our previous inline styling that we had on our <circle> tag:
<circle></circle>
Now we get this:
Note that I've hovered over the element in the dev tools to show that the element has been rotated 45 degrees. That's what the blue box is.
Important SVG elements
To demo each element, we'll use the following code as a starting point and then add each element inside the <svg> tag:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
</head>
<body>
<svg width=800 height=600>
</svg>
</body>
</html>
Circle
- attributes
rradiuscxx positioncyy position
<circle r="50" cx="200" cy="300"/>
Line
- attributes
x1starting x positiony1starting y positionx2ending x positiony2ending y position
<line x1="0" y1="0" x2="100" y2="100"/> <!-- this element won't be visible because it doesn't have a stroke -->
<line x1="0" y1="0" x2="100" y2="100" stroke="purple"/>
Rectangle
- attributes
xx position of top leftyy position of top leftwidthwidthheightheight
<rect x="50" y="20" width="150" height="150"/>
Ellipse
- attributes
cxx positioncyy positionrxx radiusryy radius
<ellipse cx="200" cy="80" rx="100" ry="50"/>
Polygon
- attributes
pointsset of coordinate pairs- each pair is of the form x,y
<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
<polyline points="20,20 40,25 60,40 80,120 120,140 200,180" stroke="blue" fill="none"/>
Text
The content of the tag is the text to be displayed
- attributes
xx position of top left corner of the elementyy position of top left corner of the element
<text x="0" y="15">I love SVG!</text>
You can use font-family and font-size CSS styling on this element
Group
- This element has no special attributes, so use transform on it.
- You can put multiple elements inside it and all of its positioning and styling will apply to its children
- Its good for moving many elements together as one
<g transform = "translate(20,30) rotate(45) scale(0.5)">
</g>
Bezier Curves
What if we want to draw complex organic shapes? To do this, we'll need to use paths. First, though, to understand paths, you have to first understand bezier curves.
Cubic Bezier Curves
Each curve is made up of four points:
- start point
- end point
- starting control point
- ending control point
The start/end point are where the curve starts and ends. The control points define the shape of the curve. It's easiest to conceptualize if with the following diagram:
As we manipulate the control points, we can see how the shape of the curve is affected:
You can even join multiple bezier curves together:
Smooth Cubic Bezier Curves
Smooth Cubic Bezier curves are just a way to simplify some cubic bezier curves when they're joined together. Take a look at the two control points in the red square below:
The point in the lower left of the square is the end control point of the first curve. The point in the upper right of the square is start control point of the second curve.
Note that the two points are reflections of each other around the central black dot which is the end point of the first curve and the start point of the second curve. The two points are exactly 180 degrees in opposite directions, and they have the same distance from that central point.
In scenarios like this, where the start control point of one curve is a reflection of the end control point of the previous curve, we can skip stating the start control point of the second curve. Instead, we let the browser calculate it, based on the end control point of the first curve.
We can also omit the start point since the browser knows it will be the same as the end point of the previous curve. In summary, to define that second curve, we only need two points:
- the end point
- the end control point
Quadratic Bezier Curve
Another situation where we can simplify defining a bezier curve is where the start control point and end control point are the same
Here we can define the curve with just three points:
- the start point
- the end point
- one single control point which acts as both control point and end control point
Smooth Quadratic Bezier Curve
The final situation where we can simplify defining a bezier curve is where we have a quadratic bezier curve (one single control point) that is a reflection of the end control point previous curve:
In this situation, the browser knows the start point of the curve (the end point of the previous curve), and it can calculate the single control point needed (since it is a quadratic bezier curve) based on the end control point of the previous curve. This is a smooth quadratic bezier curve, and you only need one point to define it:
- the end point
Drawing a path
Now that we understand bezier curves, we can use them in our SVGs with <path> elements
These tags take a d attribute which stands for a set of drawing commands. The value of this attribute is any combination of the below:
- M = moveto: move the drawing point to the given coordinates
- M x y
- L = lineto: draw a line from the previous point in the
dcommand to the point given- L x y
- C = curveto: draw a curve from the previous point in the
dcommand to the point given with the given control points- 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 curve
- uses reflection of x2 y2 of previous S or C command for x1 y1
- 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
- follows another curve
- uses reflection of previous quadratic curve's control point as its control point
- Z = closepath: draw a line from the previous point in the
dcommand to the first point in thedcommand
Note: All of the commands above can also be expressed with lower case letters. Capital letters means absolutely positioned, lower case letters mean the all points are expressed relative to the previous point in the d command.
<path d="M150 0 L75 200 L225 200 Z" stroke="black" fill="transparent"/>
<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"/>
Arcs
An arc is a command that you can add to a path that will draw part of an ellipse. To do this, we first begin with only two points:
For any two points, there are only two ellipses with the same width/height and rotation that contain both points. In the image above, try to imagine moving the ellipses around without rotating or scaling them. As soon as you do, they loose contact with at least one of the two given points. One point might be on the ellipse, but the other won't be.
We can use this information to draw any of the four colored arcs shown in the image above.
Make the following code part of the d attribute's value on a <path> element.
A rx ry x-axis-rotation large-arc-flag sweep-flag x y
- A - create an arc draw command
- rx - x radius of both ellipses (in px)
- ry - y radius of both ellipses (in px)
- x-axis-rotation - rotate both ellipses a certain number of degrees
- large-arc-flag - whether or not to travel along the arc that contains more than 180 degrees (1 to do so, 0 to not do so)
- sweep-flag - whether or not to move along the arc that goes clock-wise (1 to do so, 0 to not do so)
- x - destination x value (in px)
- y - destination y value (in px)
The large-arc-flag determines whether to make an arc that is greater than 180 degrees. Here's an example without it (note, the red shows the arc drawn, while the green arcs are other possible arcs that could be drawn using a combination of large-arc-flag and sweep-flag):
Note, it chose one of the two smaller arcs. Here's an example with the large-arc-flag set:
Note, it chose on of the two larger arcs.
In the previous example, for both situations where the large-arc-flag was set or not set, there was one other arc that could have been taken. To determine which of those two arcs to take, we use the sweep-flag, which determines whether to travel clock-wise or not from starting point to ending point. Here's an example with the large-arc-flag set, but without the sweep-flag set:
Note we move in a counter clock-wise motion from start to end (left to right). If we set the sweep-flag, we travel in a clock-wise motion:
Here are all the possible combinations for sweep-flag and large-arc-flag:
Play with the values here: http://codepen.io/lingtalfi/pen/yaLWJG





























