Two.js an API that makes it easy to create 2D shapes with code. Follow along and you’ll learn how to create and animate shapes from JavaScript.
Two.js is renderer agnostic, so you can rely on the same API to draw with Canvas, SVG, or WebGL. The library has a lot of methods which can be used to control how different shapes appear on the screen or how they are animated.
Installation
The uncompressed version of the library has a size of around 128 KB, while the compressed version is 50 KB. If you are using the latest version, you can further reduce the size of the library using a custom build.
You can either download the minified version of the library from GitHub or you can link directly to the CDN hosted version. Once you have added the library to your webpage, you can start drawing and animating different shapes or objects.
Creating Basic Shapes
First, you need to tell Two.js about the element on which you want to draw and animate your shapes. You can pass some parameters to the Two
constructor to set things up.
Set the type of renderer using the type
property. You can specify a value like svg
, webgl
, canvas
, etc. The type
is set to svg
by default. The width and height of the drawing space can be specified using the width
and height
parameters. You can also set the drawing space to the full available screen using the fullscreen
parameter. When fullscreen
is set to true, the values of width
and height
will be disregarded.
Finally, you can tell Two.js to automatically start an animation with the help of the Boolean autostart
parameter.
After passing all the desired parameters to the constructor, you can start drawing lines, rectangles, circles, and ellipses.
You can draw a line using two.makeLine(x1, y1, x2, y2)
. Here, (x1, y1)
are the coordinates of the first end point, and (x2, y2)
are the coordinates of the second end point. This function will return a Two.Line
object, which can be stored in a variable for further manipulation at a later point.
In a similar manner, you can draw normal and rounded rectangles using two.makeRectangle(x, y, width, height)
and two.makeRoundedRectangle(x, y, width, height, radius)
respectively. Remember that x
and y
determine the center of the rectangle, instead of its top left coordinates like many other libraries. The width
and height
parameters will determine the size of the rectangle. The radius
parameter is used to specify the value of the radius for the rounded corner.
You can also render circles and ellipses on a webpage using two.makeCircle(x, y, radius)
and two.makeEllipse(x, y, width, height)
respectively. Just like the rectangles, the x
and y
parameters specify the center of the circle or ellipse. Setting the width
and height
to the same value in the case of an ellipse will render it like a circle.
One useful method in Two.js that you will use frequently is two.makeGroup(objects)
. You can either pass a list of different objects or pass an array of objects, paths or groups as the parameter to this method. It will also return a Two.Group
object.
Manipulating Objects in a Group
After you have created a group, you can manipulate all its children at once using properties that the group makes available to you.
The stroke
and fill
properties can be used to set the stroke and fill color for all children in a group. They will accept all valid forms in which you can represent a color in CSS. This means that you are free to use RGB, HSL, or hex notation. You can also simply use the name of the color, like orange
, red
, or blue
. Similarly, you can set values for all other properties like linewidth
, opacity
, miter
, and cap
. It is possible to remove the fill and stroke from all children in a group using the noFill()
and noStroke()
methods.
You can also apply other physical transformations like scale
, rotation
, and translation
. These transformations will be applied on individual objects. Adding new objects to a group and removing them is easy with methods like add()
and remove()
.
Defining Gradients and Writing Text
You can define both linear and radial gradients in Two.js. Defining a gradient does not mean that it will be rendered automatically on the screen, but it will be available for you to use when setting the fill
or stroke
values of various objects.
You can define a linear gradient using two.makeLinearGradient(x1, y1, x2, y2, stops)
. The values x1
and y1
determine the coordinates of the start of the gradient. Similarly, the values x2
and y2
determine the coordinates of the end of the gradient. The stops
parameter is an array of Two.Stop
instances. These define the colors of each part of the array and where each color transitions into the next. They can be defined using new Two.Stop(offset, color, opacity)
, where offset
determines the point on the gradient where that particular color has to be fully rendered. The color
parameter determines the color of the gradient at the particular point. You can use any valid CSS color representations as its value. Finally, the opacity
parameter determines the opacity of the color. The opacity is optional, and it can have any value between 0 and 1.
You can define radial gradients in a similar manner using two.makeRadialGradient(x, y, radius, stops, fx, fy)
. In this case, the values x
and y
determine the center of the gradient. The radius
parameter specifies how far the gradient should extend. You can also pass an array of stops to this method in order to set the color composition of the gradients. The parameters fx
and fy
are optional, and they can be used to specify the focal position for the gradient.
Check out some of the types of gradient and their code in the CodePen below.
Remember that the x
and y
position of the gradients are with respect to the origin of the shape they are trying to fill. For instance, a radial gradient which is supposed to fill a shape from the center will always have x
and y
set to zero.
Two.js also allows you to write text on the drawing area and update it later according to your needs. This requires the use of the method two.makeText(message, x, y, styles)
. It might be evident from the name of the parameters that message
is the actual text that you want to write. The parameters x
and y
are the coordinates of the point which will act as the center for writing the text. The styles
parameter is an object which can be used to set the values of a large set of properties.
You can use styles to set the values of properties like font family
, size
, and alignment
. You can also specify the value of properties like fill
, stroke
, opacity
, rotation
, scale
, and translation
.
Creating a Two.js Project
After learning about all these methods and properties, it is time to apply them to a project. In this tutorial, I will show you how we can use Two.js to render the first ten elements of the periodic table with electrons rotating around the nucleus. The nucleus will also have some slight movement to improve the visual appeal of our representation.
We begin by defining some variables and functions which will be used later.
var centerX = window.innerWidth / 2; var centerY = window.innerHeight / 2; var elem = document.getElementById("atoms"); var elementNames = [ "", "Hydrogen", "Helium", "Lithium", "Beryllium", "Boron", "Carbon", "Nitrogen", "Oxygen", "Fluorine", "Neon" ]; var styles = { alignment: "center", size: 36, family: "Lato" }; var nucleusCount = 10; var nucleusArray = Array(); var electronCount = 10; var electronArray = Array(); function intRange(min, max) { return Math.random() * (max - min) + min; }
The above code stores the coordinates of the center of our window in the variables centerX
and centerY
. These will be used later to place our atom in the center. The elementNames
array contains the names of the first ten elements of the periodic table. The index of each name corresponds to the number of electrons and protons of that element, and it begins with an empty string. The styles
object contains properties for styling the text object.
We have also defined a function intRange()
to get a random integer value within given extremes.
var two = new Two({ fullscreen: true }).appendTo(elem); var protonColor = two.makeRadialGradient( 0, 0, 15, new Two.Stop(0, "red", 1), new Two.Stop(1, "black", 1) ); var neutronColor = two.makeRadialGradient( 0, 0, 15, new Two.Stop(0, "blue", 1), new Two.Stop(1, "black", 1) ); for (i = 0; i < nucleusCount; i++) { nucleusArray.push(two.makeCircle(intRange(-10, 10), intRange(-10, 10), 8)); } nucleusArray.forEach(function(nucleus, index) { if (index % 2 == 0) { nucleus.fill = protonColor; } if (index % 2 == 1) { nucleus.fill = neutronColor; } nucleus.noStroke(); });
This creates an instance of Two and defines two radial gradients. The red/black radial gradients will represent protons, and blue/black gradients will represent neutrons.
We have used the intRange()
function to place all these neutrons and protons within 20 pixels of each other. The makeCircle()
method also sets the radius of these protons and neutrons to 10 pixels. After that, we iterate over nucleusArray
and fill each circle with a different gradient alternately.
for (var i = 0; i < 10; i++) { if (i < 2) { var shellRadius = 50; var angle = i * Math.PI; electronArray.push( two.makeCircle( Math.cos(angle) * shellRadius, Math.sin(angle) * shellRadius, 5 ) ); } if (i >= 2 && i < 10) { var shellRadius = 80; var angle = (i - 2) * Math.PI / 4; electronArray.push( two.makeCircle( Math.cos(angle) * shellRadius, Math.sin(angle) * shellRadius, 5 ) ); } }
Placing neutrons and protons inside the nucleus was easy. However, properly placing the electrons at a uniform distance will require a little maths. We use the shellRadius
variable to specify the distance of different electron shells from the nucleus. A whole circle covers an angle equal to 2 PI radians. We can place different electrons uniformly by distributing the 2 PI radians between them equally.
The Math.cos()
and Math.sin()
functions are used to separate the vertical and horizontal components of the position vector of different electrons based on their angle.
var orbitA = two.makeCircle(centerX, centerY, 50); orbitA.fill = "transparent"; orbitA.linewidth = 2; orbitA.stroke = "rgba(0, 0, 0, 0.1)"; var orbitB = two.makeCircle(centerX, centerY, 80); orbitB.fill = "transparent"; orbitB.linewidth = 2; orbitB.stroke = "rgba(0, 0, 0, 0.1)"; var groupElectronA = two.makeGroup(electronArray.slice(0, 2)); groupElectronA.translation.set(centerX, centerY); groupElectronA.fill = "orange"; groupElectronA.linewidth = 1; var groupElectronB = two.makeGroup(electronArray.slice(2, 10)); groupElectronB.translation.set(centerX, centerY); groupElectronB.fill = "yellow"; groupElectronB.linewidth = 1; var groupNucleus = two.makeGroup(nucleusArray); groupNucleus.translation.set(centerX, centerY);
This part of the code puts electrons from different shells as well as neutrons and protons in their own separate groups. It also sets the fill colors for all electrons in a specific orbit at once.
two .bind("update", function(frameCount) { groupElectronA.rotation += 0.025 * Math.PI; groupElectronB.rotation += 0.005 * Math.PI; groupNucleus.rotation -= 0.05; }) .play(); var text = two.makeText("", centerX, 100, styles); nucleusArray.forEach(function(nucleus, index) { nucleus.opacity = 0; }); electronArray.forEach(function(electron, index) { electron.opacity = 0; });
This part of the code sets the opacity of individual electrons and protons to zero. It also tells Two.js to rotate the electrons and protons at specific speeds.
visible = 0; document.addEventListener("click", function(event) { if (visible < nucleusArray.length) { nucleusArray[visible].opacity = 1; electronArray[visible].opacity = 1; visible++; text.value = elementNames[visible]; } else { nucleusArray.forEach(el => el.opacity=0); electronArray.forEach(el => el.opacity=0); visible = 0; text.value = elementNames[0]; } });
The final part of the code allows us to iterate through the elements by clicking the mouse or tapping. To load the next element, we make one more electron and one more proton or neutron visible and update the element name. After clicking on the last element, all the particles are hidden again so we can start over. The visible
variable keeps track of the number of atomic particles that are currently visible so that we can show or hide them accordingly.
Try clicking or tapping in the following CodePen demo to see the first ten elements of the periodic table.
Final Thoughts
We began this tutorial with a brief introduction to the Two.js library and how it can be used to draw shapes like rectangles, circles, and ellipses. After that, we discussed how we can group different objects together to manipulate them all at once. We used this ability to group elements to translate and rotate them in synchronization. These tools all came together in our animation of the atoms of the first ten elements in the periodic table.
As you can see, creating animated 2D graphics is very easy using Two.js. The focus of this post was to help you get started quickly, so we only covered the basics. However, you should read the official documentation to learn more about the library!
Powered by WPeMatico