|
@@ -0,0 +1,408 @@
|
|
|
|
+/*
|
|
|
|
+ * Cloud 9 Carousel 2.0
|
|
|
|
+ * Cleaned up, refactored, and improved version of CloudCarousel
|
|
|
|
+ *
|
|
|
|
+ * See the demo and get the latest version on GitHub:
|
|
|
|
+ * http://specious.github.io/cloud9carousel/
|
|
|
|
+ *
|
|
|
|
+ * Copyright (c) 2014 by Ildar Sagdejev ( http://twitter.com/tknomad )
|
|
|
|
+ * Copyright (c) 2011 by R. Cecco ( http://www.professorcloud.com )
|
|
|
|
+ * MIT License
|
|
|
|
+ *
|
|
|
|
+ * Please retain this copyright header in all versions of the software
|
|
|
|
+ *
|
|
|
|
+ * Requires:
|
|
|
|
+ * - jQuery 1.3.0 or later -OR- Zepto 1.1.1 or later
|
|
|
|
+ *
|
|
|
|
+ * Optional (jQuery only):
|
|
|
|
+ * - Reflection support via reflection.js plugin by Christophe Beyls
|
|
|
|
+ * http://www.digitalia.be/software/reflectionjs-for-jquery
|
|
|
|
+ * - Mousewheel support via mousewheel plugin
|
|
|
|
+ * http://plugins.jquery.com/mousewheel/
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+;(function ($) {
|
|
|
|
+ //
|
|
|
|
+ // Detect CSS transform support
|
|
|
|
+ //
|
|
|
|
+ var transform = (function () {
|
|
|
|
+ var vendors = ['webkit', 'moz', 'ms']
|
|
|
|
+ var style = document.createElement('div').style
|
|
|
|
+ var trans = 'transform' in style ? 'transform' : undefined
|
|
|
|
+
|
|
|
|
+ for (var i = 0, count = vendors.length; i < count; i++) {
|
|
|
|
+ var prop = vendors[i] + 'Transform'
|
|
|
|
+ if (prop in style) {
|
|
|
|
+ trans = prop
|
|
|
|
+ break
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return trans
|
|
|
|
+ })()
|
|
|
|
+
|
|
|
|
+ var Item = function (element, options) {
|
|
|
|
+ element.item = this
|
|
|
|
+ this.element = element
|
|
|
|
+
|
|
|
|
+ if (element.tagName === 'IMG') {
|
|
|
|
+ this.fullWidth = element.width
|
|
|
|
+ this.fullHeight = element.height
|
|
|
|
+ } else {
|
|
|
|
+ element.style.display = 'inline-block'
|
|
|
|
+ this.fullWidth = element.offsetWidth
|
|
|
|
+ this.fullHeight = element.offsetHeight
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ element.style.position = 'absolute'
|
|
|
|
+
|
|
|
|
+ if (options.mirror && this.element.tagName === 'IMG') {
|
|
|
|
+ // Wrap image in a div together with its generated reflection
|
|
|
|
+ this.reflection = $(element).reflect(options.mirror).next()[0]
|
|
|
|
+
|
|
|
|
+ var $reflection = $(this.reflection)
|
|
|
|
+ this.reflection.fullHeight = $reflection.height()
|
|
|
|
+ $reflection.css('margin-top', options.mirror.gap + 'px')
|
|
|
|
+ $reflection.css('width', '100%')
|
|
|
|
+ element.style.width = '100%'
|
|
|
|
+
|
|
|
|
+ // The item element now contains the image and reflection
|
|
|
|
+ this.element = this.element.parentNode
|
|
|
|
+ this.element.item = this
|
|
|
|
+ this.element.alt = element.alt
|
|
|
|
+ this.element.title = element.title
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (transform && options.transforms) this.element.style[transform + 'Origin'] = '0 0'
|
|
|
|
+
|
|
|
|
+ this.moveTo = function (x, y, scale) {
|
|
|
|
+ this.width = this.fullWidth * scale
|
|
|
|
+ this.height = this.fullHeight * scale
|
|
|
|
+ this.x = x
|
|
|
|
+ this.y = y
|
|
|
|
+ this.scale = scale
|
|
|
|
+
|
|
|
|
+ var style = this.element.style
|
|
|
|
+ style.zIndex = ('' + scale * 100) | 0
|
|
|
|
+
|
|
|
|
+ if (transform && options.transforms) {
|
|
|
|
+ style[transform] = 'translate(' + x + 'px, ' + y + 'px) scale(' + scale + ')'
|
|
|
|
+ } else {
|
|
|
|
+ // The gap between the image and its reflection doesn't resize automatically
|
|
|
|
+ if (options.mirror && this.element.tagName === 'IMG')
|
|
|
|
+ this.reflection.style.marginTop = options.mirror.gap * scale + 'px'
|
|
|
|
+
|
|
|
|
+ style.width = this.width + 'px'
|
|
|
|
+ style.left = x + 'px'
|
|
|
|
+ style.top = y + 'px'
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var time = (function () {
|
|
|
|
+ return !window.performance || !window.performance.now
|
|
|
|
+ ? function () {
|
|
|
|
+ return +new Date()
|
|
|
|
+ }
|
|
|
|
+ : function () {
|
|
|
|
+ return performance.now()
|
|
|
|
+ }
|
|
|
|
+ })()
|
|
|
|
+
|
|
|
|
+ //
|
|
|
|
+ // Detect requestAnimationFrame() support
|
|
|
|
+ //
|
|
|
|
+ // Support legacy browsers:
|
|
|
|
+ // http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
|
|
|
|
+ //
|
|
|
|
+ var cancelFrame = window.cancelAnimationFrame || window.cancelRequestAnimationFrame
|
|
|
|
+ var requestFrame = window.requestAnimationFrame
|
|
|
|
+
|
|
|
|
+ ;(function () {
|
|
|
|
+ var vendors = ['webkit', 'moz', 'ms']
|
|
|
|
+
|
|
|
|
+ for (var i = 0, count = vendors.length; i < count && !cancelFrame; i++) {
|
|
|
|
+ cancelFrame =
|
|
|
|
+ window[vendors[i] + 'CancelAnimationFrame'] ||
|
|
|
|
+ window[vendors[i] + 'CancelRequestAnimationFrame']
|
|
|
|
+ requestFrame = requestFrame && window[vendors[i] + 'RequestAnimationFrame']
|
|
|
|
+ }
|
|
|
|
+ })()
|
|
|
|
+
|
|
|
|
+ let myTime = -1
|
|
|
|
+
|
|
|
|
+ var Carousel = function (element, options) {
|
|
|
|
+ var self = this
|
|
|
|
+
|
|
|
|
+ // 邵根
|
|
|
|
+ myTime = window.setInterval(() => {
|
|
|
|
+ try {
|
|
|
|
+ clearInterval(myTime)
|
|
|
|
+ window.moveFu(self)
|
|
|
|
+ } catch (error) {}
|
|
|
|
+ }, 200)
|
|
|
|
+
|
|
|
|
+ var $container = $(element)
|
|
|
|
+ this.items = []
|
|
|
|
+ this.xOrigin = options.xOrigin === null ? $container.width() * 0.5 : options.xOrigin
|
|
|
|
+ this.yOrigin = options.yOrigin === null ? $container.height() * 0.1 : options.yOrigin
|
|
|
|
+ this.xRadius = options.xRadius === null ? $container.width() / 2.3 : options.xRadius
|
|
|
|
+ this.yRadius = options.yRadius === null ? $container.height() / 6 : options.yRadius
|
|
|
|
+ this.farScale = options.farScale
|
|
|
|
+ this.rotation = this.destRotation = Math.PI / 2 // start with the first item positioned in front
|
|
|
|
+ this.speed = options.speed
|
|
|
|
+ this.smooth = options.smooth
|
|
|
|
+ this.fps = options.fps
|
|
|
|
+ this.timer = 0
|
|
|
|
+ this.autoPlayAmount = options.autoPlay
|
|
|
|
+ this.autoPlayDelay = options.autoPlayDelay
|
|
|
|
+ this.autoPlayTimer = 0
|
|
|
|
+ this.onLoaded = options.onLoaded
|
|
|
|
+ this.onRendered = options.onRendered
|
|
|
|
+
|
|
|
|
+ this.itemOptions = {
|
|
|
|
+ transforms: options.transforms
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (options.mirror) {
|
|
|
|
+ this.itemOptions.mirror = $.extend({ gap: 2 }, options.mirror)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $container.css({ position: 'relative', overflow: 'hidden' })
|
|
|
|
+
|
|
|
|
+ // Rotation:
|
|
|
|
+ // * 0 : right
|
|
|
|
+ // * Pi/2 : front
|
|
|
|
+ // * Pi : left
|
|
|
|
+ // * 3 Pi/2 : back
|
|
|
|
+ this.rotateItem = function (itemIndex, rotation) {
|
|
|
|
+ var item = this.items[itemIndex]
|
|
|
|
+ var sin = Math.sin(rotation)
|
|
|
|
+ var farScale = this.farScale
|
|
|
|
+ var scale = farScale + (1 - farScale) * (sin + 1) * 0.5
|
|
|
|
+
|
|
|
|
+ item.moveTo(
|
|
|
|
+ this.xOrigin + scale * (Math.cos(rotation) * this.xRadius - item.fullWidth * 0.5),
|
|
|
|
+ this.yOrigin + scale * sin * this.yRadius,
|
|
|
|
+ scale
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.render = function () {
|
|
|
|
+ var count = this.items.length
|
|
|
|
+ var spacing = (2 * Math.PI) / count
|
|
|
|
+ var radians = this.rotation
|
|
|
|
+
|
|
|
|
+ for (var i = 0; i < count; i++) {
|
|
|
|
+ this.rotateItem(i, radians)
|
|
|
|
+ radians += spacing
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (typeof this.onRendered === 'function') this.onRendered(this)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.playFrame = function () {
|
|
|
|
+ var rem = self.destRotation - self.rotation
|
|
|
|
+ var now = time()
|
|
|
|
+ var dt = (now - self.lastTime) * 0.002
|
|
|
|
+ self.lastTime = now
|
|
|
|
+
|
|
|
|
+ if (Math.abs(rem) < 0.003) {
|
|
|
|
+ self.rotation = self.destRotation
|
|
|
|
+ self.pause()
|
|
|
|
+ } else {
|
|
|
|
+ // Rotate asymptotically closer to the destination
|
|
|
|
+ self.rotation = self.destRotation - rem / (1 + self.speed * dt)
|
|
|
|
+ self.scheduleNextFrame()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.render()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.scheduleNextFrame = function () {
|
|
|
|
+ this.lastTime = time()
|
|
|
|
+
|
|
|
|
+ this.timer =
|
|
|
|
+ this.smooth && cancelFrame
|
|
|
|
+ ? requestFrame(self.playFrame)
|
|
|
|
+ : setTimeout(self.playFrame, 1000 / this.fps)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.itemsRotated = function () {
|
|
|
|
+ return (this.items.length * (Math.PI / 2 - this.rotation)) / (2 * Math.PI)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.floatIndex = function () {
|
|
|
|
+ var count = this.items.length
|
|
|
|
+ var floatIndex = this.itemsRotated() % count
|
|
|
|
+
|
|
|
|
+ // Make sure float-index is positive
|
|
|
|
+ return floatIndex < 0 ? floatIndex + count : floatIndex
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.nearestIndex = function () {
|
|
|
|
+ return Math.round(this.floatIndex()) % this.items.length
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let myIndex = -1
|
|
|
|
+
|
|
|
|
+ this.nearestItem = function () {
|
|
|
|
+ // 获取索引
|
|
|
|
+
|
|
|
|
+ if (this.nearestIndex() !== myIndex) {
|
|
|
|
+ // 邵根
|
|
|
|
+ window.getIndexFu(this.nearestIndex())
|
|
|
|
+ myIndex = this.nearestIndex()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return this.items[this.nearestIndex()]
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.play = function () {
|
|
|
|
+ if (this.timer === 0) this.scheduleNextFrame()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.pause = function () {
|
|
|
|
+ this.smooth && cancelFrame ? cancelFrame(this.timer) : clearTimeout(this.timer)
|
|
|
|
+ this.timer = 0
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //
|
|
|
|
+ // Spin the carousel. Count is the number (+-) of carousel items to rotate
|
|
|
|
+ //
|
|
|
|
+ this.go = function (count) {
|
|
|
|
+ this.destRotation += ((2 * Math.PI) / this.items.length) * count
|
|
|
|
+ this.play()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.deactivate = function () {
|
|
|
|
+ this.pause()
|
|
|
|
+ clearInterval(this.autoPlayTimer)
|
|
|
|
+ options.buttonLeft.unbind('click')
|
|
|
|
+ options.buttonRight.unbind('click')
|
|
|
|
+ $container.unbind('.cloud9')
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.autoPlay = function () {
|
|
|
|
+ this.autoPlayTimer = setInterval(function () {
|
|
|
|
+ self.go(self.autoPlayAmount)
|
|
|
|
+ }, this.autoPlayDelay)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.enableAutoPlay = function () {
|
|
|
|
+ // Stop auto-play on mouse over
|
|
|
|
+ $container.bind('mouseover.cloud9', function () {
|
|
|
|
+ clearInterval(self.autoPlayTimer)
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ // Resume auto-play when mouse leaves the container
|
|
|
|
+ $container.bind('mouseout.cloud9', function () {
|
|
|
|
+ self.autoPlay()
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ this.autoPlay()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.bindControls = function () {
|
|
|
|
+ options.buttonLeft.bind('click', function () {
|
|
|
|
+ self.go(-1)
|
|
|
|
+ return false
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ options.buttonRight.bind('click', function () {
|
|
|
|
+ self.go(1)
|
|
|
|
+ return false
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ if (options.mouseWheel) {
|
|
|
|
+ $container.bind('mousewheel.cloud9', function (event, delta) {
|
|
|
|
+ self.go(delta > 0 ? 1 : -1)
|
|
|
|
+ return false
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (options.bringToFront) {
|
|
|
|
+ $container.bind('click.cloud9', function (event) {
|
|
|
|
+ var hits = $(event.target).closest('.' + options.itemClass)
|
|
|
|
+
|
|
|
|
+ if (hits.length !== 0) {
|
|
|
|
+ // var idx = self.items.indexOf(hits[0].item)
|
|
|
|
+ // var count = self.items.length
|
|
|
|
+ // var diff = idx - (self.floatIndex() % count)
|
|
|
|
+ // // Choose direction based on which way is shortest
|
|
|
|
+ // if (2 * Math.abs(diff) > count) diff += diff > 0 ? -count : count
|
|
|
|
+ // self.destRotation = self.rotation
|
|
|
|
+ // self.go(-diff)
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var items = $container.find('.' + options.itemClass)
|
|
|
|
+
|
|
|
|
+ this.finishInit = function () {
|
|
|
|
+ //
|
|
|
|
+ // Wait until all images have completely loaded
|
|
|
|
+ //
|
|
|
|
+ for (var i = 0; i < items.length; i++) {
|
|
|
|
+ var item = items[i]
|
|
|
|
+ if (
|
|
|
|
+ item.tagName === 'IMG' &&
|
|
|
|
+ (item.width === undefined || (item.complete !== undefined && !item.complete))
|
|
|
|
+ )
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ clearInterval(this.initTimer)
|
|
|
|
+
|
|
|
|
+ // Init items
|
|
|
|
+ for (i = 0; i < items.length; i++)
|
|
|
|
+ this.items.push(new Item(items[i], this.itemOptions))
|
|
|
|
+
|
|
|
|
+ // Disable click-dragging of items
|
|
|
|
+ $container.bind('mousedown onselectstart', function () {
|
|
|
|
+ return false
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ if (this.autoPlayAmount !== 0) this.enableAutoPlay()
|
|
|
|
+ this.bindControls()
|
|
|
|
+ this.render()
|
|
|
|
+
|
|
|
|
+ if (typeof this.onLoaded === 'function') this.onLoaded(this)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.initTimer = setInterval(function () {
|
|
|
|
+ self.finishInit()
|
|
|
|
+ }, 50)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //
|
|
|
|
+ // The jQuery plugin
|
|
|
|
+ //
|
|
|
|
+ $.fn.Cloud9Carousel = function (options) {
|
|
|
|
+ return this.each(function () {
|
|
|
|
+ /* For full list of options see the README */
|
|
|
|
+ options = $.extend(
|
|
|
|
+ {
|
|
|
|
+ xOrigin: null, // null: calculated automatically
|
|
|
|
+ yOrigin: null,
|
|
|
|
+ xRadius: null,
|
|
|
|
+ yRadius: null,
|
|
|
|
+ farScale: 0.5, // scale of the farthest item
|
|
|
|
+ transforms: true, // enable CSS transforms
|
|
|
|
+ smooth: true, // enable smooth animation via requestAnimationFrame()
|
|
|
|
+ fps: 30, // fixed frames per second (if smooth animation is off)
|
|
|
|
+ speed: 4, // positive number
|
|
|
|
+ autoPlay: 0, // [ 0: off | number of items (integer recommended, positive is clockwise) ]
|
|
|
|
+ autoPlayDelay: 4000,
|
|
|
|
+ bringToFront: false,
|
|
|
|
+ itemClass: 'cloud9-item',
|
|
|
|
+ handle: 'carousel'
|
|
|
|
+ },
|
|
|
|
+ options
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ $(this).data(options.handle, new Carousel(this, options))
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+})(window.jQuery || window.Zepto)
|