Categories
Plugins

jquery.circular

jquery.circular
— No! Not yet another fraking carousel library!
— Here, grab that circular cookie.
— Oh? All right then.

jquery.circular is a carousel backend library, targeted at front-end developers.
If you are:

looking for a fully customizable, KISS carousel library
tired of tweaking (as in, re-writing) conceptually closed jQuery libraries
unwilling to waste time implementing your own carousel backend, though

then, this may be of some interest to you.
Default settings

4s display per slide
1s fade-in/out transition between slides
starts on first slide (id == 0)

All of this is overridable. It supports defining custom transitions,
provides hooks to interfer with the carousel during its lifecycle,
exposes clean public and internal APIs.
Don’t fancying the default fade-in/out effect? Want something more
edge-casy? Like, say…

sliding horizontally movie trailers’ slides
while altering opacity
while allowing to fade to a video, fullscreen, upon clicking a slide
not forgetting to pause the carousel until it’s resumed (closed the video)
while monitoring in real-time some trailer API so as to be able to feed the carousel in with the most recent additions

It’s straightforward: just wire things up, leveraging circular’s backbone (its API, hooks and events).
Usage
$(‘.wannabe-carousel’).circular()with .wannabe-carousel, a container for some slides and slide’s controls.
It could be of the following shape:
%div.wannabe-carousel  %ul.slides    %li.slide(data-id: 0)      -# first slide’s content    %li.slide(data-id: 1)      -# second slide’s content  %ul.controls    %li.control(data-id: 0)      -# first slide’s control    %li.control(data-id: 1)      -# second slide’s controlIf the carousel’s structure changes during its lifecycle (adding/removing
slides…), one must re-create the circular instance. This may change in
the future (adding/removing slides and controls).
Settings
aSlide
Defines what maps to a slide. It is a jQuery selector resolved
within the container, so actually it could be anything within your ‘body’ if
you’d like to.
It is expected to be associated a data-attribute, data-id,
with a unique slide id within the slides set.
Default: ‘.slides .slide’
aControl
Defines what maps to a slide’s control. Same as for aSlide.
It is expected to be associated a data-attribute data-id matching one of the
id provided by a slide from the matching slides set.
Default: ‘.controls .control’
transitionDelay
In milliseconds, the duration of the transition.
It will be passed as delay to any custom animation function.
Default: 1000
displayDuration
In milliseconds, the duration of display.
Default: 4000
directJump
Whether to skip animations when jumping to a slide using a control.
Actually, this sets the delay parameter available to animations callbacks to
the value 0. Callbacks are free to implement any custom logic in this case.
Default: false
pauseOnHover
Whether to pause the carousel when hovered.
It binds to the container. To implement custom behavior, bind to whatever you’d
like using the pause() and resume() functions from the API.
Default: false
startingPoint
Defines which data-id to begin with.
Default: 0
autoStart
Whether to start running the carousel when initialized.
Default: true
beforeStart
A hook for you to interfer with the carousel before it gets started.
Called at initialization time, just before start() (that is, will be called
even if autoStart is false).
Arguments:

currentSlide: the current slide’s descriptor (see below)
$slides: jQuery selector for the slides set

Default: empty hook
effects: in|out
Implement custom animations for the transitions.
A transition between two slides occurs in a fixed fashion (at least for the
time being): the current slide “moves away”, then the next slide “comes in”.
In the default implementation, those two events are schedulded so that the
slide that “comes in” waits for the slide that “moves away” to effectively
vanish, in a fade out/fade in animation. The scheduling is enforced by
promises.
One can override the effects for the “moves away” (effects.out) and “comes
in” (effects.in) animations, as long as the implementations are exposed
wrapped in deferrables exposing a promise() accessor (in the same way that
jQuery’s Deferred API is designed).
Implementations must be functions returning functions. They receive
delay, the transition delay from the settings, as their sole argument,
and the inner functions are to be executed in the context of the moving
slide (that is, the “current slide” while it’s moving away, and the “next
slide” while it’s coming in).
Promises allow for custom scheduling. The general idea is that
effects.in will be called once effects.out is resolved. Some events are
fired when promises are resolved as well. This is flexible enough to allow
basically any scheduling, such as concurrent animations, delayed
animations, or even reversed animations with a little more work.
For instance, to provide horizontal sliding/fading animations, using a
Backbone View and some CoffeeScript just because we can (and for
a greater expressiveness/length ratio):
class Carousel extends Backbone.View  render: ->    @$el.circular      beforeStart: @prepare      effects:        out: @slideOut        in: @slideIn    @      # Prepare the carousel for our custom transition effect, that is,   # horizontal sliding where order does not matter.   prepare: (currentSlide, $slides) =>    w = currentSlide.slide.width()    h = currentSlide.slide.height()    # Set dimensions of the carousel’s container, for slides will be     # in absolute position.     @$el.css      height: “#{h}px”      overflow: ‘hidden’    # Move all slides to the right, but the first one.     $slides.css _({position: ‘absolute’}).extend(@pos(w))    currentSlide.slide.css @pos(0)    $slides.show()      # A slide must go away. It’s currently visible, let’s slide it horizontally   # to the left, then reset its position to the right.   #   # Note: the surrounding deferred allows to decouple the sliding effect from   # the transition lifecycle. In our custom transition effect, we want the   # next slide to slide in *right away*, not until after the current slide has   # slided out completely.   slideOut: (delay) =>    carousel = @    processing = $.Deferred()    ->      w = $(@).width()      cssStep1 = _(carousel.pos(w, true)).extend({opacity: 0.2})      cssStep2 = _(carousel.pos(w)).extend({opacity: 1})      $(@).animate(cssStep1, delay)          .promise().done -> $(@).css(cssStep2)      processing.resolve()      processing   # A slide must be displayed. It’s currently hidden to the right, let’s slide   # it horizontally to the original position.   slideIn: (delay) =>    carousel = @    ->      $(@).animate(carousel.pos(0), delay)   # Where to place a slide, using left/right offsets.   # A “lefty” slide is to be positionned on the left side of the   # carousel’s container, a “righty” one (default case)… on   # the right side.   pos: (offset, lefty = false) ->    if offset == 0      left: 0      right: 0    else if lefty      left: “-#{offset}px”      right: “#{offset}px”    else      left: “#{offset}px”      right: “-#{offset}px” $ ->  carousel = new Carousel    el: $(‘.wannabe-carousel’).get(0)Hopefully this cumbersome example makes it more obvious how to use
circular’s API to implement custom behavior, while relying on its
core loop implementation to orchestrate the carousel’s lifecycle.
Events
jquery.circular provides a few events you can bind to. Most of them return
slide objects. Those “slides” are actually returned as slides descriptors
(not DOM nodes per se): those are objects of the following shape (also see
the current() method of the API described in the next section):
{  id: Integer,                      // current slide’s id  slide: jQuery.fn.jQuery.init[1],  // a jQuery matcher for the current slide  control: jQuery.fn.jQuery.init[1] // a jQuery matcher for the current slide’s control}All callbacks take an optional last argument, which is the jQuery matcher you
are binding to ($(this), that is). This may come in handy if you change the
callback’s scope, for whatever reason. For the sake of simplicity, this last
argument is not shown in the code examples below.
circular:init
Triggered when the carousel starts.
$(‘.wannabe-carousel’).on(‘circular:init’, function() {  // sit and watch})circular:jumped
Triggered by the default implementation of jumpTo() (see API below).
$(‘.wannabe-carousel’).on(‘circular:jumped’, function(event, newSlide, prevSlide) {  // newSlide is the newly active slide  // prevSlide is the former active slide});circular:selected
Triggered when a slide has been selected, either when a automated transition
occurs or when a manual selection was performed/triggered.
This event fires in between the transition’s animation (typically, before the
selected slide “fades” in, and after the previously active slide has “fade”
out).
$(‘.wannabe-carousel’).on(‘circular:selected’, function(event, slide) {  // slide is the selected slide});circular:fading
Triggered when the active slide is about to become inactive and replaced by
another slide.
$(‘.wannabe-carousel’).on(‘circular:fading’, function(event, prevSlide, nextSlide) {  // prevSlide is the “fading” slide  // nextSlide is the slide about to become the selected one});circular:faded:out
Triggered when a slide has completed its animation of “fading” out.
$(‘.wannabe-carousel’).on(‘circular:faded’, function(event, newSlide, prevSlide) {  // newSlide is the new active, visible slide  // prevSlide is the former active slide});circular:faded:in
Triggered when a slide has been selected and has been made visible, after the
transition’s animation.
$(‘.wannabe-carousel’).on(‘circular:faded’, function(event, newSlide, prevSlide) {  // newSlide is the new active, visible slide  // prevSlide is the former active slide});circular:toSelf
Triggered when an attempt to transitioning to the currently active slide has
been made.
The transition is invalid (nothing happpens), but this special event is fired
to notify about the attempt.
$(‘.wannabe-carousel’).on(‘circular:toSelf’, function(event, slide) {  // slide is the selected slide});circular:paused
Triggered when the carousel has been paused by calling pause().
$(‘.wannabe-carousel’).on(‘circular:paused’, function(event, currentSlide) {  // the carousel was paused on currentSlide});circular:resumed
Triggered when the carousel has been resumed by calling resume().
$(‘.wannabe-carousel’).on(‘circular:resumed’, function(event, currentSlide) {  // the carousel was resumed, starting from currentSlide});Public API
init()
Inits the carousel. It should not be called more than once, and will actually
be ran automatically when calling .circular() on a jQuery matcher.
slides()
Returns the slides set as a jQuery selector.
$(‘.wannabe-carousel’).circular(‘slides’)controls()
Returns the slides’ controls set as a jQuery selector.
$(‘.wannabe-carousel’).circular(‘controls’)currentSlide()
Returns the current slide’s DOM element.
$(‘.wannabe-carousel’).circular(‘currentSlide’)currentControl()
Returns the DOM element for the current slide’s control.
$(‘.wannabe-carousel’).circular(‘currentControl’)current()
Returns both current slide and current slide’s control DOM elements as an
object, under the slide and control properties respectively.
$(‘.wannabe-carousel’).circular(‘current’)pause()
Pauses the carousel, if currently running.
$(‘.wannabe-carousel’).circular(‘pause’)resume()
Resumes the carousel, if not currently running.
$(‘.wannabe-carousel’).circular(‘resume’)jumpTo

arguments: event[, id]

This is an event handler implementing the business logic involved when jumping
to a specific slide. By default, it relies on a default implementation that can
be overriden, although it will probably just be fine in most cases.
Bind events to the jumpTo handler to add custom interactions support. It
expects the DOM element you bind to to provide an id data-attribute matching
the slide’s id you want to jump to, but in case this is not possible, an
explicit id can be passed as the second argument.
This callback will not resume the carousel if it has been paused.
// Why not enabling transitioning to the fourth slide by hovering (default: clicking) its control?$(‘.slide-control[data-id=”3″]’).on(‘hover’, $(‘.wannabe-carousel’).circular(‘jumpTo’)) // let’s say we are able to pick a random number among the slides indexes, and// are willing to crazy-jump to it each time a div is clicked:$(‘body’).on(‘click’, ‘div’, function() {  id = getRandomSlideId()  $(‘.wannabe-carousel’).circular(‘jumpTo’, id);})isAlive()
Checks whether the carousel booted and is alive and well.
$(‘.wannabe-carousel’).circular(‘isAlive’)isRunning()
Checks whether the carousel’s internal loop is running.
$(‘.wannabe-carousel’).circular(‘isRunning’)About this plugin
What it is trying not to do

Make unfair assumptions about your DOM tree
Be overall complicated
Provide styles, pictures…

What it is trying to do

KISS
Simple code so that one can hack on
Use a convention over configuration approach, but remain fully tweakable
Modern patterns (proper jQuery Plugin’s API,
Deferred-based architecture, maybe generator-based at some point…)

On the roadmap

Allow the slides and the controls to be anywhere in the DOM (fully
data-* based), removing any unfair assumption about your DOM tree
Some more events (started, stopped, maybe paused/resumed)
Some hooks (beforeStart, beforeStop, things like that)
A demo page with examples and a nice design!

When this is implemented, release 0.1.0 and start using SemVer.
See TODO.md for other ideas.
License
MIT (see circular.coffee for details and credits/authorship).

GitHub Repo