Requirements
To create this applet, you'll need the Raphael library, jQuery, and jQuery UI (for the slider). Note you'll also need a jQuery UI CSS for the slider to work properly. The following CDNs can be included between your
<head></head>
tags:
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.4/raphael-min.js"></script>
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.11.4/themes/ui-lightness/jquery-ui.css">
HTML
Between the
<body></body>
tags, create two divs: one to hold the canvas, and one to hold the slider. You can arrange these in either order. In this example, I'll give the canvas an id of "canvas" and the slider div will have an id of "rslider". We'll also include an area for the JavaScript:
<div id="canvas"></div>
<div id="rslider"></div>
<script></script>
The JavaScript
All of the following code should be placed in the
<script></script>
tags created in the last step. First, we'll set up variables to store the dimensions of the canvas. Using jQuery, we retrieve the available width, and multiply by 0.7 so that the canvas doesn't take up the entire screen. Since we're making squares, we'll set the height to be equal to the width. Then, we'll create our Raphael canvas, called 'paper' in the div created earlier:
var w = 0.7*$('#canvas').width()
var h = w
var paper = new Raphael( document.getElementById('canvas'), w, h )
Next, set up an array of colors. Use as many as you like:
var colors = ["#aaa", "#c66e77", "#cba271", "#cbc671", "#b7c56e", "#7abc69", "#67b9ad", "#6791b9", "#6767b9", "#b9323e", "#c05d34", "#c08134", "#c0b934", "#a0b631", "#44a82d", "#2ca498", "#2c69a4", "#2e2ca4", "#6c2ca4"]
To get the gradient effect, we'll use a function that takes in a color (as a hexadecimal string) and a luminance (a number between -1 and 1) and returns a new hexadecimal string with the luminance applied to that color. This way, we can create multiple shades using the same color:
function ColorLuminance(hex, lum) {
hex = String(hex).replace(/[^0-9a-f]/gi, '');
if (hex.length < 6) {
hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
}
lum = lum || 0;
var rgb = "#", c, i;
for (i = 0; i < 3; i++) {
c = parseInt(hex.substr(i*2,2), 16);
c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
rgb += ("00"+c).substr(c.length);
}
return rgb;
}
Then, create an array with a few choices for the luminance. Each should be a value between -1 and 1. A luminance near -1 will return a very dark shade, and values closer to 1 will return shades that are almost white. The demo uses four values:
var lum = [0.5,0.25,-0.25,-0.5]
Now, we'll set up some initial values. When the page is first displayed, we'll want to see 30 squares (feel free to change this). The size of the largest square will depend on the size of the canvas. I'll let the largest square be 3/4 the canvas size, since the square will be rotated and I don't want the corners chopped:
var numberRects = 30
var maxsize = 0.75*w
We'll create the actual squares within a function, since we'll need to do it more than one. Each time the function is called, we'll clear the canvas and construct the squares in a for loop. Each square is layered on top of the previous one, so to get the stacked effect, we'll center them all at the same point but make each square a little smaller than the one before it. The size difference depends on how many squares we have, so we'll set up a variable to store that ratio.
function makeRects() {
paper.clear() // clear the canvas, so we're not drawing new squares on the old ones
var sizeChange = maxsize/numberRects // variable to store size difference between the squares
var cstart = -1 // color counter (see more below)
for (i = 0; i < numberRects; i++) { // loop over the number of rectangles we need to make
if ( i%lum.length == 0 ) { cstart++ }
// We want to keep the same color for four squares in a row, only changing their luminance.
// The i%lum.length == 0 code checks to see if i is a multiple of the number of values in the lum array.
// If it is, then we'll increase the value of cstart (and choose the color from our color array with index cstart).
// Otherwise, we'll keep using the same color.
cstart = cstart % colors.length
// If we only have 10 colors, but we're making 100 rectangles, we'd run out of colors!
// So we'll reset cstart back to 0 each time it reaches the end of the color array.
var thisRectSize = maxsize - i*sizeChange
// How big should the latest rectangle be? It depends on the order it's created, and how large the biggest one is.
r = paper.rect( w/2 - thisRectSize/2, w/2 - thisRectSize/2, thisRectSize, thisRectSize, 10)
// Make a rectangle, centered on the canvas.
// Since the definition point of a rectangle is the top left corner, we have to do some adjustment based on its size to get it centered.
// We'll also give each rectangle a corner radius of 10, to get the rounded corners.
// For small squares, they'll appear circular - decrease the 10 if you want 'squared' squares.
r.attr({'transform':'r'+(i*5)})
// Rotate the rectangle, based on the order it's created. Each rectangle is angled 5 degrees more than the original one.
r.attr({'fill': ColorLuminance( colors[cstart], lum[i%(lum.length)]), 'stroke-width':0})
// Fill in the rectangle with the color at index cstart from the color array, and with a new luminance, using the function created above.
// Set the stroke to 0 so the rectangles don't have an outline.
}
}
makeRects() // Call the function to draw the first set of 30 rectangles on the screen
You should now see the squares on your page, but there's no slider or way to change the number of squares. That will be done with jQuery UI:
$( "#rslider" ).slider({ // create the slider in the slider div
min: 2, // require at least 2 squares on the canvas
max: 300, // limit the number of squares to 300
animate: 'fast', // if you click the slider bar, move the handle to location
value: 30, // initial starting value is 30 rectangles
width: w, // let the slider take up the whole screen
slide: function(e,ui) { numberRects = ui.value; makeRects() }
// When the slider is moved, retrieve its new value and redefine numberRects as this number and draw the rectangles again.
});
Issues to Notice
Every time the slider is repositioned, the canvas is cleared and new squares are drawn. Each new square requires a function call (to set its color) and a transformation - both of which take time. As a result, when the number of squares is larger (even just at 100, but particularly over 200) we see a slight delay in the drawing update. This is a good reminder of the limitations with web graphics!
Complete Uncommented Code
Copy and paste the code below into a blank html page, and you'll have your own applet:
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.4/raphael-min.js"></script>
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.11.4/themes/ui-lightness/jquery-ui.css">
</head>
<body>
<div id="canvas"></div>
<div id="rslider"></div>
<script>
var w = 0.7*$('#canvas').width()
var h = w
var paper = new Raphael( document.getElementById('canvas'), w, h )
var colors = ["#aaa", "#c66e77", "#cba271", "#cbc671", "#b7c56e", "#7abc69", "#67b9ad", "#6791b9", "#6767b9", "#b9323e", "#c05d34", "#c08134", "#c0b934", "#a0b631", "#44a82d", "#2ca498", "#2c69a4", "#2e2ca4", "#6c2ca4"]
var lum = [0.5,0.25,-0.25,-0.5]
function ColorLuminance(hex, lum) {
hex = String(hex).replace(/[^0-9a-f]/gi, '');
if (hex.length < 6) {
hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
}
lum = lum || 0;
var rgb = "#", c, i;
for (i = 0; i < 3; i++) {
c = parseInt(hex.substr(i*2,2), 16);
c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
rgb += ("00"+c).substr(c.length);
}
return rgb;
}
var numberRects = 30
var maxsize = 0.75*w
function makeRects() {
paper.clear()
var sizeChange = maxsize/numberRects
var cstart = -1
for (i = 0; i < numberRects; i++) {
if ( i%4 == 0 ) { cstart++ }
cstart = cstart % colors.length
var thisRectSize = maxsize - i*sizeChange
r = paper.rect( w/2 - thisRectSize/2, w/2 - thisRectSize/2, thisRectSize, thisRectSize, 10)
r.attr({'transform':'r'+(i*5)})
r.attr({'fill': ColorLuminance( colors[cstart], lum[i%(lum.length)]), 'stroke-width':0})
}
}
makeRects()
$( "#rslider" ).slider({
min: 2,
max: 300,
animate: 'fast',
value: 30,
width: w,
slide: function(e,ui) { numberRects = ui.value; makeRects() }
});
</script>
</body>