![roundrr](http://addyosmani.com/blog/wp-content/uploads/roundrr4E.jpg)
Hey guys. Quite often with image galleries powered by jQuery, we’re presented with a very similar layout – one which contains the images or items presented inside a rectangle. This is great for providing an experience users are used to, but there are a few interesting alternatives that you may like to hear about – how about displaying your images or items around a shape like a circle or an ellipse instead?. In today’s post I’ll be showing you how to do just that with a brand new plugin I’ve written called jQuery Roundrr.
I’ll also show you how you can leverage the power of math with JavaScript to generate coordinates for positioning your images in interesting ways without needing to use HTML5 or CSS3 – just pure javascript. The benefit of this approach is that much of what I show you today will work cross-browser right out of the box.
Before we get started, here are some demos of Roundrr in action. Remember you can also use the left and right keys on your keyboard to control the interface.
![pick mode](http://addyosmani.com/blog/wp-content/uploads/2010/10/snipp2.jpg)
![twt](http://addyosmani.com/blog/wp-content/uploads/2010/10/twt.jpg)
Introduction
Today’s post is broken up into two parts. In part 1, I’m going to introduce you to some of the concepts involved in getting the coordinates we need to plot our image galleries around a shape and in part 2 I’ll introduce you to jQuery Roundrr plugin I wrote that will allow you to easily create interactive event-based galleries using jQuery and a little CSS.
Part 1 – Getting started with Shape-based coordinates
Understanding Coordinate-Based Positioning
In most programming languages that work with a visual window or canvas, you’re provided with a means for positioning content using the concept of X and Y coordinates. These allow you to tell the presentation layer or interpreter (in the case of the browser, CSS) where it is on the screen that you would like your content to be displayed.
Example 1. When you create a div container, center it and then fill it with a list of images without specific positions, what you’re allowing the browser to do is calculate these positions for you.
Example 2. If you tell the browser that each image in your list should be specifically positioned (using margin-left and margin-top) you’re taking control of exactly where you would like the content to be positioned. We’re going to leverage these two CSS attributes for today’s project.
How To Position Items Around A Cirlcle
![math](http://addyosmani.com/blog/wp-content/uploads/2010/10/math.jpg)
A lot of the math that goes into basic shape-based positioning is relatively straight-forward. Chances are you probably already covered it in highschool, so much of this will seem familiar. Consider that a circle has a center (j,k) and a radius r. The x and y positions for any point that occurs on the circle’s path is then
x(t) = r cos (t) + j, y(t) = r sin(t) + k. A more generic way of writing this which can be used for our first attempt at coordinate-based positioning is:
- x = centerX + Math.cos(radians) * radius;
- y = centerY + Math.sin(radians) * radius;
where radians = (angle_of_the_circle/180) * Math.PI. centerX is the X center of the circle on our page and centerY is the Y center of the circle.
![jshape](http://addyosmani.com/blog/wp-content/uploads/2010/10/jshape.jpg)
In a moment I’m going to show you how to get through this math in JavaScript and output the positions you need to display content around a circle.If we were to implement the above equations using jQuery (with vanilla JavaScript intact for the equations) this would look like:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- function drawCircle(selector, center, radius, angle, x, y)
- {
-
- var total = $(selector).length;
- var alpha = Math.PI * 2 / total;
-
- $(selector).each(function(index)
- {
- var theta = alpha * index;
- var pointx = Math.floor(Math.cos( theta ) * radius);
- var pointy = Math.floor(Math.sin( theta ) * radius );
-
-
-
- $(this).css('margin-left', pointx + x + 'px');
- $(this).css('margin-top', pointy + y + 'px');
- });
-
- }
Understanding The Equation Of An Ellipse
![jshape2](http://addyosmani.com/blog/wp-content/uploads/2010/10/jshape2.jpg)
Now that we’ve covered circles lets move onto a slightly more advanced shape – the ellipse. Ellipses are two dimension-closed curves and one of the many in-the-wild examples of a common ellipse is the oval. The parametric form of an ellipse rotated by an angle k is:
- x = p + a * cos(t) * cos(k) – b * sin(t) * sin(k)
- y = q + b * sin(t) * cos(k) + a * cos(t) * sin(k)
Where a and b are two lengths which describe the curve and p and q define the center/offset of the ellipse relative to the page. Here is my version of the JavaScript code that draws the content of a selector around an Ellipse.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- function drawEllipse(selector, x, y, a, b, angle)
- {
- var steps = $(selector).length;
-
- var i = 0;
- var beta = -angle * (Math.PI / 180);
- var sinbeta = Math.sin(beta);
- var cosbeta = Math.cos(beta);
-
- $(selector).each(function(index)
- {
- i+= (360/steps);
- var alpha = i * (Math.PI / 180) ;
- var sinalpha = Math.sin(alpha);
- var cosalpha = Math.cos(alpha);
- var X = x + (a * cosalpha * cosbeta - b * sinalpha * sinbeta);
- var Y = y + (a * cosalpha * sinbeta + b * sinalpha * cosbeta);
-
- X = Math.floor(X);
- Y = Math.floor(Y);
-
-
-
- $(this).css('margin-top', X + 'px');
- $(this).css('margin-left', Y + 'px');
-
- });
-
- }
Demos Set 1: Positioning Images Around A Circle or Ellipse
For the purposes of this section, I’ve written a very minimalist library called jsShapeLib which you can download or fork on Github here. It’s purpose is to provide you with raw coordinate positions using formulae for both circles and ellipses, such as the one I’ve mentioned above. To use jsShapeLib, all you need to do is call the drawCircle() or drawEllipse() functions with their relevant parameters.
See below for two demos of how you can use jsShapeLib to position items. Note that because we’re using primarily standard JavaScript functions to achieve our coordinate positioning, all of this code is cross-browser compatible out of the box.
jsShapeLib Demo 1: Drawing A Circle Of Images
jsShapeLib Demo 2: Drawing An Ellipse Of Images
jsShapeLib Demo 3: Drawing A Permutated Circle With LightBox Functionality
![oval](http://addyosmani.com/blog/wp-content/uploads/2010/10/ovall.jpg)
I hope to extend jsShapeLib in the future to support much more complex shapes including parallelograms and triangles amongst others.
Part 2 – jQuery Roundrr
Now that we’ve gone through how to plot images around a circle, how about we take this further?. When you move outside of the concept of using a rectangle for your content, you open up a whole new world of user interface possibilities.
How about automatically animating your circle, spinning it or turning it into a content gallery with a central area of focus?. I decided to take jsShapeLib further to provide all of these things in a jQuery plugin. I wanted to make sure that the plugin would be able to offer enough flexibility to do three things:
- Allow users to create interesting interfaces that go beyond just plotting
- Allow users the flexibility to add as many images as they wanted and easily configure the radius of the circle
- Offer a complete events model for interacting with the content circle with configuration options
Looking around I saw that there was already a plugin out there that did a little of what I wanted called RadMenu by Nirvana Tikku, so I decided to take the existing codebase, strip it down a little and then extend it to offer a better events model, more configuration options, autoplay features, ‘pick’ mode (which I will show you soon), keyboard event support and a few other extras.
How Does Roundrr Work?
Much like any other image gallery plugin, Roundrr works with normal lists of images and transforms them accordingly. It supports two modes which you can use depending on what you want to do – for most people the ‘standard’ mode should be fine but if you want to use ‘pick’ mode instead (see below) you can also take advantage of that.
To keep Roundrr compact, it encourages the idea of extending functionality through your front-end code using the events model. What this means is that Roundrr will provide you with the ability to plot, and animate in either direction your wheel/circle of images but more custom interaction features (such as displaying a larger image of a thumbnail in the center of the screen or displaying a tweet relevant to the thumbnail) are done outside of the plugin. This allows it to be relatively customizable.
To demonstrate how to get setup with Roundrr, let’s take a look at the ‘Pick’ demo and how you can recreate it.
How To Get Started With ‘Pick’ Mode
![Roundrr](http://addyosmani.com/blog/wp-content/uploads/2010/10/snipp.jpg)
Step 1: Include Dependancies
- <!--Include jQuery and jQuery roundrr dependancies-->
- < script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"><!-- script-->
- < script type='text/javascript' src='scripts/ui/jQuery.roundrr.js'><!-- script-->
-
- <!--First stylesheet configures Roundrr presentation, second is font-face (optional) -->
- <link href="css/style.css" type="text/css" rel="stylesheet">
- <link href="fonts/stylesheet.css" type="text/css" rel="stylesheet">
Step 2: HTML/CSS
- <div class="container">
- <div id="roundrr_navigation"> </div>
-
-
- <div class="roundrr_center"> </div>
-
-
- <div class="roundrr_speech"> </div>
-
-
- <div id="roundrr_caption"> </div>
-
-
- <div id="roundrr_pick"> </div>
-
-
- <div id="roundrr_container">
- <ul class="list">
- <li class="item">
- <div class="my_class"><img alt="Al Yankovic" src="http://a0.twimg.com/profile_images/246073324/IL2_bigger.jpg"></div>
- </li>
- <li class="item">
- <div class="my_class"><img alt="Taylor Swift" src="http://a0.twimg.com/profile_images/390558464/Photo_190_bigger.jpg"></div>
- </li>
- <li class="item">
- <div class="my_class"><img alt="Dandy Warhols" src="http://a1.twimg.com/profile_images/930308885/ban_bigger.jpg"></div>
- </li>
- <li class="item">
- <div class="my_class"><img alt="Lenny Kravitz" src="http://a0.twimg.com/profile_images/1117384560/image_bigger.jpg"></div>
- </li>
- <li class="item">
- <div class="my_class"><img alt="Katy Perry" src="http://a3.twimg.com/profile_images/1107052519/i_mblue_bigger.jpg"></div>
- </li>
-
- etc. </ul>
- </div>
- </div>
Step 3: JavaScript
![mock](http://addyosmani.com/blog/wp-content/uploads/2010/10/MATHE.jpg)
Here’s the part where we initialize the Roundrr plugin through .roundrrwheel() and supply the configuration options we’d like to use. For further details on how to configure the plugin, please see the documentation section of this post.
- var currentImage = "";
- var interfaceLock = false;
-
- jQuery("#roundrr_container").roundrrwheel(
- {
- mode: 'pick',
- autoplay: false,
- autoplayDirection: 'anticlockwise',
- autoplayDuration: 4000,
- centerClass: 'roundrr_center',
- listClass: 'list',
- itemClass: 'item',
- radius: 220,
- animSpeed:400,
- centerX: 29,
- centerY: 60,
- animationEffect: 1,
- selectEvent: "click",
- centerImageSrc: "images/placeholder2.png",
-
- onSelect: function($selected)
- {
- showLargeImage($selected, 'noanim');
- },
-
- onNextBegin: function($m)
- {
- interfaceLock = true;
- },
-
- onPrevBegin: function($m)
- {
- interfaceLock = true;
- },
-
- onImageFocus: function($item)
- {
- showLargeImage($item, 'none');
- },
-
- onImageBlur: function($item)
- {
- },
-
- onAnimationComplete: function($menu)
- {
- jQuery('.centerImage').attr('src', currentImage);
- jQuery('.centerImage').load(function()
- {
- interfaceLock = false;
- });
- },
- angleOffset: Math.PI,
-
- onShow: function($menuitems)
- {
- $menuitems.each(function(i)
- {
- var $this = jQuery(this);
- $this.delay(i*100).fadeIn(500);
- });
-
- }
-
- });
Additional Functions
Here is an example of additional functions that are called during different events in the plugin setup. Here you’ll see how I call for a larger image to be displayed based on the thumbnail’s URL or how the clockwise/anticlockwise animations are called through next() and prev(). The keyboard events can also be seen below.
- Query("#roundrr_container").roundrrwheel("show");
- $('#next').bind('click', spinMenuRight);
- $('#prev').bind('click', spinMenuLeft);
-
- function showLargeImage($i, mode)
- {
-
- interfaceLock = true;
- var thisImage = $i.find('img');
- var focusedImage = thisImage.attr('src');
- var largerImage = focusedImage.replace('_bigger', '');
- var imageCaption = thisImage.attr('alt');
- var speechBubble = jQuery('.roundrr_speech');
- speechBubble.fadeIn();
-
- currentImage = largerImage;
-
- jQuery('#roundrr_caption').fadeIn();
- jQuery('#roundrr_caption').html(imageCaption);
-
- if(mode=='noanim')
- {
- jQuery('.centerImage').attr('src', currentImage);
- jQuery('.centerImage').load(function()
- {
- interfaceLock = false;
-
- });
- }
-
- }
-
-
- function spinMenuLeft()
- {
- if(!(interfaceLock))
- {
- jQuery("#roundrr_container").roundrrwheel("prev");
- }
- }
-
-
- function spinMenuRight()
- {
- if(!(interfaceLock))
- {
- jQuery("#roundrr_container").roundrrwheel("next");
- }
- }
-
-
- $(window).keydown(function(event)
- {
- var keycode = event.keyCode;
- switch(keycode)
- {
- case 39:
- spinMenuLeft();
- break;
- case 37:
- spinMenuRight();
- break;
- } });
-
- });
Documentation
If you would like to read more about the different configuration options available inRoundrr as well as find out what each of the plugin parameters do, you can find additional documentation on the Github page for the project here.
![standard](http://addyosmani.com/blog/wp-content/uploads/2010/10/stand.jpg)
![lght](http://addyosmani.com/blog/wp-content/uploads/2010/10/lght.jpg)
Demos And Downloads
Demo Set 2 – jQuery Roundrr
Demo 1: ‘Pick’ mode demo
Demo 2: ‘Standard’ demo with more images and LightBox support
Demo 3: Tweets gallery demo
Demo 4: (User requested) An Autoplay demo in the clockwise direction
Thanks
Thats it! I hope you find Roundrr useful. If you liked it, please feel free to share it with your friends and colleagues by clicking the retweet button. Until next time, good luck with your projects!.