Creating a Word Search with Raphael
In this post, we will cover the basic steps needed to create a word search game using Raphaël.js. The idea for this post came from a word search game that I orginally coded in Java using Pulpcore. I wanted to be able to run the app on my Chromebook but it needed to be rewritten, as the Java plugin isn't supported. Due to the low numbers of sprites drawn on the screen at any given time and their rather static nature, I decided to use SVG as the display technology. As we'll see later in this post, while Canvas is perfectly capable of rising to the task, SVG gives us a couple of built-in advantages.
Raphaël Basics
The Raphael
function creates a SVG object that we can further build
upon using JavaScript. We'll use this object in the coming chapters to
draw rectangles, text, and paths. The most basic constructor for a
Raphael object is listed in the snippet below. The default constructor
adds the SVG node to the end of the page. Although we won't cover them
here, there are options to draw inside a particular HTML div as well.
var paper = Raphael(50, 50, 640, 480);
This snippet in particular creates an object starting at (50,50) with a width of 640 pixels and a height of 480 pixels.
Wordsearch Basics
The gameplay for a word search is fairly simple. A list of words are hidden in typically a rectangular grid of characters. Words can be hidden vertically, horizontally, or diagonally. When one locates a word, it is circled or struck through in the grid and crossed out in the word list. Play continues until all words are found. Sometimes series of numbers will be hidden in puzzles as well. To create the game, we only need to know about a few different kinds of Raphaël functions: text, print, rect, and path. In the following sections, we'll cover them all in a bit more detail.
Drawing Text with Raphaël.js
Raphaël has two distinct commands for drawing text: text
and print
.
Both methods were used to create the game. print
slightly edges out
text
for its ability to use packaged fonts.
The `text` function
The text function shown in the snippet below, takes only a text string to dray and x and y positions.
title = paper.text(100, 100, "Spooky things");
Although it doesn't give you an option to do so when declaring the variable you are able to set the following attributes on a text object.
- font (String)
- font-family (String)
- font-size (Number)
- font-weight (String)
- text-anchor ('start', 'middle', 'end')
The text function using the default font and font-size unless modified by the attributes listed above. You are also limited to the fonts installed on the client's machine.
The `print` function
Before you can use custom fonts with Raphaël, you'll need to convert them to a format that Raphaël can read. Cufòn is a project that converts TrueType and OpenType fonts to SVG fonts and VML fonts(IE8 and below). The web interface is fairly straightforward. Simply upload a font, select what styles to export and grab the JavaScript file it outputs. Google Web Fonts is a great source for free and open source fonts to use in your applications. Provided we have included a Droid Sans font on our path, listed below is the code to display a string using the font with a 28pt font size.
helloText = paper.print (x, y, "Hello", paper.getFont("Droid Sans"), 28)
At first glance, this might seem like a more concise way to do what the text function already does. The key difference is while the text function injects a text object into the SVG DOM, the print function creates a series of paths. A text object has the same attributes for all its characters whereas a the output of print is a group of paths each capable of its own attributes. Paths have a greater level of flexibility. They can b e morphed intoone another or transformed in many different ways. Each character can be referenced individually. If we wanted to change the color of the "o" to red, we would execute:
helloText[4].attr({fill:'red'})
Paths
While I won't go into the minutae here, at the most basic level, paths are lists of instructions to draw objects. You can draw lines and curves to create more complex objects. For the purposes of the word search game, we'll be using them in a very constrained manner. After successfully locating a word a path will cross it out in the grid. The following snippet shows the CoffeeScript code to draw a line from (x1,y1) to (x2,y2).
pathData = "M#{x1} #{y1} L#{x2} #{y2}"
You can find list of the possible path instructions in my book or on the W3C reference site.
Putting it all together
So far, we have talked about the elements that allow up to draw text and paths on the screen. One missing element is interaction. Every element in Raphaël can respond to mouse events. mouseout, mouseover, click, and pretty much any other mouse-based event you'd expect to be supported is built-in. In the word search game, I used events to indicate which letters were being selected and where the mouse was hovering. The snippet below shows the mouseDown function to start tracking and change the color of the letters when the mouse is down. When the mouse is released, it checks to see if the accumulated letters are in the word list and clears the states from the letters in the grid.
downHandler = (event) ->
#start tracking position
state = game.isSelecting
if state is no
game.tempWord = new LetterSet()
game.isSelecting = yes
game.tempWord.push self
self.text.attr {fill: "red"}
else
game.isSelecting = no
game.findWord()
game.clearStates()
For a mouse event to work, it depends on the event firing while you are in the confines of the element. For some fonts, it's very difficult to do. We can increase the area around the letter by drawing another object and having its events be tied to the letter. I chose to use rectangle although any other shape or path will serve the same purpose. The snippet below shows the creation of a rectangle and adding a mousedown function to it.
rect = paper.rect (x, x, sideLength, sideLength)
rect.mouseDown(downHandler)
Source Code
Now that you've learned how it works, download the source code here.