jquery.cloud9carousel.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /*
  2. * Cloud 9 Carousel 2.0
  3. * Cleaned up, refactored, and improved version of CloudCarousel
  4. *
  5. * See the demo and get the latest version on GitHub:
  6. * http://specious.github.io/cloud9carousel/
  7. *
  8. * Copyright (c) 2014 by Ildar Sagdejev ( http://twitter.com/tknomad )
  9. * Copyright (c) 2011 by R. Cecco ( http://www.professorcloud.com )
  10. * MIT License
  11. *
  12. * Please retain this copyright header in all versions of the software
  13. *
  14. * Requires:
  15. * - jQuery 1.3.0 or later -OR- Zepto 1.1.1 or later
  16. *
  17. * Optional (jQuery only):
  18. * - Reflection support via reflection.js plugin by Christophe Beyls
  19. * http://www.digitalia.be/software/reflectionjs-for-jquery
  20. * - Mousewheel support via mousewheel plugin
  21. * http://plugins.jquery.com/mousewheel/
  22. */
  23. ;(function ($) {
  24. //
  25. // Detect CSS transform support
  26. //
  27. var transform = (function () {
  28. var vendors = ['webkit', 'moz', 'ms']
  29. var style = document.createElement('div').style
  30. var trans = 'transform' in style ? 'transform' : undefined
  31. for (var i = 0, count = vendors.length; i < count; i++) {
  32. var prop = vendors[i] + 'Transform'
  33. if (prop in style) {
  34. trans = prop
  35. break
  36. }
  37. }
  38. return trans
  39. })()
  40. var Item = function (element, options) {
  41. element.item = this
  42. this.element = element
  43. if (element.tagName === 'IMG') {
  44. this.fullWidth = element.width
  45. this.fullHeight = element.height
  46. } else {
  47. element.style.display = 'inline-block'
  48. this.fullWidth = element.offsetWidth
  49. this.fullHeight = element.offsetHeight
  50. }
  51. element.style.position = 'absolute'
  52. if (options.mirror && this.element.tagName === 'IMG') {
  53. // Wrap image in a div together with its generated reflection
  54. this.reflection = $(element).reflect(options.mirror).next()[0]
  55. var $reflection = $(this.reflection)
  56. this.reflection.fullHeight = $reflection.height()
  57. $reflection.css('margin-top', options.mirror.gap + 'px')
  58. $reflection.css('width', '100%')
  59. element.style.width = '100%'
  60. // The item element now contains the image and reflection
  61. this.element = this.element.parentNode
  62. this.element.item = this
  63. this.element.alt = element.alt
  64. this.element.title = element.title
  65. }
  66. if (transform && options.transforms) this.element.style[transform + 'Origin'] = '0 0'
  67. this.moveTo = function (x, y, scale) {
  68. this.width = this.fullWidth * scale
  69. this.height = this.fullHeight * scale
  70. this.x = x
  71. this.y = y
  72. this.scale = scale
  73. var style = this.element.style
  74. style.zIndex = ('' + scale * 100) | 0
  75. if (transform && options.transforms) {
  76. style[transform] = 'translate(' + x + 'px, ' + y + 'px) scale(' + scale + ')'
  77. } else {
  78. // The gap between the image and its reflection doesn't resize automatically
  79. if (options.mirror && this.element.tagName === 'IMG')
  80. this.reflection.style.marginTop = options.mirror.gap * scale + 'px'
  81. style.width = this.width + 'px'
  82. style.left = x + 'px'
  83. style.top = y + 'px'
  84. }
  85. }
  86. }
  87. var time = (function () {
  88. return !window.performance || !window.performance.now
  89. ? function () {
  90. return +new Date()
  91. }
  92. : function () {
  93. return performance.now()
  94. }
  95. })()
  96. //
  97. // Detect requestAnimationFrame() support
  98. //
  99. // Support legacy browsers:
  100. // http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
  101. //
  102. var cancelFrame = window.cancelAnimationFrame || window.cancelRequestAnimationFrame
  103. var requestFrame = window.requestAnimationFrame
  104. ;(function () {
  105. var vendors = ['webkit', 'moz', 'ms']
  106. for (var i = 0, count = vendors.length; i < count && !cancelFrame; i++) {
  107. cancelFrame =
  108. window[vendors[i] + 'CancelAnimationFrame'] ||
  109. window[vendors[i] + 'CancelRequestAnimationFrame']
  110. requestFrame = requestFrame && window[vendors[i] + 'RequestAnimationFrame']
  111. }
  112. })()
  113. let myTime = -1
  114. var Carousel = function (element, options) {
  115. var self = this
  116. // 邵根
  117. myTime = window.setInterval(() => {
  118. try {
  119. clearInterval(myTime)
  120. window.moveFu(self)
  121. } catch (error) {}
  122. }, 200)
  123. var $container = $(element)
  124. this.items = []
  125. this.xOrigin = options.xOrigin === null ? $container.width() * 0.5 : options.xOrigin
  126. this.yOrigin = options.yOrigin === null ? $container.height() * 0.1 : options.yOrigin
  127. this.xRadius = options.xRadius === null ? $container.width() / 2.3 : options.xRadius
  128. this.yRadius = options.yRadius === null ? $container.height() / 6 : options.yRadius
  129. this.farScale = options.farScale
  130. this.rotation = this.destRotation = Math.PI / 2 // start with the first item positioned in front
  131. this.speed = options.speed
  132. this.smooth = options.smooth
  133. this.fps = options.fps
  134. this.timer = 0
  135. this.autoPlayAmount = options.autoPlay
  136. this.autoPlayDelay = options.autoPlayDelay
  137. this.autoPlayTimer = 0
  138. this.onLoaded = options.onLoaded
  139. this.onRendered = options.onRendered
  140. this.itemOptions = {
  141. transforms: options.transforms
  142. }
  143. if (options.mirror) {
  144. this.itemOptions.mirror = $.extend({ gap: 2 }, options.mirror)
  145. }
  146. $container.css({ position: 'relative', overflow: 'hidden' })
  147. // Rotation:
  148. // * 0 : right
  149. // * Pi/2 : front
  150. // * Pi : left
  151. // * 3 Pi/2 : back
  152. this.rotateItem = function (itemIndex, rotation) {
  153. var item = this.items[itemIndex]
  154. var sin = Math.sin(rotation)
  155. var farScale = this.farScale
  156. var scale = farScale + (1 - farScale) * (sin + 1) * 0.5
  157. item.moveTo(
  158. this.xOrigin + scale * (Math.cos(rotation) * this.xRadius - item.fullWidth * 0.5),
  159. this.yOrigin + scale * sin * this.yRadius,
  160. scale
  161. )
  162. }
  163. this.render = function () {
  164. var count = this.items.length
  165. var spacing = (2 * Math.PI) / count
  166. var radians = this.rotation
  167. for (var i = 0; i < count; i++) {
  168. this.rotateItem(i, radians)
  169. radians += spacing
  170. }
  171. if (typeof this.onRendered === 'function') this.onRendered(this)
  172. }
  173. this.playFrame = function () {
  174. var rem = self.destRotation - self.rotation
  175. var now = time()
  176. var dt = (now - self.lastTime) * 0.002
  177. self.lastTime = now
  178. if (Math.abs(rem) < 0.003) {
  179. self.rotation = self.destRotation
  180. self.pause()
  181. } else {
  182. // Rotate asymptotically closer to the destination
  183. self.rotation = self.destRotation - rem / (1 + self.speed * dt)
  184. self.scheduleNextFrame()
  185. }
  186. self.render()
  187. }
  188. this.scheduleNextFrame = function () {
  189. this.lastTime = time()
  190. this.timer =
  191. this.smooth && cancelFrame
  192. ? requestFrame(self.playFrame)
  193. : setTimeout(self.playFrame, 1000 / this.fps)
  194. }
  195. this.itemsRotated = function () {
  196. return (this.items.length * (Math.PI / 2 - this.rotation)) / (2 * Math.PI)
  197. }
  198. this.floatIndex = function () {
  199. var count = this.items.length
  200. var floatIndex = this.itemsRotated() % count
  201. // Make sure float-index is positive
  202. return floatIndex < 0 ? floatIndex + count : floatIndex
  203. }
  204. this.nearestIndex = function () {
  205. return Math.round(this.floatIndex()) % this.items.length
  206. }
  207. let myIndex = -1
  208. this.nearestItem = function () {
  209. // 获取索引
  210. if (this.nearestIndex() !== myIndex) {
  211. // 邵根
  212. window.getIndexFu(this.nearestIndex())
  213. myIndex = this.nearestIndex()
  214. }
  215. return this.items[this.nearestIndex()]
  216. }
  217. this.play = function () {
  218. if (this.timer === 0) this.scheduleNextFrame()
  219. }
  220. this.pause = function () {
  221. this.smooth && cancelFrame ? cancelFrame(this.timer) : clearTimeout(this.timer)
  222. this.timer = 0
  223. }
  224. //
  225. // Spin the carousel. Count is the number (+-) of carousel items to rotate
  226. //
  227. this.go = function (count) {
  228. this.destRotation += ((2 * Math.PI) / this.items.length) * count
  229. this.play()
  230. }
  231. this.deactivate = function () {
  232. this.pause()
  233. clearInterval(this.autoPlayTimer)
  234. options.buttonLeft.unbind('click')
  235. options.buttonRight.unbind('click')
  236. $container.unbind('.cloud9')
  237. }
  238. this.autoPlay = function () {
  239. this.autoPlayTimer = setInterval(function () {
  240. self.go(self.autoPlayAmount)
  241. }, this.autoPlayDelay)
  242. }
  243. this.enableAutoPlay = function () {
  244. // Stop auto-play on mouse over
  245. $container.bind('mouseover.cloud9', function () {
  246. clearInterval(self.autoPlayTimer)
  247. })
  248. // Resume auto-play when mouse leaves the container
  249. $container.bind('mouseout.cloud9', function () {
  250. self.autoPlay()
  251. })
  252. this.autoPlay()
  253. }
  254. this.bindControls = function () {
  255. options.buttonLeft.bind('click', function () {
  256. self.go(-1)
  257. return false
  258. })
  259. options.buttonRight.bind('click', function () {
  260. self.go(1)
  261. return false
  262. })
  263. if (options.mouseWheel) {
  264. $container.bind('mousewheel.cloud9', function (event, delta) {
  265. self.go(delta > 0 ? 1 : -1)
  266. return false
  267. })
  268. }
  269. if (options.bringToFront) {
  270. $container.bind('click.cloud9', function (event) {
  271. var hits = $(event.target).closest('.' + options.itemClass)
  272. if (hits.length !== 0) {
  273. // var idx = self.items.indexOf(hits[0].item)
  274. // var count = self.items.length
  275. // var diff = idx - (self.floatIndex() % count)
  276. // // Choose direction based on which way is shortest
  277. // if (2 * Math.abs(diff) > count) diff += diff > 0 ? -count : count
  278. // self.destRotation = self.rotation
  279. // self.go(-diff)
  280. }
  281. })
  282. }
  283. }
  284. var items = $container.find('.' + options.itemClass)
  285. this.finishInit = function () {
  286. //
  287. // Wait until all images have completely loaded
  288. //
  289. for (var i = 0; i < items.length; i++) {
  290. var item = items[i]
  291. if (
  292. item.tagName === 'IMG' &&
  293. (item.width === undefined || (item.complete !== undefined && !item.complete))
  294. )
  295. return
  296. }
  297. clearInterval(this.initTimer)
  298. // Init items
  299. for (i = 0; i < items.length; i++)
  300. this.items.push(new Item(items[i], this.itemOptions))
  301. // Disable click-dragging of items
  302. $container.bind('mousedown onselectstart', function () {
  303. return false
  304. })
  305. if (this.autoPlayAmount !== 0) this.enableAutoPlay()
  306. this.bindControls()
  307. this.render()
  308. if (typeof this.onLoaded === 'function') this.onLoaded(this)
  309. }
  310. this.initTimer = setInterval(function () {
  311. self.finishInit()
  312. }, 50)
  313. }
  314. //
  315. // The jQuery plugin
  316. //
  317. $.fn.Cloud9Carousel = function (options) {
  318. return this.each(function () {
  319. /* For full list of options see the README */
  320. options = $.extend(
  321. {
  322. xOrigin: null, // null: calculated automatically
  323. yOrigin: null,
  324. xRadius: null,
  325. yRadius: null,
  326. farScale: 0.5, // scale of the farthest item
  327. transforms: true, // enable CSS transforms
  328. smooth: true, // enable smooth animation via requestAnimationFrame()
  329. fps: 30, // fixed frames per second (if smooth animation is off)
  330. speed: 4, // positive number
  331. autoPlay: 0, // [ 0: off | number of items (integer recommended, positive is clockwise) ]
  332. autoPlayDelay: 4000,
  333. bringToFront: false,
  334. itemClass: 'cloud9-item',
  335. handle: 'carousel'
  336. },
  337. options
  338. )
  339. $(this).data(options.handle, new Carousel(this, options))
  340. })
  341. }
  342. })(window.jQuery || window.Zepto)