< drag to rotate >

loading

Go to angle

Ease360 is a 360° image spin sequencer, designed for a better feel. Utilizing HTML5 canvas and JavaScript, Ease360 offers greater performance and control than other methods. Responsive layouts, retina images, event handling, and smart preloading are all supported. Basic setup only requires 3 parameters, but you can do much more.

Features

  • Physics-based solution for 360 spin
  • Responsive design options
  • Modern HTML5 canvas implementation
  • Multiple callbacks and animation methods
  • Mobile and tablet support
  • Smart loading for optimize file weight

Download

Get it on GitHub

Settings

Option Type Default Description
frames array null Ordered list of string paths for animation frames — required
width int null Pixel width of the provided frames — required
height int null Pixel height of the provided frames — required
framesHighDPI array Retina frames — ordered paths at 2× the dimensions of the frames array.
frameDirection int 1 Set -1 to reverse the frame sequence.
startAngle int Starting angle on initialization. Must match an angle present in the provided frames.
backgroundSize enum "stretch" "stretch" sizes images to fit the element. "cover" renders at the largest size contained within or covering the background area.
backgroundOffsetY float 0 Pixel offset in Y when backgroundSize is "cover".
backgroundOffsetX float 0 Pixel offset in X when backgroundSize is "cover".
preloadSmart boolean false When true, every other frame loads initially (50%), with the remaining frames loading on user interaction. Requires an even-length frames array.
flex object {"w": false} Set {"w": true} when the element uses a percentage-based width.
eventDirection enum "left-right" "left-right" captures horizontal drag/touch. "up-down" captures vertical. "none" disables interaction.
responsive array Set of breakpoints with their own options. breakpoint parameter is required in each entry.
breakpoint int Max-width value where a responsive feature set applies. Must be declared inside the responsive array.

Properties

Property Type Default Description
angle() int Returns the current angle (0–359). getter
progress float 0 Loading progress of the frame set as a 0–1 value. getter
physics.damping float 0.95 Range 0.85–0.98 controls feel from firm to fluid. A value of 1.0 creates a continuous spin. setter-getter

Methods / Events

Method Parameters Default Description
angle(angle) int Sets angle position when an argument is provided. setter
angleTo(angle, time) int, float (optional) 0, 1.0 Animates to the specified angle. Duration defaults to 1s.
angleStep(angle) int 0 Adjusts angle relative to current position — positive or negative offset.
spinOver(speed) float 1.0 Continuous playthrough of the frame set. Speed can be positive or negative. Intended for hover/rollover effects.
spinOut() none Cancels spinOver().
changeFrame() array, array (required if High-DPI was set) Replaces the current frame set. If initialized with framesHighDPI, a second High-DPI array is required.

Callbacks

Callback Description
progressUpdate Triggered when the progress property changes.
angleUpdate Triggered when the angle property changes.
responsiveUpdate Triggered when the active responsive breakpoint changes.
stateUpdate Triggered on engine status change. Values: "init", "start", "active", "stop".

Example

Below is the initialization code used for the Genesis G90 demo above.

"use strict";

var myEase360;

var G90lg = [], G90Retinalg = [];
var G90md = [], G90Retinamd = [];
var G90sm = [], G90Retinasm = [];
var G90xs = [], G90Retinaxs = [];

var pathG90lg = "./examples/images/G90_LondonGray_lg-360/";
var pathG90md = "./examples/images/G90_LondonGray_md-360/";
var pathG90sm = "./examples/images/G90_LondonGray_sm-360/";
var pathG90xs = "./examples/images/G90_LondonGray_xs-360/";

for (var i = 0; i < 36; i++) {
    G90lg.push(pathG90lg + "G90_LondonGray_lg_" + (i * 10) + ".jpg");
    G90Retinalg.push(pathG90lg + "G90_LondonGray_lg@2x_" + (i * 10) + ".jpg");

    G90md.push(pathG90md + "G90_LondonGray_md_" + (i * 10) + ".jpg");
    G90Retinamd.push(pathG90md + "G90_LondonGray_md@2x_" + (i * 10) + ".jpg");

    G90sm.push(pathG90sm + "G90_LondonGray_sm_" + (i * 10) + ".jpg");
    G90Retinasm.push(pathG90sm + "G90_LondonGray_sm@2x_" + (i * 10) + ".jpg");

    G90xs.push(pathG90xs + "G90_LondonGray_xs_" + (i * 10) + ".jpg");
    G90Retinaxs.push(pathG90xs + "G90_LondonGray_xs@2x_" + (i * 10) + ".jpg");
}

$(function () {
    myEase360 = $('#myEase360').ease360({
        frames: G90lg,              // required
        framesHighDPI: G90Retinalg,
        frameDirection: -1,         // sequence is reversed
        width: 1442,                // required
        height: 950,                // required
        backgroundSize: "cover",
        preloadSmart: true,
        flex: { "w": true },
        responsive: [
            {
                breakpoint: 962,
                frames: G90md,
                framesHighDPI: G90Retinamd,
                width: 962,
                height: 634,
                flex: { "w": true }
            },
            {
                breakpoint: 660,
                frames: G90sm,
                framesHighDPI: G90Retinasm,
                width: 668,
                height: 440,
                flex: { "w": true }
            },
            {
                breakpoint: 340,
                frames: G90xs,
                framesHighDPI: G90Retinaxs,
                width: 342,
                height: 225,
                flex: { "w": true }
            }
        ],
        angleUpdate:    function () { myAngleUpdate(); },
        progressUpdate: function () { myProgress(); },
        responsiveUpdate: function () { myResponsiveUpdate(); }
    });

    myEase360.physics.damping = 0.94;
});

var $instructions = $('.instructions');

function myAngleUpdate() {
    if (myEase360 == null) return;
    if ($instructions.hasClass('opacity1')) $instructions.removeClass('opacity1');
}

function myProgress() {
    var multiplier = myEase360.preloadSmart ? 2 : 1;
    var percent = Math.floor(myEase360.progress * 100) * multiplier;
    $('.loading h4').html(percent + "%");

    if (percent === 100) {
        $('#myEase360 canvas').addClass('opacity1');
        $('.loading').addClass('opacity0').delay(2000).queue(function () {
            $(this).css({ "z-index": 0 });
            $('#ease360Layout h3.instructions').addClass('opacity1');
            $('.loading h4').html("loading");
            $(this).dequeue();
        });
    }
}

function myResponsiveUpdate() {
    $('.loading').removeClass('opacity0');
    $('#myEase360 canvas').removeClass('opacity1');
    $(".loading").css({ "z-index": 1000 });
}

function goto(angle) {
    myEase360.angleTo(angle, 0.75); // time is optional, defaults to 1s
}