360°旋转图片

  • Post author:
  • Post category:其他


今天实现一个触摸屏,可以360°来回滑动的图片效果放在vue项目里面!

网上有很多方法,这里采用其中之一,希望对你有帮助~

按步骤引入:

  1. 在index.html 引入 jq 和一个封装好的js。

    封装好的js如下
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
      (factory((global.SpriteSpin = {})));
}(this, (function (exports) { 'use strict';

  /**
   * @internal
   */
  var Api = /** @class */ (function () {
    function Api(data) {
      this.data = data;
    }
    return Api;
  }());
  /**
   * Adds methods to the SpriteSpin api
   *
   * @public
   */
  function extendApi(methods) {
    var api = Api.prototype;
    for (var key in methods) {
      if (methods.hasOwnProperty(key)) {
        if (api[key]) {
          throw new Error('API method is already defined: ' + key);
        }
        else {
          api[key] = methods[key];
        }
      }
    }
    return api;
  }

  var $$1 = window.jQuery || window.$;

  function getCursorPosition(event) {
    var touches = event.touches;
    var source = event;
    // jQuery Event normalization does not preserve the 'event.touches'
    // try to grab touches from the original event
    if (event.touches === undefined && event.originalEvent !== undefined) {
      touches = event.originalEvent.touches;
    }
    // get current touch or mouse position
    if (touches !== undefined && touches.length > 0) {
      source = touches[0];
    }
    return {
      x: source.clientX || 0,
      y: source.clientY || 0
    };
  }

  var canvas;
  var context;
  function detectionContext() {
    if (context) {
      return context;
    }
    if (!canvas) {
      canvas = document.createElement('canvas');
    }
    if (!canvas || !canvas.getContext) {
      return null;
    }
    context = canvas.getContext('2d');
    return context;
  }
  /**
   * Idea taken from https://github.com/stomita/ios-imagefile-megapixel
   * Detects whether the image has been sub sampled by the browser and does not have its original dimensions.
   * This method unfortunately does not work for images that have transparent background.
   */
  function detectSubsampling(img, width, height) {
    if (!detectionContext()) {
      return false;
    }
    // sub sampling happens on images above 1 megapixel
    if (width * height <= 1024 * 1024) {
      return false;
    }
    // set canvas to 1x1 pixel size and fill it with magenta color
    canvas.width = canvas.height = 1;
    context.fillStyle = '#FF00FF';
    context.fillRect(0, 0, 1, 1);
    // render the image with a negative offset to the left so that it would
    // fill the canvas pixel with the top right pixel of the image.
    context.drawImage(img, -width + 1, 0);
    // check color value to confirm image is covering edge pixel or not.
    // if color still magenta, the image is assumed to be sub sampled.
    try {
      var dat = context.getImageData(0, 0, 1, 1).data;
      return (dat[0] === 255) && (dat[1] === 0) && (dat[2] === 255);
    }
    catch (err) {
      // avoids cross origin exception for chrome when code runs without a server
      return false;
    }
  }

  /**
   *
   */
  function getOuterSize(data) {
    var width = Math.floor(data.width || data.frameWidth || data.target.innerWidth());
    var height = Math.floor(data.height || data.frameHeight || data.target.innerHeight());
    return {
      aspect: width / height,
      height: height,
      width: width
    };
  }
  function getComputedSize(data) {
    var size = getOuterSize(data);
    if (typeof window.getComputedStyle !== 'function') {
      return size;
    }
    var style = window.getComputedStyle(data.target[0]);
    if (!style.width) {
      return size;
    }
    size.width = Math.floor(Number(style.width.replace('px', '')));
    size.height = Math.floor(size.width / size.aspect);
    return size;
  }
  /**
   *
   */
  function getInnerSize(data) {
    var width = Math.floor(data.frameWidth || data.width || data.target.innerWidth());
    var height = Math.floor(data.frameHeight || data.height || data.target.innerHeight());
    return {
      aspect: width / height,
      height: height,
      width: width
    };
  }
  /**
   *
   */
  function getInnerLayout(mode, inner, outer) {
    // get mode
    var isFit = mode === 'fit';
    var isFill = mode === 'fill';
    var isMatch = mode === 'stretch';
    // resulting layout
    var layout = {
      width: '100%',
      height: '100%',
      top: 0,
      left: 0,
      bottom: 0,
      right: 0,
      position: 'absolute',
      overflow: 'hidden'
    };
    // no calculation here
    if (!mode || isMatch) {
      return layout;
    }
    // get size and aspect
    var aspectIsGreater = inner.aspect >= outer.aspect;
    // mode == original
    var width = inner.width;
    var height = inner.height;
    // keep aspect ratio but fit/fill into container
    if (isFit && aspectIsGreater || isFill && !aspectIsGreater) {
      width = outer.width;
      height = outer.width / inner.aspect;
    }
    if (isFill && aspectIsGreater || isFit && !aspectIsGreater) {
      height = outer.height;
      width = outer.height * inner.aspect;
    }
    // floor the numbers
    width = Math.floor(width);
    height = Math.floor(height);
    // position in center
    layout.width = width;
    layout.height = height;
    layout.top = Math.floor((outer.height - height) / 2);
    layout.left = Math.floor((outer.width - width) / 2);
    layout.right = layout.left;
    layout.bottom = layout.top;
    return layout;
  }

  var img;
  /**
   * gets the original width and height of an image element
   */
  function naturalSize(image) {
    // for browsers that support naturalWidth and naturalHeight properties
    if (image.naturalWidth) {
      return {
        height: image.naturalHeight,
        width: image.naturalWidth
      };
    }
    // browsers that do not support naturalWidth and naturalHeight properties have to fall back to the width and
    // height properties. However, the image might have a css style applied so width and height would return the
    // css size. To avoid thet create a new Image object that is free of css rules and grab width and height
    // properties
    //
    // assume that the src has already been downloaded, so no onload callback is needed.
    img = img || new Image();
    img.src = image.src;
    return {
      height: img.height,
      width: img.width
    };
  }

  /**
   * Measures the image frames that are used in the given data object
   */
  function measure(images, options) {
    if (images.length === 1) {
      return [measureSheet(images[0], options)];
    }
    else if (options.framesX && options.framesY) {
      return measureMutipleSheets(images, options);
    }
    else {
      return measureFrames(images, options);
    }
  }
  function measureSheet(image, options) {
    var result = { id: 0, sprites: [] };
    measureImage(image, options, result);
    var frames = options.frames;
    var framesX = Number(options.framesX) || frames;
    var framesY = Math.ceil(frames / framesX);
    var frameWidth = Math.floor(result.width / framesX);
    var frameHeight = Math.floor(result.height / framesY);
    var divisor = result.isSubsampled ? 2 : 1;
    for (var i = 0; i < frames; i++) {
      var x = (i % framesX) * frameWidth;
      var y = Math.floor(i / framesX) * frameHeight;
      result.sprites.push({
        id: i,
        x: x, y: y,
        width: frameWidth,
        height: frameHeight,
        sampledX: x / divisor,
        sampledY: y / divisor,
        sampledWidth: frameWidth / divisor,
        sampledHeight: frameHeight / divisor
      });
    }
    return result;
  }
  function measureFrames(images, options) {
    var result = [];
    for (var id = 0; id < images.length; id++) {
      // TODO: optimize
      // dont measure images with same size twice
      var sheet = measureSheet(images[id], { frames: 1, framesX: 1, detectSubsampling: options.detectSubsampling });
      sheet.id = id;
      result.push(sheet);
    }
    return result;
  }
  function measureMutipleSheets(images, options) {
    var result = [];
    for (var id = 0; id < images.length; id++) {
      // TODO: optimize
      // dont measure images with same size twice
      var sheet = measureSheet(images[id], {
        frames: undefined,
        framesX: options.framesX,
        framesY: options.framesY,
        detectSubsampling: options.detectSubsampling
      });
      sheet.id = id;
      result.push(sheet);
    }
    return result;
  }
  function measureImage(image, options, result) {
    var size = naturalSize(image);
    result.isSubsampled = options.detectSubsampling && detectSubsampling(image, size.width, size.height);
    result.width = size.width;
    result.height = size.height;
    result.sampledWidth = size.width / (result.isSubsampled ? 2 : 1);
    result.sampledHeight = size.height / (result.isSubsampled ? 2 : 1);
    return result;
  }
  function findSpecs(metrics, frames, frame, lane) {
    var spriteId = lane * frames + frame;
    var sheetId = 0;
    var sprite = null;
    var sheet = null;
    while (true) {
      sheet = metrics[sheetId];
      if (!sheet) {
        break;
      }
      if (spriteId >= sheet.sprites.length) {
        spriteId -= sheet.sprites.length;
        sheetId++;
        continue;
      }
      sprite = sheet.sprites[spriteId];
      break;
    }
    return { sprite: sprite, sheet: sheet };
  }

  function indexOf(element, arr) {
    for (var i = 0; i < arr.length; i++) {
      if (arr[i] === element) {
        return i;
      }
    }
  }
  function noop() {
    //
  }
  function preload(opts) {
    var src;
    var input = opts.source;
    src = typeof input === 'string' ? [input] : input;
    // const src: string[] =  ? [opts.source] : opts.source
    var images = [];
    var targetCount = (opts.preloadCount || src.length);
    var onInitiated = opts.initiated || noop;
    var onProgress = opts.progress || noop;
    var onComplete = opts.complete || noop;
    var count = 0;
    var completed = false;
    var firstLoaded = false;
    var tick = function () {
      count += 1;
      onProgress({
        index: indexOf(this, images),
        loaded: count,
        total: src.length,
        percent: Math.round((count / src.length) * 100)
      });
      firstLoaded = firstLoaded || (this === images[0]);
      if (firstLoaded && !completed && (count >= targetCount)) {
        completed = true;
        onComplete(images);
      }
    };
    for (var _i = 0, src_1 = src; _i < src_1.length; _i++) {
      var url = src_1[_i];
      var img = new Image();
      // push result
      images.push(img);
      // bind logic, dont care about abort/errors
      img.onload = img.onabort = img.onerror = tick;
      // begin load
      img.src = url;
    }
    onInitiated(images);
  }

  function padNumber(num, length, pad) {
    var result = String(num);
    while (result.length < length) {
      result = String(pad) + result;
    }
    return result;
  }
  /**
   * Generates an array of source strings
   *
   * @remarks
   * Takes a template string and generates an array of strings by interpolating {lane} and {frame} placeholders.
   *
   * ```
   * sourceArray('http://example.com/image_{frame}.jpg, { frame: [1, 3], digits: 2 })
   * // gives:
   * // [ 'http://example.com/image_01.jpg', 'http://example.com/image_02.jpg', 'http://example.com/image_03.jpg' ]
   *
   * sourceArray('http://example.com/image_FRAME.jpg, { frame: [1, 3], digits: 2, framePlacer: 'FRAME' })
   * // gives:
   * // [ 'http://example.com/image_01.jpg', 'http://example.com/image_02.jpg', 'http://example.com/image_03.jpg' ]
   * ```
   *
   * @param template - The template string
   * @param opts - Interpolation options
   *
   * @public
   */
  function sourceArray(template, opts) {
    var digits = opts.digits || 2;
    var lPlacer = opts.lanePlacer || '{lane}';
    var fPlacer = opts.framePlacer || '{frame}';
    var fStart = 0;
    var fEnd = 0;
    if (opts.frame) {
      fStart = opts.frame[0];
      fEnd = opts.frame[1];
    }
    var lStart = 0;
    var lEnd = 0;
    if (opts.lane) {
      lStart = opts.lane[0];
      lEnd = opts.lane[1];
    }
    var result = [];
    for (var lane = lStart; lane <= lEnd; lane += 1) {
      for (var frame = fStart; frame <= fEnd; frame += 1) {
        result.push(template
          .replace(lPlacer, padNumber(lane, digits, '0'))
          .replace(fPlacer, padNumber(frame, digits, '0')));
      }
    }
    return result;
  }

  /**
   * The namespace that is used to bind functions to DOM events and store the data object
   */
  var namespace = 'spritespin';
  /**
   * Event names that are recognized by SpriteSpin. A module can implement any of these and they will be bound
   * to the target element on which the plugin is called.
   */
  var eventNames = [
    'mousedown',
    'mousemove',
    'mouseup',
    'mouseenter',
    'mouseover',
    'mouseleave',
    'mousewheel',
    'wheel',
    'click',
    'dblclick',
    'touchstart',
    'touchmove',
    'touchend',
    'touchcancel',
    'selectstart',
    'gesturestart',
    'gesturechange',
    'gestureend'
  ];
  /**
   *
   */
  var callbackNames = [
    'onInit',
    'onProgress',
    'onLoad',
    'onFrameChanged',
    'onFrame',
    'onDraw',
    'onComplete',
    'onDestroy'
  ];
  /**
   * Names of events for that the default behavior should be prevented.
   */
  var eventsToPrevent = [
    'dragstart'
  ];
  /**
   * Default set of SpriteSpin options. This also represents the majority of data attributes that are used during the
   * lifetime of a SpriteSpin instance. The data is stored inside the target DOM element on which the plugin is called.
   */
  var defaults = {
    source: undefined,
    width: undefined,
    height: undefined,
    frames: undefined,
    framesX: undefined,
    lanes: 1,
    sizeMode: undefined,
    renderer: 'canvas',
    lane: 0,
    frame: 0,
    frameTime: 40,
    animate: true,
    retainAnimate: false,
    reverse: false,
    loop: true,
    stopFrame: 0,
    wrap: true,
    wrapLane: false,
    sense: 1,
    senseLane: undefined,
    orientation: 'horizontal',
    detectSubsampling: true,
    preloadCount: undefined,
    touchScrollTimer: [200, 1500],
    responsive: undefined,
    plugins: undefined
  };

  function noop$1() {
    // noop
  }
  function wrapConsole(type) {
    return console && console[type] ? function () {
      var args = [];
      for (var _i = 0; _i < arguments.length; _i++) {
        args[_i] = arguments[_i];
      }
      return console.log.apply(console, args);
    } : noop$1;
  }
  var log = wrapConsole('log');
  var warn = wrapConsole('warn');
  var error = wrapConsole('error');
  function toArray(value) {
    return Array.isArray(value) ? value : [value];
  }
  /**
   * clamps the given value by the given min and max values
   */
  function clamp(value, min, max) {
    return (value > max ? max : (value < min ? min : value));
  }
  /**
   *
   */
  function wrap(value, min, max, size) {
    while (value > max) {
      value -= size;
    }
    while (value < min) {
      value += size;
    }
    return value;
  }
  /**
   * prevents default action on the given event
   */
  function prevent(e) {
    e.preventDefault();
    return false;
  }
  /**
   * Binds on the given target and event the given function.
   * The SpriteSpin namespace is attached to the event name
   */
  function bind(target, event, func) {
    if (func) {
      target.bind(event + '.' + namespace, function (e) {
        func.apply(target, [e, target.spritespin('data')]);
      });
    }
  }
  /**
   * Unbinds all SpriteSpin events from given target element
   */
  function unbind(target) {
    target.unbind('.' + namespace);
  }
  /**
   * Checks if given object is a function
   */
  function isFunction(fn) {
    return typeof fn === 'function';
  }
  function pixelRatio(context) {
    var devicePixelRatio = window.devicePixelRatio || 1;
    var backingStoreRatio = context.webkitBackingStorePixelRatio ||
      context.mozBackingStorePixelRatio ||
      context.msBackingStorePixelRatio ||
      context.oBackingStorePixelRatio ||
      context.backingStorePixelRatio || 1;
    return devicePixelRatio / backingStoreRatio;
  }



  var _Utils = Object.freeze({
    $: $$1,
    getCursorPosition: getCursorPosition,
    detectSubsampling: detectSubsampling,
    getOuterSize: getOuterSize,
    getComputedSize: getComputedSize,
    getInnerSize: getInnerSize,
    getInnerLayout: getInnerLayout,
    measure: measure,
    findSpecs: findSpecs,
    naturalSize: naturalSize,
    preload: preload,
    sourceArray: sourceArray,
    noop: noop$1,
    log: log,
    warn: warn,
    error: error,
    toArray: toArray,
    clamp: clamp,
    wrap: wrap,
    prevent: prevent,
    bind: bind,
    unbind: unbind,
    isFunction: isFunction,
    pixelRatio: pixelRatio
  });

  /**
   * Applies css attributes to layout the SpriteSpin containers.
   *
   * @internal
   */
  function applyLayout(data) {
    // disable selection
    data.target
      .attr('unselectable', 'on')
      .css({
        width: '',
        height: '',
        '-ms-user-select': 'none',
        '-moz-user-select': 'none',
        '-khtml-user-select': 'none',
        '-webkit-user-select': 'none',
        'user-select': 'none'
      });
    var size = data.responsive ? getComputedSize(data) : getOuterSize(data);
    var layout = getInnerLayout(data.sizeMode, getInnerSize(data), size);
    // apply layout on target
    data.target.css({
      width: size.width,
      height: size.height,
      position: 'relative',
      overflow: 'hidden'
    });
    // apply layout on stage
    data.stage
      .css(layout)
      .hide();
    if (!data.canvas) {
      return;
    }
    // apply layout on canvas
    data.canvas.css(layout).hide();
    // apply pixel ratio on canvas
    data.canvasRatio = data.canvasRatio || pixelRatio(data.context);
    if (typeof layout.width === 'number' && typeof layout.height === 'number') {
      data.canvas[0].width = (layout.width * data.canvasRatio) || size.width;
      data.canvas[0].height = (layout.height * data.canvasRatio) || size.height;
    }
    else {
      data.canvas[0].width = (size.width * data.canvasRatio);
      data.canvas[0].height = (size.height * data.canvasRatio);
    }
    // width and height must be set before calling scale
    data.context.scale(data.canvasRatio, data.canvasRatio);
  }

  /**
   * Gets a state object by name.
   * @internal
   * @param data - The SpriteSpin instance data
   * @param name - The name of the state object
   */
  function getState(data, name) {
    data.state = data.state || {};
    data.state[name] = data.state[name] || {};
    return data.state[name];
  }
  /**
   * Gets a plugin state object by name.
   *
   * @remarks
   * Plugins should use this method to get or create a state object where they can
   * store any instance variables.
   *
   * @public
   * @param data - The SpriteSpin instance data
   * @param name - The name of the plugin
   */
  function getPluginState(data, name) {
    var state = getState(data, 'plugin');
    state[name] = state[name] || {};
    return state[name];
  }
  /**
   * Checks whether a flag is set. See {@link flag}.
   *
   * @public
   * @param data - The SpriteSpin instance data
   * @param key - The name of the flag
   */
  function is(data, key) {
    return !!getState(data, 'flags')[key];
  }
  /**
   * Sets a flag value. See {@link is}.
   *
   * @public
   * @param data - The SpriteSpin instance data
   * @param key - The name of the flag
   * @param value - The value to set
   */
  function flag(data, key, value) {
    getState(data, 'flags')[key] = !!value;
  }

  /**
   * Gets the playback state
   *
   * @public
   * @param data - The SpriteSpin instance data
   */
  function getPlaybackState(data) {
    return getState(data, 'playback');
  }
  function updateLane(data, lane) {
    data.lane = data.wrapLane
      ? wrap(lane, 0, data.lanes - 1, data.lanes)
      : clamp(lane, 0, data.lanes - 1);
  }
  function updateAnimationFrame(data) {
    data.frame += (data.reverse ? -1 : 1);
    // wrap the frame value to fit in range [0, data.frames)
    data.frame = wrap(data.frame, 0, data.frames - 1, data.frames);
    // stop animation if loop is disabled and the stopFrame is reached
    if (!data.loop && (data.frame === data.stopFrame)) {
      stopAnimation(data);
    }
  }
  function updateInputFrame(data, frame) {
    data.frame = Number(frame);
    data.frame = data.wrap
      ? wrap(data.frame, 0, data.frames - 1, data.frames)
      : clamp(data.frame, 0, data.frames - 1);
  }
  function updateAnimation(data) {
    var state = getPlaybackState(data);
    if (state.handler) {
      updateBefore(data);
      updateAnimationFrame(data);
      updateAfter(data);
    }
  }
  function updateBefore(data) {
    var state = getPlaybackState(data);
    state.lastFrame = data.frame;
    state.lastLane = data.lane;
  }
  function updateAfter(data) {
    var state = getPlaybackState(data);
    if (state.lastFrame !== data.frame || state.lastLane !== data.lane) {
      data.target.trigger('onFrameChanged.' + namespace, data);
    }
    data.target.trigger('onFrame.' + namespace, data);
    data.target.trigger('onDraw.' + namespace, data);
  }
  /**
   * Updates the frame or lane number of the SpriteSpin data.
   *
   * @public
   * @param data - The SpriteSpin instance data
   * @param frame - The frame number to set
   * @param lane - The lane number to set
   */
  function updateFrame(data, frame, lane) {
    updateBefore(data);
    if (frame != null) {
      updateInputFrame(data, frame);
    }
    if (lane != null) {
      updateLane(data, lane);
    }
    updateAfter(data);
  }
  /**
   * Stops the running animation.
   *
   * @public
   * @param data - The SpriteSpin instance data
   */
  function stopAnimation(data) {
    data.animate = false;
    var state = getPlaybackState(data);
    if (state.handler != null) {
      window.clearInterval(state.handler);
      state.handler = null;
    }
  }
  /**
   * Starts animation playback if needed.
   *
   * @remarks
   * Starts animation playback if `animate` property is `true` and the animation is not yet running.
   *
   * @public
   * @param data - The SpriteSpin instance data
   */
  function applyAnimation(data) {
    var state = getPlaybackState(data);
    if (state.handler && (!data.animate || state.frameTime !== data.frameTime)) {
      stopAnimation(data);
    }
    if (data.animate && !state.handler) {
      state.frameTime = data.frameTime;
      state.handler = window.setInterval(function () { return updateAnimation(data); }, state.frameTime);
    }
  }
  /**
   * Starts the animation playback
   *
   * @remarks
   * Starts the animation playback and also sets the `animate` property to `true`
   *
   * @public
   * @param data - The SpriteSpin instance data
   */
  function startAnimation(data) {
    data.animate = true;
    applyAnimation(data);
  }

  var plugins = {};
  /**
   * Registers a plugin.
   *
   * @remarks
   * Use this to add custom Rendering or Updating modules that can be addressed with the 'module' option.
   *
   * @public
   * @param name - The name of the plugin
   * @param plugin - The plugin implementation
   */
  function registerPlugin(name, plugin) {
    if (plugins[name]) {
      error("Plugin name \"" + name + "\" is already taken");
      return;
    }
    plugin = plugin || {};
    plugins[name] = plugin;
    return plugin;
  }
  /**
   * Registers a plugin.
   *
   * @public
   * @deprecated Use {@link registerPlugin} instead
   * @param name - The name of the plugin
   * @param plugin - The plugin implementation
   */
  function registerModule(name, plugin) {
    warn('"registerModule" is deprecated, use "registerPlugin" instead');
    registerPlugin(name, plugin);
  }
  /**
   * Gets an active plugin by name
   *
   * @internal
   * @param name - The name of the plugin
   */
  function getPlugin(name) {
    return plugins[name];
  }
  /**
   * Replaces module names on given SpriteSpin data and replaces them with actual implementations.
   * @internal
   */
  function applyPlugins(data) {
    fixPlugins(data);
    for (var i = 0; i < data.plugins.length; i += 1) {
      var name_1 = data.plugins[i];
      if (typeof name_1 !== 'string') {
        continue;
      }
      var plugin = getPlugin(name_1);
      if (!plugin) {
        error('No plugin found with name ' + name_1);
        continue;
      }
      data.plugins[i] = plugin;
    }
  }
  function fixPlugins(data) {
    // tslint:disable no-string-literal
    if (data['mods']) {
      warn('"mods" option is deprecated, use "plugins" instead');
      data.plugins = data['mods'];
      delete data['mods'];
    }
    if (data['behavior']) {
      warn('"behavior" option is deprecated, use "plugins" instead');
      data.plugins.push(data['behavior']);
      delete data['behavior'];
    }
    if (data['module']) {
      warn('"module" option is deprecated, use "plugins" instead');
      data.plugins.push(data['module']);
      delete data['module'];
    }
  }

  var $$2 = $$1;
  var counter = 0;
  /**
   * Collection of all SpriteSpin instances
   */
  var instances = {};
  function pushInstance(data) {
    counter += 1;
    data.id = String(counter);
    instances[data.id] = data;
  }
  function popInstance(data) {
    delete instances[data.id];
  }
  function eachInstance(cb) {
    for (var id in instances) {
      if (instances.hasOwnProperty(id)) {
        cb(instances[id]);
      }
    }
  }
  var lazyinit = function () {
    // replace function with a noop
    // this logic must run only once
    lazyinit = function () { };
    function onEvent(eventName, e) {
      eachInstance(function (data) {
        for (var _i = 0, _a = data.plugins; _i < _a.length; _i++) {
          var module_1 = _a[_i];
          if (typeof module_1[eventName] === 'function') {
            module_1[eventName].apply(data.target, [e, data]);
          }
        }
      });
    }
    function onResize() {
      eachInstance(function (data) {
        if (data.responsive) {
          boot(data);
        }
      });
    }
    var _loop_1 = function (eventName) {
      $$2(window.document).bind(eventName + '.' + namespace, function (e) {
        onEvent('document' + eventName, e);
      });
    };
    for (var _i = 0, eventNames_1 = eventNames; _i < eventNames_1.length; _i++) {
      var eventName = eventNames_1[_i];
      _loop_1(eventName);
    }
    var resizeTimeout = null;
    $$2(window).on('resize', function () {
      window.clearTimeout(resizeTimeout);
      resizeTimeout = window.setTimeout(onResize, 100);
    });
  };
  /**
   * (re)binds all spritespin events on the target element
   *
   * @internal
   */
  function applyEvents(data) {
    var target = data.target;
    // Clear all SpriteSpin events on the target element
    unbind(target);
    // disable all default browser behavior on the following events
    // mainly prevents image drag operation
    for (var _i = 0, eventsToPrevent_1 = eventsToPrevent; _i < eventsToPrevent_1.length; _i++) {
      var eName = eventsToPrevent_1[_i];
      bind(target, eName, prevent);
    }
    // Bind module functions to SpriteSpin events
    for (var _a = 0, _b = data.plugins; _a < _b.length; _a++) {
      var plugin = _b[_a];
      for (var _c = 0, eventNames_2 = eventNames; _c < eventNames_2.length; _c++) {
        var eName = eventNames_2[_c];
        bind(target, eName, plugin[eName]);
      }
      for (var _d = 0, callbackNames_1 = callbackNames; _d < callbackNames_1.length; _d++) {
        var eName = callbackNames_1[_d];
        bind(target, eName, plugin[eName]);
      }
    }
    // bind auto start function to load event.
    bind(target, 'onLoad', function (e, d) {
      applyAnimation(d);
    });
    // bind all user events that have been passed on initialization
    for (var _e = 0, callbackNames_2 = callbackNames; _e < callbackNames_2.length; _e++) {
      var eName = callbackNames_2[_e];
      bind(target, eName, data[eName]);
    }
  }
  function applyMetrics(data) {
    if (!data.images) {
      data.metrics = [];
    }
    data.metrics = measure(data.images, data);
    var spec = findSpecs(data.metrics, data.frames, 0, 0);
    if (spec.sprite) {
      // TODO: try to remove frameWidth/frameHeight
      data.frameWidth = spec.sprite.width;
      data.frameHeight = spec.sprite.height;
    }
  }
  /**
   * Runs the boot process.
   *
   * @remarks
   * (re)initializes plugins, (re)initializes the layout, (re)binds events and loads source images.
   *
   * @internal
   */
  function boot(data) {
    applyPlugins(data);
    applyEvents(data);
    applyLayout(data);
    data.source = toArray(data.source);
    data.loading = true;
    data.target
      .addClass('loading')
      .trigger('onInit.' + namespace, data);
    preload({
      source: data.source,
      preloadCount: data.preloadCount,
      progress: function (progress) {
        data.progress = progress;
        data.target.trigger('onProgress.' + namespace, data);
      },
      complete: function (images) {
        data.images = images;
        data.loading = false;
        data.frames = data.frames || images.length;
        applyMetrics(data);
        applyLayout(data);
        data.stage.show();
        data.target
          .removeClass('loading')
          .trigger('onLoad.' + namespace, data)
          .trigger('onFrame.' + namespace, data)
          .trigger('onDraw.' + namespace, data)
          .trigger('onComplete.' + namespace, data);
      }
    });
  }
  /**
   * Creates a new SpriteSpin instance
   *
   * @public
   */
  function create(options) {
    var _this = this;
    var target = options.target;
    // SpriteSpin is not initialized
    // Create default settings object and extend with given options
    var data = $$2.extend(true, {}, defaults, options);
    // ensure source is set
    data.source = data.source || [];
    // ensure plugins are set
    data.plugins = data.plugins || [
      '360',
      'drag'
    ];
    // if image tags are contained inside this DOM element
    // use these images as the source files
    target.find('img').each(function () {
      if (!Array.isArray(data.source)) {
        data.source = [];
      }
      data.source.push($$2(_this).attr('src'));
    });
    // build inner html
    // <div>
    //   <div class='spritespin-stage'></div>
    //   <canvas class='spritespin-canvas'></canvas>
    // </div>
    target
      .empty()
      .addClass('spritespin-instance')
      .append("<div class='spritespin-stage'></div>");
    // add the canvas element if canvas rendering is enabled and supported
    if (data.renderer === 'canvas') {
      var canvas = document.createElement('canvas');
      if (!!(canvas.getContext && canvas.getContext('2d'))) {
        data.canvas = $$2(canvas).addClass('spritespin-canvas');
        data.context = canvas.getContext('2d');
        target.append(data.canvas);
        target.addClass('with-canvas');
      }
      else {
        // fallback to image rendering mode
        data.renderer = 'image';
      }
    }
    // setup references to DOM elements
    data.target = target;
    data.stage = target.find('.spritespin-stage');
    // store the data
    target.data(namespace, data);
    pushInstance(data);
    return data;
  }
  /**
   * Creates a new SpriteSpin instance, or updates an existing one
   *
   * @public
   */
  function createOrUpdate(options) {
    lazyinit();
    var data = options.target.data(namespace);
    if (!data) {
      data = create(options);
    }
    else {
      $$2.extend(data, options);
    }
    boot(data);
    return data;
  }
  /**
   * Destroys the SpriteSpin instance
   *
   * @remarks
   * - stops running animation
   * - unbinds all events
   * - deletes the data on the target element
   *
   * @public
   */
  function destroy(data) {
    popInstance(data);
    stopAnimation(data);
    data.target
      .trigger('onDestroy', data)
      .html(null)
      .attr('style', null)
      .attr('unselectable', null)
      .removeClass(['spritespin-instance', 'with-canvas']);
    unbind(data.target);
    data.target.removeData(namespace);
  }

  /**
   * Gets the current input state
   *
   * @public
   * @param data - The SpriteSpin instance data
   */
  function getInputState(data) {
    return getState(data, 'input');
  }
  /**
   * Updates the input state using a mous or touch event.
   *
   * @public
   * @param e - The input event
   * @param data - The SpriteSpin instance data
   */
  function updateInput(e, data) {
    var cursor = getCursorPosition(e);
    var state = getInputState(data);
    // cache positions from previous frame
    state.oldX = state.currentX;
    state.oldY = state.currentY;
    state.currentX = cursor.x;
    state.currentY = cursor.y;
    // Fix old position.
    if (state.oldX === undefined || state.oldY === undefined) {
      state.oldX = state.currentX;
      state.oldY = state.currentY;
    }
    // Cache the initial click/touch position and store the frame number at which the click happened.
    // Useful for different behavior implementations. This must be restored when the click/touch is released.
    if (state.startX === undefined || state.startY === undefined) {
      state.startX = state.currentX;
      state.startY = state.currentY;
      state.clickframe = data.frame;
      state.clicklane = data.lane;
    }
    // Calculate the vector from start position to current pointer position.
    state.dX = state.currentX - state.startX;
    state.dY = state.currentY - state.startY;
    // Calculate the vector from last frame position to current pointer position.
    state.ddX = state.currentX - state.oldX;
    state.ddY = state.currentY - state.oldY;
    // Normalize vectors to range [-1:+1]
    state.ndX = state.dX / data.target.innerWidth();
    state.ndY = state.dY / data.target.innerHeight();
    state.nddX = state.ddX / data.target.innerWidth();
    state.nddY = state.ddY / data.target.innerHeight();
  }
  /**
   * Resets the input state.
   *
   * @public
   */
  function resetInput(data) {
    var input = getInputState(data);
    input.startX = input.startY = undefined;
    input.currentX = input.currentY = undefined;
    input.oldX = input.oldY = undefined;
    input.dX = input.dY = 0;
    input.ddX = input.ddY = 0;
    input.ndX = input.ndY = 0;
    input.nddX = input.nddY = 0;
  }

  function extension(option, value) {
    var $target = $$1(this);
    if (option === 'data') {
      return $target.data(namespace);
    }
    if (option === 'api') {
      var data = $target.data(namespace);
      data.api = data.api || new Api(data);
      return data.api;
    }
    if (option === 'destroy') {
      return $target.each(function () {
        var data = $target.data(namespace);
        if (data) {
          destroy(data);
        }
      });
    }
    if (arguments.length === 2 && typeof option === 'string') {
      option = (_a = {}, _a[option] = value, _a);
    }
    if (typeof option === 'object') {
      return createOrUpdate($$1.extend(true, { target: $target }, option)).target;
    }
    throw new Error('Invalid call to spritespin');
    var _a;
  }
  $$1.fn[namespace] = extension;

// tslint:disable:object-literal-shorthand
// tslint:disable:only-arrow-functions
  extendApi({
    // Gets a value indicating whether the animation is currently running.
    isPlaying: function () {
      return getPlaybackState(this.data).handler != null;
    },
    // Gets a value indicating whether the animation looping is enabled.
    isLooping: function () {
      return this.data.loop;
    },
    // Starts/Stops the animation playback
    toggleAnimation: function () {
      if (this.isPlaying()) {
        this.stopAnimation();
      }
      else {
        this.startAnimation();
      }
    },
    // Stops animation playback
    stopAnimation: function () {
      this.data.animate = false;
      stopAnimation(this.data);
    },
    // Starts animation playback
    startAnimation: function () {
      this.data.animate = true;
      applyAnimation(this.data);
    },
    // Sets a value indicating whether the animation should be looped or not.
    // This might start the animation (if the 'animate' data attribute is set to true)
    loop: function (value) {
      this.data.loop = value;
      applyAnimation(this.data);
      return this;
    },
    // Gets the current frame number
    currentFrame: function () {
      return this.data.frame;
    },
    // Updates SpriteSpin to the specified frame.
    updateFrame: function (frame) {
      updateFrame(this.data, frame);
      return this;
    },
    // Skips the given number of frames
    skipFrames: function (step) {
      var data = this.data;
      updateFrame(data, data.frame + (data.reverse ? -step : +step));
      return this;
    },
    // Updates SpriteSpin so that the next frame is shown
    nextFrame: function () {
      return this.skipFrames(1);
    },
    // Updates SpriteSpin so that the previous frame is shown
    prevFrame: function () {
      return this.skipFrames(-1);
    },
    // Starts the animations that will play until the given frame number is reached
    // options:
    //   force [boolean] starts the animation, even if current frame is the target frame
    //   nearest [boolean] animates to the direction with minimum distance to the target frame
    playTo: function (frame, options) {
      var data = this.data;
      options = options || {};
      if (!options.force && data.frame === frame) {
        return;
      }
      if (options.nearest) {
        // distance to the target frame
        var a = frame - data.frame;
        // distance to last frame and the to target frame
        var b = frame > data.frame ? a - data.frames : a + data.frames;
        // minimum distance
        var c = Math.abs(a) < Math.abs(b) ? a : b;
        data.reverse = c < 0;
      }
      data.animate = true;
      data.loop = false;
      data.stopFrame = frame;
      applyAnimation(data);
      return this;
    }
  });

  function pick(target, names) {
    for (var _i = 0, names_1 = names; _i < names_1.length; _i++) {
      var name_1 = names_1[_i];
      if (target[name_1] || name_1 in target) {
        return name_1;
      }
    }
    return names[0];
  }
  var browser = {
    requestFullscreen: pick(document.documentElement, [
      'requestFullscreen',
      'webkitRequestFullScreen',
      'mozRequestFullScreen',
      'msRequestFullscreen'
    ]),
    exitFullscreen: pick(document, [
      'exitFullscreen',
      'webkitExitFullscreen',
      'webkitCancelFullScreen',
      'mozCancelFullScreen',
      'msExitFullscreen'
    ]),
    fullscreenElement: pick(document, [
      'fullscreenElement',
      'webkitFullscreenElement',
      'webkitCurrentFullScreenElement',
      'mozFullScreenElement',
      'msFullscreenElement'
    ]),
    fullscreenEnabled: pick(document, [
      'fullscreenEnabled',
      'webkitFullscreenEnabled',
      'mozFullScreenEnabled',
      'msFullscreenEnabled'
    ]),
    fullscreenchange: pick(document, [
      'onfullscreenchange',
      'onwebkitfullscreenchange',
      'onmozfullscreenchange',
      'onMSFullscreenChange'
    ]).replace(/^on/, ''),
    fullscreenerror: pick(document, [
      'onfullscreenerror',
      'onwebkitfullscreenerror',
      'onmozfullscreenerror',
      'onMSFullscreenError'
    ]).replace(/^on/, '')
  };
  var changeEvent = browser.fullscreenchange + '.' + namespace + '-fullscreen';
  function unbindChangeEvent() {
    $$1(document).unbind(changeEvent);
  }
  function bindChangeEvent(callback) {
    unbindChangeEvent();
    $$1(document).bind(changeEvent, callback);
  }
  var orientationEvent = 'orientationchange.' + namespace + '-fullscreen';
  function unbindOrientationEvent() {
    $$1(window).unbind(orientationEvent);
  }
  function bindOrientationEvent(callback) {
    unbindOrientationEvent();
    $$1(window).bind(orientationEvent, callback);
  }
  function requestFullscreenNative(e) {
    e = e || document.documentElement;
    e[browser.requestFullscreen]();
  }
  function exitFullscreen() {
    return document[browser.exitFullscreen]();
  }
  function fullscreenEnabled() {
    return document[browser.fullscreenEnabled];
  }
  function fullscreenElement() {
    return document[browser.fullscreenElement];
  }
  function isFullscreen() {
    return !!fullscreenElement();
  }
  function toggleFullscreen(data, opts) {
    if (isFullscreen()) {
      this.apiRequestFullscreen(opts);
    }
    else {
      this.exitFullscreen();
    }
  }
  function requestFullscreen(data, opts) {
    opts = opts || {};
    var oWidth = data.width;
    var oHeight = data.height;
    var oSource = data.source;
    var oSize = data.sizeMode;
    var oResponsive = data.responsive;
    var enter = function () {
      data.width = window.screen.width;
      data.height = window.screen.height;
      data.source = (opts.source || oSource);
      data.sizeMode = opts.sizeMode || 'fit';
      data.responsive = false;
      boot(data);
    };
    var exit = function () {
      data.width = oWidth;
      data.height = oHeight;
      data.source = oSource;
      data.sizeMode = oSize;
      data.responsive = oResponsive;
      boot(data);
    };
    bindChangeEvent(function () {
      if (isFullscreen()) {
        enter();
        bindOrientationEvent(enter);
      }
      else {
        unbindChangeEvent();
        unbindOrientationEvent();
        exit();
      }
    });
    requestFullscreenNative(data.target[0]);
  }
  extendApi({
    fullscreenEnabled: fullscreenEnabled,
    fullscreenElement: fullscreenElement,
    exitFullscreen: exitFullscreen,
    toggleFullscreen: function (opts) {
      toggleFullscreen(this.data, opts);
    },
    requestFullscreen: function (opts) {
      requestFullscreen(this.data, opts);
    }
  });

  (function () {
    var NAME = 'click';
    function click(e, data) {
      if (data.loading || !data.stage.is(':visible')) {
        return;
      }
      updateInput(e, data);
      var input = getInputState(data);
      var half, pos;
      var target = data.target, offset = target.offset();
      if (data.orientation === 'horizontal') {
        half = target.innerWidth() / 2;
        pos = input.currentX - offset.left;
      }
      else {
        half = target.innerHeight() / 2;
        pos = input.currentY - offset.top;
      }
      updateFrame(data, data.frame + (pos > half ? 1 : -1));
    }
    registerPlugin(NAME, {
      name: NAME,
      mouseup: click,
      touchend: click
    });
  })();

  (function () {
    var NAME = 'drag';
    function getState$$1(data) {
      return getPluginState(data, NAME);
    }
    function getAxis(data) {
      if (typeof data.orientation === 'number') {
        return data.orientation * Math.PI / 180;
      }
      if (data.orientation === 'horizontal') {
        return 0;
      }
      return Math.PI / 2;
    }
    function onInit(e, data) {
      var state = getState$$1(data);
      var d = [200, 1500];
      var t = data.touchScrollTimer || d;
      state.minTime = t[0] || d[0];
      state.maxTime = t[1] || d[1];
    }
    function dragStart(e, data) {
      var state = getState$$1(data);
      if (data.loading || is(data, 'dragging') || data['zoomPinFrame'] && !data.stage.is(':visible')) {
        return;
      }
      // Touch scroll can only be disabled by cancelling the 'touchstart' event.
      // If we would try to cancel the 'touchmove' event during a scroll
      // chrome browser raises an error
      //
      // When a user interacts with sprite spin, we dont know whether the intention
      // is to scroll the page or to roll the spin.
      //
      // On first interaction with SpriteSpin the scroll is not disabled
      // On double tap within 200ms the scroll is not disabled
      // Scroll is only disabled if there was an interaction with SpriteSpin in the past 1500ms
      var now = new Date().getTime();
      if (state.endAt && (now - state.endAt > state.maxTime)) {
        // reset timer if the user has no interaction with spritespin within 1500ms
        state.startAt = null;
        state.endAt = null;
      }
      if (state.startAt && (now - state.startAt > state.minTime)) {
        // disable scroll only if there was already an interaction with spritespin
        // however, allow scrolling on double tab within 200ms
        e.preventDefault();
      }
      state.startAt = now;
      state.wasPlaying = !!getPlaybackState(data).handler;
      state.frame = data.frame || 0;
      state.lane = data.lane || 0;
      flag(data, 'dragging', true);
      updateInput(e, data);
    }
    function dragEnd(e, data) {
      if (is(data, 'dragging')) {
        getState$$1(data).endAt = new Date().getTime();
        flag(data, 'dragging', false);
        resetInput(data);
        if (data.retainAnimate && getState$$1(data).wasPlaying) {
          startAnimation(data);
        }
      }
    }
    function drag(e, data) {
      var state = getState$$1(data);
      var input = getInputState(data);
      if (!is(data, 'dragging')) {
        return;
      }
      updateInput(e, data);
      var rad = getAxis(data);
      var sn = Math.sin(rad);
      var cs = Math.cos(rad);
      var x = ((input.nddX * cs - input.nddY * sn) * data.sense) || 0;
      var y = ((input.nddX * sn + input.nddY * cs) * (data.senseLane || data.sense)) || 0;
      // accumulate
      state.frame += data.frames * x;
      state.lane += data.lanes * y;
      // update spritespin
      var oldFrame = data.frame;
      var oldLane = data.lane;
      updateFrame(data, Math.floor(state.frame), Math.floor(state.lane));
      stopAnimation(data);
    }
    function mousemove(e, data) {
      dragStart(e, data);
      drag(e, data);
    }
    registerPlugin('drag', {
      name: 'drag',
      onInit: onInit,
      mousedown: dragStart,
      mousemove: drag,
      mouseup: dragEnd,
      documentmousemove: drag,
      documentmouseup: dragEnd,
      touchstart: dragStart,
      touchmove: drag,
      touchend: dragEnd,
      touchcancel: dragEnd
    });
    registerPlugin('move', {
      name: 'move',
      onInit: onInit,
      mousemove: mousemove,
      mouseleave: dragEnd,
      touchstart: dragStart,
      touchmove: drag,
      touchend: dragEnd,
      touchcancel: dragEnd
    });
  })();

  (function () {
    var NAME = 'hold';
    function getState$$1(data) {
      return getPluginState(data, NAME);
    }
    function rememberOptions(data) {
      var state = getState$$1(data);
      state.frameTime = data.frameTime;
      state.animate = data.animate;
      state.reverse = data.reverse;
    }
    function restoreOptions(data) {
      var state = getState$$1(data);
      data.frameTime = state.frameTime;
      data.animate = state.animate;
      data.reverse = state.reverse;
    }
    function start(e, data) {
      if (is(data, 'loading') || is(data, 'dragging') || !data.stage.is(':visible')) {
        return;
      }
      rememberOptions(data);
      updateInput(e, data);
      flag(data, 'dragging', true);
      data.animate = true;
      applyAnimation(data);
    }
    function stop(e, data) {
      flag(data, 'dragging', false);
      resetInput(data);
      stopAnimation(data);
      restoreOptions(data);
      applyAnimation(data);
    }
    function update(e, data) {
      if (!is(data, 'dragging')) {
        return;
      }
      updateInput(e, data);
      var input = getInputState(data);
      var half, delta;
      var target = data.target, offset = target.offset();
      if (data.orientation === 'horizontal') {
        half = target.innerWidth() / 2;
        delta = (input.currentX - offset.left - half) / half;
      }
      else {
        half = (data.height / 2);
        delta = (input.currentY - offset.top - half) / half;
      }
      data.reverse = delta < 0;
      delta = delta < 0 ? -delta : delta;
      data.frameTime = 80 * (1 - delta) + 20;
      if (((data.orientation === 'horizontal') && (input.dX < input.dY)) ||
        ((data.orientation === 'vertical') && (input.dX < input.dY))) {
        e.preventDefault();
      }
    }
    function onFrame(e, data) {
      data.animate = true;
      applyAnimation(data);
    }
    registerPlugin(NAME, {
      name: NAME,
      mousedown: start,
      mousemove: update,
      mouseup: stop,
      mouseleave: stop,
      touchstart: start,
      touchmove: update,
      touchend: stop,
      touchcancel: stop,
      onFrame: onFrame
    });
  })();

  (function () {
    var NAME = 'swipe';
    function getState$$1(data) {
      return getPluginState(data, NAME);
    }
    function getOption(data, name, fallback) {
      return data[name] || fallback;
    }
    function init(e, data) {
      var state = getState$$1(data);
      state.fling = getOption(data, 'swipeFling', 10);
      state.snap = getOption(data, 'swipeSnap', 0.50);
    }
    function start(e, data) {
      if (!data.loading && !is(data, 'dragging')) {
        updateInput(e, data);
        flag(data, 'dragging', true);
      }
    }
    function update(e, data) {
      if (!is(data, 'dragging')) {
        return;
      }
      updateInput(e, data);
      var frame = data.frame;
      var lane = data.lane;
      updateFrame(data, frame, lane);
    }
    function end(e, data) {
      if (!is(data, 'dragging')) {
        return;
      }
      flag(data, 'dragging', false);
      var state = getState$$1(data);
      var input = getInputState(data);
      var frame = data.frame;
      var lane = data.lane;
      var snap = state.snap;
      var fling = state.fling;
      var dS, dF;
      if (data.orientation === 'horizontal') {
        dS = input.ndX;
        dF = input.ddX;
      }
      else {
        dS = input.ndY;
        dF = input.ddY;
      }
      if (dS >= snap || dF >= fling) {
        frame = data.frame - 1;
      }
      else if (dS <= -snap || dF <= -fling) {
        frame = data.frame + 1;
      }
      resetInput(data);
      updateFrame(data, frame, lane);
      stopAnimation(data);
    }
    registerPlugin(NAME, {
      name: NAME,
      onLoad: init,
      mousedown: start,
      mousemove: update,
      mouseup: end,
      mouseleave: end,
      touchstart: start,
      touchmove: update,
      touchend: end,
      touchcancel: end
    });
  })();

  (function () {
    var NAME = 'wheel';
    function wheel(e, data) {
      if (!data.loading && data.stage.is(':visible')) {
        e.preventDefault();
        var we = e.originalEvent;
        var signX = we.deltaX === 0 ? 0 : we.deltaX > 0 ? 1 : -1;
        var signY = we.deltaY === 0 ? 0 : we.deltaY > 0 ? 1 : -1;
        updateFrame(data, data.frame + signY, data.lane + signX);
      }
    }
    registerPlugin(NAME, {
      name: NAME,
      wheel: wheel
    });
  })();

  (function () {
    var template = "\n<div class='spritespin-progress'>\n  <div class='spritespin-progress-label'></div>\n  <div class='spritespin-progress-bar'></div>\n</div>\n";
    function getState$$1(data) {
      return getPluginState(data, NAME);
    }
    var NAME = 'progress';
    function onInit(e, data) {
      var state = getState$$1(data);
      if (!state.stage) {
        state.stage = $$1(template);
        state.stage.appendTo(data.target);
      }
      state.stage.find('.spritespin-progress-label')
        .text("0%")
        .css({ 'text-align': 'center' });
      state.stage.find('.spritespin-progress-bar').css({
        width: "0%"
      });
      state.stage.hide().fadeIn();
    }
    function onProgress(e, data) {
      var state = getState$$1(data);
      state.stage.find('.spritespin-progress-label')
        .text(data.progress.percent + "%")
        .css({ 'text-align': 'center' });
      state.stage.find('.spritespin-progress-bar').css({
        width: data.progress.percent + "%"
      });
    }
    function onLoad(e, data) {
      $$1(getState$$1(data).stage).fadeOut();
    }
    function onDestroy(e, data) {
      $$1(getState$$1(data).stage).remove();
    }
    registerPlugin(NAME, {
      name: NAME,
      onInit: onInit,
      onProgress: onProgress,
      onLoad: onLoad,
      onDestroy: onDestroy
    });
  })();

  (function () {
    var NAME = '360';
    function onLoad(e, data) {
      data.stage.find('.spritespin-frames').detach();
      if (data.renderer === 'image') {
        $(data.images).addClass('spritespin-frames').appendTo(data.stage);
      }
    }
    function onDraw(e, data) {
      var specs = findSpecs(data.metrics, data.frames, data.frame, data.lane);
      var sheet = specs.sheet;
      var sprite = specs.sprite;
      if (!sheet || !sprite) {
        return;
      }
      var src = data.source[sheet.id];
      var image = data.images[sheet.id];
      if (data.renderer === 'canvas') {
        data.canvas.show();
        var w = data.canvas[0].width / data.canvasRatio;
        var h = data.canvas[0].height / data.canvasRatio;
        data.context.clearRect(0, 0, w, h);
        data.context.drawImage(image, sprite.sampledX, sprite.sampledY, sprite.sampledWidth, sprite.sampledHeight, 0, 0, w, h);
        return;
      }
      var scaleX = data.stage.innerWidth() / sprite.sampledWidth;
      var scaleY = data.stage.innerHeight() / sprite.sampledHeight;
      var top = Math.floor(-sprite.sampledY * scaleY);
      var left = Math.floor(-sprite.sampledX * scaleX);
      var width = Math.floor(sheet.sampledWidth * scaleX);
      var height = Math.floor(sheet.sampledHeight * scaleY);
      if (data.renderer === 'background') {
        data.stage.css({
          'background-image': "url('" + src + "')",
          'background-position': left + "px " + top + "px",
          'background-repeat': 'no-repeat',
          // set custom background size to enable responsive rendering
          '-webkit-background-size': width + "px " + height + "px",
          '-moz-background-size': width + "px " + height + "px",
          '-o-background-size': width + "px " + height + "px",
          'background-size': width + "px " + height + "px" /* Chrome, Firefox 4+, IE 9+, Opera, Safari 5+ */
        });
        return;
      }
      $(data.images).hide();
      $(image).show().css({
        position: 'absolute',
        top: top,
        left: left,
        'max-width': 'initial',
        width: width,
        height: height
      });
    }
    registerPlugin(NAME, {
      name: NAME,
      onLoad: onLoad,
      onDraw: onDraw
    });
  })();

  (function () {
    var NAME = 'blur';
    function getState$$1(data) {
      return getPluginState(data, NAME);
    }
    function getOption(data, name, fallback) {
      return data[name] || fallback;
    }
    function init(e, data) {
      var state = getState$$1(data);
      state.canvas = state.canvas || $$1("<canvas class='blur-layer'></canvas>");
      state.context = state.context || state.canvas[0].getContext('2d');
      state.steps = state.steps || [];
      state.fadeTime = Math.max(getOption(data, 'blurFadeTime', 200), 1);
      state.frameTime = Math.max(getOption(data, 'blurFrameTime', data.frameTime), 16);
      state.trackTime = null;
      state.cssBlur = !!getOption(data, 'blurCss', false);
      var inner = getInnerSize(data);
      var outer = data.responsive ? getComputedSize(data) : getOuterSize(data);
      var css = getInnerLayout(data.sizeMode, inner, outer);
      state.canvas[0].width = data.width * data.canvasRatio;
      state.canvas[0].height = data.height * data.canvasRatio;
      state.canvas.css(css).show();
      state.context.scale(data.canvasRatio, data.canvasRatio);
      data.target.append(state.canvas);
    }
    function onFrame(e, data) {
      var state = getState$$1(data);
      trackFrame(data);
      if (state.timeout == null) {
        loop(data);
      }
    }
    function trackFrame(data) {
      var state = getState$$1(data);
      var ani = getPlaybackState(data);
      // distance between frames
      var d = Math.abs(data.frame - ani.lastFrame);
      // shortest distance
      d = d >= data.frames / 2 ? data.frames - d : d;
      state.steps.unshift({
        frame: data.frame,
        lane: data.lane,
        live: 1,
        step: state.frameTime / state.fadeTime,
        d: d,
        alpha: 0
      });
    }
    var toRemove = [];
    function removeOldFrames(frames) {
      toRemove.length = 0;
      for (var i = 0; i < frames.length; i += 1) {
        if (frames[i].alpha <= 0) {
          toRemove.push(i);
        }
      }
      for (var _i = 0, toRemove_1 = toRemove; _i < toRemove_1.length; _i++) {
        var item = toRemove_1[_i];
        frames.splice(item, 1);
      }
    }
    function loop(data) {
      var state = getState$$1(data);
      state.timeout = window.setTimeout(function () { tick(data); }, state.frameTime);
    }
    function killLoop(data) {
      var state = getState$$1(data);
      window.clearTimeout(state.timeout);
      state.timeout = null;
    }
    function applyCssBlur(canvas, d) {
      var amount = Math.min(Math.max((d / 2) - 4, 0), 2.5);
      var blur = "blur(" + amount + "px)";
      canvas.css({
        '-webkit-filter': blur,
        filter: blur
      });
    }
    function clearFrame(data, state) {
      state.canvas.show();
      var w = state.canvas[0].width / data.canvasRatio;
      var h = state.canvas[0].height / data.canvasRatio;
      // state.context.clearRect(0, 0, w, h)
    }
    function drawFrame(data, state, step) {
      if (step.alpha <= 0) {
        return;
      }
      var specs = findSpecs(data.metrics, data.frames, step.frame, step.lane);
      var sheet = specs.sheet;
      var sprite = specs.sprite;
      if (!sheet || !sprite) {
        return;
      }
      var src = data.source[sheet.id];
      var image = data.images[sheet.id];
      if (image.complete === false) {
        return;
      }
      state.canvas.show();
      var w = state.canvas[0].width / data.canvasRatio;
      var h = state.canvas[0].height / data.canvasRatio;
      state.context.globalAlpha = step.alpha;
      state.context.drawImage(image, sprite.sampledX, sprite.sampledY, sprite.sampledWidth, sprite.sampledHeight, 0, 0, w, h);
    }
    function tick(data) {
      var state = getState$$1(data);
      killLoop(data);
      if (!state.context) {
        return;
      }
      var d = 0;
      clearFrame(data, state);
      state.context.clearRect(0, 0, data.width, data.height);
      for (var _i = 0, _a = state.steps; _i < _a.length; _i++) {
        var step = _a[_i];
        step.live = Math.max(step.live - step.step, 0);
        step.alpha = Math.max(step.live - 0.25, 0);
        drawFrame(data, state, step);
        d += step.alpha + step.d;
      }
      if (state.cssBlur) {
        applyCssBlur(state.canvas, d);
      }
      removeOldFrames(state.steps);
      if (state.steps.length) {
        loop(data);
      }
    }
    registerPlugin(NAME, {
      name: NAME,
      onLoad: init,
      onFrameChanged: onFrame
    });
  })();

  (function () {
    var max = Math.max;
    var min = Math.min;
    var NAME = 'ease';
    function getState$$1(data) {
      return getPluginState(data, NAME);
    }
    function getOption(data, name, fallback) {
      return data[name] || fallback;
    }
    function init(e, data) {
      var state = getState$$1(data);
      state.maxSamples = max(getOption(data, 'easeMaxSamples', 5), 0);
      state.damping = max(min(getOption(data, 'easeDamping', 0.9), 0.999), 0);
      state.abortTime = max(getOption(data, 'easeAbortTime', 250), 16);
      state.updateTime = max(getOption(data, 'easeUpdateTime', data.frameTime), 16);
      state.samples = [];
      state.steps = [];
    }
    function update(e, data) {
      if (is(data, 'dragging')) {
        killLoop(data);
        sampleInput(data);
      }
    }
    function end(e, data) {
      var state = getState$$1(data);
      var samples = state.samples;
      var last;
      var lanes = 0;
      var frames = 0;
      var time = 0;
      for (var _i = 0, samples_1 = samples; _i < samples_1.length; _i++) {
        var sample = samples_1[_i];
        if (!last) {
          last = sample;
          continue;
        }
        var dt = sample.time - last.time;
        if (dt > state.abortTime) {
          lanes = frames = time = 0;
          return killLoop(data);
        }
        frames += sample.frame - last.frame;
        lanes += sample.lane - last.lane;
        time += dt;
        last = sample;
      }
      samples.length = 0;
      if (!time) {
        return;
      }
      state.lane = data.lane;
      state.lanes = 0;
      state.laneStep = lanes / time * state.updateTime;
      state.frame = data.frame;
      state.frames = 0;
      state.frameStep = frames / time * state.updateTime;
      loop(data);
    }
    function sampleInput(data) {
      var state = getState$$1(data);
      // add a new sample
      state.samples.push({
        time: new Date().getTime(),
        frame: data.frame,
        lane: data.lane
      });
      // drop old samples
      while (state.samples.length > state.maxSamples) {
        state.samples.shift();
      }
    }
    function killLoop(data) {
      var state = getState$$1(data);
      if (state.handler != null) {
        window.clearTimeout(state.handler);
        state.handler = null;
      }
    }
    function loop(data) {
      var state = getState$$1(data);
      state.handler = window.setTimeout(function () { tick(data); }, state.updateTime);
    }
    function tick(data) {
      var state = getState$$1(data);
      state.lanes += state.laneStep;
      state.frames += state.frameStep;
      state.laneStep *= state.damping;
      state.frameStep *= state.damping;
      var frame = Math.floor(state.frame + state.frames);
      var lane = Math.floor(state.lane + state.lanes);
      updateFrame(data, frame, lane);
      if (is(data, 'dragging')) {
        killLoop(data);
      }
      else if (Math.abs(state.frameStep) > 0.005 || Math.abs(state.laneStep) > 0.005) {
        loop(data);
      }
      else {
        killLoop(data);
      }
    }
    registerPlugin(NAME, {
      name: NAME,
      onLoad: init,
      mousemove: update,
      mouseup: end,
      mouseleave: end,
      touchmove: update,
      touchend: end,
      touchcancel: end
    });
  })();

  (function () {
    var NAME = 'gallery';
    function getState$$1(data) {
      return getPluginState(data, NAME);
    }
    function getOption(data, name, fallback) {
      return data[name] || fallback;
    }
    function load(e, data) {
      var state = getState$$1(data);
      state.images = [];
      state.offsets = [];
      state.frame = data.frame;
      state.speed = getOption(data, 'gallerySpeed', 500);
      state.opacity = getOption(data, 'galleryOpacity', 0.25);
      state.stage = getOption(data, 'galleryStage', $$1('<div></div>'));
      state.stage.empty().addClass('gallery-stage').prependTo(data.stage);
      var size = 0;
      for (var _i = 0, _a = data.images; _i < _a.length; _i++) {
        var image = _a[_i];
        var naturalSize$$1 = naturalSize(image);
        var scale = data.height / naturalSize$$1.height;
        var img = $$1(image);
        state.stage.append(img);
        state.images.push(img);
        state.offsets.push(-size + (data.width - image.width * scale) / 2);
        size += data.width;
        img.css({
          'max-width': 'initial',
          opacity: state.opacity,
          width: data.width,
          height: data.height
        });
      }
      var innerSize = getInnerSize(data);
      var outerSize = data.responsive ? getComputedSize(data) : getOuterSize(data);
      var layout = getInnerLayout(data.sizeMode, innerSize, outerSize);
      state.stage.css(layout).css({ width: size, left: state.offsets[state.frame] });
      state.images[state.frame].animate({ opacity: 1 }, { duration: state.speed });
    }
    function draw(e, data) {
      var state = getState$$1(data);
      var input = getInputState(data);
      var isDragging = is(data, 'dragging');
      if (state.frame !== data.frame && !isDragging) {
        state.stage.stop(true, false).animate({ left: state.offsets[data.frame] }, { duration: state.speed });
        state.images[state.frame].animate({ opacity: state.opacity }, { duration: state.speed });
        state.frame = data.frame;
        state.images[state.frame].animate({ opacity: 1 }, { duration: state.speed });
        state.stage.animate({ left: state.offsets[state.frame] });
      }
      else if (isDragging || state.dX !== input.dX) {
        state.dX = input.dX;
        state.ddX = input.ddX;
        state.stage.stop(true, true).css({ left: state.offsets[state.frame] + state.dX });
      }
    }
    registerPlugin(NAME, {
      name: NAME,
      onLoad: load,
      onDraw: draw
    });
  })();

  (function () {
    var NAME = 'panorama';
    function getState$$1(data) {
      return getPluginState(data, NAME);
    }
    function onLoad(e, data) {
      var state = getState$$1(data);
      var sprite = data.metrics[0];
      if (!sprite) {
        return;
      }
      if (data.orientation === 'horizontal') {
        state.scale = data.target.innerHeight() / sprite.sampledHeight;
        data.frames = sprite.sampledWidth;
      }
      else {
        state.scale = data.target.innerWidth() / sprite.sampledWidth;
        data.frames = sprite.sampledHeight;
      }
      var width = Math.floor(sprite.sampledWidth * state.scale);
      var height = Math.floor(sprite.sampledHeight * state.scale);
      data.stage.css({
        'background-image': "url(" + data.source[sprite.id] + ")",
        'background-repeat': 'repeat-both',
        // set custom background size to enable responsive rendering
        '-webkit-background-size': width + "px " + height + "px",
        '-moz-background-size': width + "px " + height + "px",
        '-o-background-size': width + "px " + height + "px",
        'background-size': width + "px " + height + "px" /* Chrome, Firefox 4+, IE 9+, Opera, Safari 5+ */
      });
    }
    function onDraw(e, data) {
      var state = getState$$1(data);
      var px = data.orientation === 'horizontal' ? 1 : 0;
      var py = px ? 0 : 1;
      var offset = data.frame % data.frames;
      var left = Math.round(px * offset * state.scale);
      var top = Math.round(py * offset * state.scale);
      data.stage.css({ 'background-position': left + "px " + top + "px" });
    }
    registerPlugin(NAME, {
      name: NAME,
      onLoad: onLoad,
      onDraw: onDraw
    });
  })();

  (function () {
    var NAME = 'zoom';
    function getState$$1(data) {
      return getPluginState(data, NAME);
    }
    function getOption(data, name, fallback) {
      return name in data ? data[name] : fallback;
    }
    function onInit(e, data) {
      var state = getState$$1(data);
      state.source = getOption(data, 'zoomSource', data.source);
      state.useWheel = getOption(data, 'zoomUseWheel', false);
      state.useClick = getOption(data, 'zoomUseClick', true);
      state.pinFrame = getOption(data, 'zoomPinFrame', true);
      state.doubleClickTime = getOption(data, 'zoomDoubleClickTime', 500);
      state.stage = state.stage || $$1("<div class='zoom-stage'></div>");
      state.stage.css({
        width: '100%',
        height: '100%',
        top: 0,
        left: 0,
        bottom: 0,
        right: 0,
        position: 'absolute'
      })
        .appendTo(data.target)
        .hide();
    }
    function onDestroy(e, data) {
      var state = getState$$1(data);
      if (state.stage) {
        state.stage.remove();
        delete state.stage;
      }
    }
    function updateInput$$1(e, data) {
      var state = getState$$1(data);
      if (!state.stage.is(':visible')) {
        return;
      }
      e.preventDefault();
      if (state.pinFrame) {
        // hack into drag/move module and disable dragging
        // prevents frame change during zoom mode
        flag(data, 'dragging', false);
      }
      // grab touch/cursor position
      var cursor = getCursorPosition(e);
      // normalize cursor position into [0:1] range
      var x = cursor.x / data.width;
      var y = cursor.y / data.height;
      if (state.oldX == null) {
        state.oldX = x;
        state.oldY = y;
      }
      if (state.currentX == null) {
        state.currentX = x;
        state.currentY = y;
      }
      // calculate move delta since last frame and remember current position
      var dx = x - state.oldX;
      var dy = y - state.oldY;
      state.oldX = x;
      state.oldY = y;
      // invert drag direction for touch events to enable 'natural' scrolling
      if (e.type.match(/touch/)) {
        dx = -dx;
        dy = -dy;
      }
      // accumulate display coordinates
      state.currentX = clamp(state.currentX + dx, 0, 1);
      state.currentY = clamp(state.currentY + dy, 0, 1);
      updateFrame(data, data.frame, data.lane);
    }
    function onClick(e, data) {
      var state = getState$$1(data);
      if (!state.useClick) {
        return;
      }
      e.preventDefault();
      // simulate double click
      var clickTime = new Date().getTime();
      if (!state.clickTime) {
        // on first click
        state.clickTime = clickTime;
        return;
      }
      // on second click
      var timeDelta = clickTime - state.clickTime;
      if (timeDelta > state.doubleClickTime) {
        // took too long, back to first click
        state.clickTime = clickTime;
        return;
      }
      // on valid double click
      state.clickTime = undefined;
      if (toggleZoom(data)) {
        updateInput$$1(e, data);
      }
    }
    function onMove(e, data) {
      var state = getState$$1(data);
      if (state.stage.is(':visible')) {
        updateInput$$1(e, data);
      }
    }
    function onDraw(e, data) {
      var state = getState$$1(data);
      // calculate the frame index
      var index = data.lane * data.frames + data.frame;
      // get the zoom image. Use original frames as fallback. This won't work for spritesheets
      var source = state.source[index];
      var spec = findSpecs(data.metrics, data.frames, data.frame, data.lane);
      // get display position
      var x = state.currentX;
      var y = state.currentY;
      // fallback to centered position
      if (x == null) {
        x = state.currentX = 0.5;
        y = state.currentY = 0.5;
      }
      if (source) {
        // scale up from [0:1] to [0:100] range
        x = Math.floor(x * 100);
        y = Math.floor(y * 100);
        // update background image and position
        state.stage.css({
          'background-repeat': 'no-repeat',
          'background-image': "url('" + source + "')",
          'background-position': x + "% " + y + "%"
        });
      }
      else if (spec.sheet && spec.sprite) {
        var sprite = spec.sprite;
        var sheet = spec.sheet;
        var src = data.source[sheet.id];
        var left = -Math.floor(sprite.sampledX + x * (sprite.sampledWidth - data.width));
        var top_1 = -Math.floor(sprite.sampledY + y * (sprite.sampledHeight - data.height));
        var width = sheet.sampledWidth;
        var height = sheet.sampledHeight;
        state.stage.css({
          'background-image': "url('" + src + "')",
          'background-position': left + "px " + top_1 + "px",
          'background-repeat': 'no-repeat',
          // set custom background size to enable responsive rendering
          '-webkit-background-size': width + "px " + height + "px",
          '-moz-background-size': width + "px " + height + "px",
          '-o-background-size': width + "px " + height + "px",
          'background-size': width + "px " + height + "px" /* Chrome, Firefox 4+, IE 9+, Opera, Safari 5+ */
        });
      }
    }
    function toggleZoom(data) {
      var state = getState$$1(data);
      if (!state.stage) {
        throw new Error('zoom module is not initialized or is not available.');
      }
      if (state.stage.is(':visible')) {
        showZoom(data);
      }
      else {
        hideZoom(data);
        return true;
      }
      return false;
    }
    function showZoom(data) {
      var state = getState$$1(data);
      state.stage.fadeOut();
      data.stage.fadeIn();
    }
    function hideZoom(data) {
      var state = getState$$1(data);
      state.stage.fadeIn();
      data.stage.fadeOut();
    }
    function wheel(e, data) {
      var state = getState$$1(data);
      if (!data.loading && state.useWheel) {
        var we = e.originalEvent;
        var signY = we.deltaY === 0 ? 0 : we.deltaY > 0 ? 1 : -1;
        if (typeof state.useWheel === 'number') {
          signY *= state.useWheel;
        }
        if (state.stage.is(':visible') && signY > 0) {
          e.preventDefault();
          showZoom(data);
        }
        if (!state.stage.is(':visible') && signY < 0) {
          e.preventDefault();
          hideZoom(data);
        }
      }
    }
    registerPlugin(NAME, {
      name: NAME,
      mousedown: onClick,
      touchstart: onClick,
      mousemove: onMove,
      touchmove: onMove,
      wheel: wheel,
      onInit: onInit,
      onDestroy: onDestroy,
      onDraw: onDraw
    });
    extendApi({
      toggleZoom: function () { toggleZoom(this.data); } // tslint:disable-line
    });
  })();

  var Utils = _Utils;

  exports.Utils = Utils;
  exports.sourceArray = sourceArray;
  exports.Api = Api;
  exports.extendApi = extendApi;
  exports.instances = instances;
  exports.applyEvents = applyEvents;
  exports.boot = boot;
  exports.create = create;
  exports.createOrUpdate = createOrUpdate;
  exports.destroy = destroy;
  exports.namespace = namespace;
  exports.eventNames = eventNames;
  exports.callbackNames = callbackNames;
  exports.eventsToPrevent = eventsToPrevent;
  exports.defaults = defaults;
  exports.getInputState = getInputState;
  exports.updateInput = updateInput;
  exports.resetInput = resetInput;
  exports.applyLayout = applyLayout;
  exports.getPlaybackState = getPlaybackState;
  exports.updateFrame = updateFrame;
  exports.stopAnimation = stopAnimation;
  exports.applyAnimation = applyAnimation;
  exports.startAnimation = startAnimation;
  exports.registerPlugin = registerPlugin;
  exports.registerModule = registerModule;
  exports.getPlugin = getPlugin;
  exports.applyPlugins = applyPlugins;
  exports.getState = getState;
  exports.getPluginState = getPluginState;
  exports.is = is;
  exports.flag = flag;

  Object.defineProperty(exports, '__esModule', { value: true });

})));
//# sourceMappingURL=spritespin.js.map

  1. 在页面上引入 html标签

    <div class="spritespin" > </div>
  2. 初始化时候调用一下即可
		  $(function() {
			  $('a.js-fullscreen').click(function(e) {
				  e.preventDefault();
				  $('.spritespin').spritespin('api').requestFullscreen();
			  });
			  $('.spritespin').spritespin({
				  source: SpriteSpin.sourceArray('../../../static/imgs/{frame}.jpg', { frame: [0,39], digits: 1 }),
				  width: 450,
				  height: 450,
				  sense: 1
			  });
		  });

这样就可以360°滑动,但是如果嵌套在轮播图是有问题 需要更改第 2、3步骤:

2. 第二步更改成

<div :class="'spritespin' + index" ></div>


3. 第三步改成

	  initPic(classNum,picAllList){
			  $('a.js-fullscreen').click(function(e) {
				  e.preventDefault();
				  $(classNum).spritespin('api').requestFullscreen();
			  });
			  $(classNum).spritespin({
				  source:picAllList,
				  width: 533,
				  height: 441,
				  sense: 1
			  });
		  },

这个方法在调用的时候是传的一个数组如下

 for(let i = 0; i< len; i++) 
 	{
	  	this.initPic('.spritespin'+i,this.prdList[i].picList)
	 }

这样就可以在轮播图中调用多个360°滑动图片!



版权声明:本文为return_js原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。