vc_carousel.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /**
  2. * Created by duhuan on 2017/9/14.
  3. */
  4. ;(function($) { "use strict";
  5. // CAROUSEL CLASS DEFINITION
  6. // =========================
  7. var Carousel = function (element, options) {
  8. this.$element = $(element)
  9. this.$indicators = this.$element.find('.vc_carousel-indicators')
  10. this.options = options
  11. this.paused =
  12. this.sliding =
  13. this.interval =
  14. this.$active =
  15. this.$items = null
  16. this.options.pause == 'hover' && this.$element
  17. .on('mouseenter', $.proxy(this.pause, this))
  18. .on('mouseleave', $.proxy(this.cycle, this))
  19. this._build() // new
  20. }
  21. Carousel.DEFAULTS = {
  22. mode: 'horizontal'
  23. , partial: false
  24. , interval: 5000
  25. , pause: 'hover'
  26. , wrap: false
  27. , autoHeight: false
  28. , perView: 1
  29. }
  30. Carousel.prototype.cycle = function (e) {
  31. e || (this.paused = false)
  32. this.interval && clearInterval(this.interval)
  33. this.options.interval
  34. && !this.paused
  35. && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
  36. this.touch_start_position = 0;
  37. return this
  38. }
  39. Carousel.prototype.getActiveIndex = function () {
  40. this.$active = this.$element.find('.vc_item.vc_active')
  41. if(!this.$active.length) this.$active = this.$element.find('.vc_item:first').addClass('vc_active')
  42. this.$items = this.$active.parent().children()
  43. return this.$items.index(this.$active)
  44. }
  45. Carousel.prototype.showHideControl = function(index) {
  46. if(typeof index === 'undefined') var index = this.getActiveIndex()
  47. this.$left_control[index===0 ? 'hide' : 'show']()
  48. this.$right_control[index===this.items_count-1 ? 'hide' : 'show']()
  49. }
  50. Carousel.prototype.to = function (pos) {
  51. var that = this
  52. var activeIndex = this.getActiveIndex()
  53. if (pos > (this.$items.length - 1) || pos < 0) return
  54. if (this.sliding) return this.$element.one('slid', function () { that.to(pos) })
  55. if (activeIndex == pos) return this.pause().cycle()
  56. return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
  57. }
  58. Carousel.prototype.pause = function (e) {
  59. e || (this.paused = true)
  60. if (this.$element.find('.vc_right.vc_carousel-control, .vc_left.vc_carousel-control').length && $.support.transition.end) {
  61. this.$element.trigger($.support.transition.end)
  62. this.cycle(true)
  63. }
  64. this.interval = clearInterval(this.interval)
  65. return this
  66. }
  67. Carousel.prototype.next = function () {
  68. if (this.sliding) return
  69. return this.slide('next')
  70. }
  71. Carousel.prototype.prev = function () {
  72. if (this.sliding) return
  73. return this.slide('prev')
  74. }
  75. Carousel.prototype.slide = function (type, next) {
  76. var $active = this.$element.find('.vc_item.vc_active')
  77. var $next = next || $active[type]()
  78. var isCycling = this.interval
  79. var direction = type == 'next' ? 'vc_left' : 'vc_right'
  80. var fallback = type == 'next' ? 'first' : 'last'
  81. var that = this
  82. if (!$next.length) {
  83. if (!this.options.wrap) {
  84. this.returnSwipedSlide()
  85. return
  86. }
  87. $next = this.$element.find('.vc_item')[fallback]()
  88. }
  89. this.sliding = true
  90. isCycling && this.pause()
  91. var e = $.Event('slide.vc.carousel', { relatedTarget: $next[0], direction: direction })
  92. if ($next.hasClass('vc_active')) return
  93. if (this.$indicators.length) {
  94. this.$indicators.find('.vc_active').removeClass('vc_active')
  95. this.$indicators.find('.vc_partial').removeClass('vc_partial')
  96. this.$element.one('slid', function () {
  97. var index = that.getActiveIndex(),
  98. $nextIndicator = $(that.$indicators.children().slice(index, that.getActiveIndex() + that.options.perView))
  99. $nextIndicator && $nextIndicator.addClass('vc_active')
  100. that.options.partial && $nextIndicator && (index+1 < that.items_count ? $nextIndicator.last().next().addClass('vc_partial') : $nextIndicator.first().prev().addClass('vc_partial'))
  101. !that.options.wrap && that.showHideControl(index)
  102. })
  103. }
  104. this.current_index = $next.index()
  105. if(this.current_index > this.items_count) {
  106. this.current_index = 0
  107. } else if(this.current_index < 0) {
  108. this.current_index = this.items_count -1
  109. }
  110. if(this.options.autoHeight) {
  111. this.current_pos_value = -1 * this._step * this.current_index
  112. } else {
  113. this.current_pos_value = -1 * $next.position()[this.animation_position]
  114. }
  115. if(this.options.partial && this.current_index >= this.items_count-1) {
  116. this.current_pos_value += this._step*(1-this.partial_part)
  117. }
  118. if ($.support.transition && this.$element.hasClass('vc_slide')) {
  119. this.$element.trigger(e)
  120. if (e.isDefaultPrevented()) return
  121. this.$slideline_inner
  122. .addClass('vc_transition')
  123. .css(this.animation_position, this.current_pos_value + that.pos_units)
  124. if(!this.options.autoHeight) this.recalculateSlidelineHeight($next.height(), true)
  125. this.$slideline_inner.one($.support.transition.end, function(){
  126. $next.addClass('vc_active')
  127. $active.removeClass('vc_active')
  128. that.$slideline_inner.removeClass([type, 'vc_transition'].join(' '))
  129. that.sliding = false
  130. that.removeSwipeAnimationSpeed()
  131. setTimeout(function () { that.$element.trigger('slid') }, 0)
  132. }).emulateTransitionEnd(this.transition_speed)
  133. } else {
  134. this.$element.trigger(e)
  135. if (e.isDefaultPrevented()) return
  136. $active.removeClass('vc_active')
  137. $next.addClass('vc_active')
  138. this.sliding = false
  139. this.$slideline_inner.css(this.animation_position, this.current_pos_value + that.pos_units)
  140. }
  141. isCycling && this.cycle()
  142. return this
  143. }
  144. Carousel.prototype.setSwipeAnimationSpeed = function() {
  145. this.$slideline_inner.addClass('vc_swipe-transition')
  146. }
  147. Carousel.prototype.removeSwipeAnimationSpeed = function() {
  148. this.$slideline_inner.removeClass('vc_swipe-transition')
  149. }
  150. Carousel.prototype.velocity = function(time, x) {
  151. return {
  152. x: Math.abs(x / time) || 0
  153. }
  154. }
  155. Carousel.prototype.recalculateSlidelineHeight = function(height, animate) {
  156. if(animate === true) {
  157. this.$slideline.animate({height: height})
  158. } else {
  159. this.$slideline.height(height)
  160. }
  161. }
  162. /**
  163. * Change layout size after resizing of window.
  164. */
  165. Carousel.prototype.resizeAction = function() {
  166. var max_height = 0,
  167. new_slideline_height = 0
  168. if(this.options.mode === 'horizontal') {
  169. this.el_effect_size = this.$element.width() * ( this.options.partial ? this.partial_part : 1 )
  170. this.$slideline.width(this.items_count*this.el_effect_size)
  171. }
  172. if (this.options.autoHeight) {
  173. this.$items.height('auto')
  174. this.$items.each(function(){
  175. var item_height = $(this).height()
  176. if(item_height > max_height) max_height = item_height
  177. })
  178. this.$items.height(max_height)
  179. } else {
  180. this.recalculateSlidelineHeight(this.$active.height())
  181. }
  182. if(this.options.mode === 'vertical') {
  183. this._step = this.$active.height()
  184. new_slideline_height = this.$active.height() * this.options.perView * (this.options.partial ? (1 + 1-this.partial_part) : 1)
  185. this.recalculateSlidelineHeight(new_slideline_height, false)
  186. this.$slideline_inner.css({top: -1 * this.$active.position().top})
  187. this.el_effect_size = this._step
  188. }
  189. }
  190. Carousel.prototype.returnSwipedSlide = function() {
  191. var params = {}
  192. params[this.animation_position] = this.current_pos_value + this.pos_units
  193. this.$slideline_inner.animate(params)
  194. }
  195. Carousel.prototype._build = function() {
  196. var el = this.$element.get(0),
  197. _touch_start_position = false,
  198. _touch_start_time = 0,
  199. _pos_before_touch = 0,
  200. _diff = 0,
  201. _moved = false,
  202. that = this,
  203. mode = this.options.mode
  204. this.getActiveIndex()
  205. this.el_width = 0
  206. this.items_count = this.$items.length
  207. this.$slideline = this.$element.find('.vc_carousel-slideline')
  208. this.$slideline_inner = this.$slideline.find('> div')
  209. this.slideline_inner = this.$slideline_inner.get(0)
  210. this.partial_part = 0.8
  211. this._slide_width = 0
  212. this.swipe_velocity = 0.7
  213. this.current_pos_value = 0
  214. this.current_index = 0 // TODO: default start position by options
  215. this.el_effect_size = 0
  216. this.transition_speed = 600
  217. this.$left_control = this.$element.find('.vc_left.vc_carousel-control')
  218. this.$right_control = this.$element.find('.vc_right.vc_carousel-control')
  219. // Enable autoHeight if partial
  220. if(this.options.partial) this.options.autoHeight = true
  221. // Add Css classes for perView > 1
  222. if(this.options.perView > 1) this.$element.addClass('vc_per-view-more vc_per-view-' + this.options.perView)
  223. if( mode === 'horizontal') {
  224. this.pos_units = '%'
  225. this._step = 100.00/this.items_count/this.options.perView
  226. this.animation_position = 'left'
  227. this.$items.width(this._step + this.pos_units)
  228. this.touch_direction = 'pageX'
  229. } else {
  230. this.pos_units = 'px'
  231. this.animation_position = 'top'
  232. this.touch_direction = 'pageY'
  233. this.$element.addClass('vc_carousel_vertical')
  234. }
  235. // Hide first control if this.current_index === 0
  236. !that.options.wrap && this.showHideControl()
  237. // Add partial css class if partial
  238. if(this.options.partial) this.$element.addClass('vc_partial')
  239. // Set indicator
  240. if(this.$indicators.length) {
  241. var $active_indecators = that.$indicators.children()
  242. .slice(this.current_index, this.current_index + this.options.perView)
  243. .addClass('vc_active')
  244. this.options.partial && $active_indecators.last().next().addClass('vc_partial')
  245. }
  246. $(window).resize(this.resizeAction.bind(this)); this.resizeAction()
  247. el.addEventListener("touchstart", function(e){
  248. _touch_start_position = parseFloat(e[that.touch_direction])
  249. _touch_start_time = e.timeStamp
  250. _pos_before_touch = that.$slideline_inner.position()[that.animation_position]
  251. }.bind(this), false)
  252. el.addEventListener('touchmove', function(e){
  253. _diff = parseFloat(e[that.touch_direction]) - _touch_start_position
  254. _moved = Math.abs(_diff) > 0
  255. if(!_moved) return true
  256. e.preventDefault()
  257. that.slideline_inner.style[that.animation_position] = (_pos_before_touch + _diff) + 'px'
  258. }, false)
  259. el.addEventListener('touchend', function(e){
  260. var time,part,velocity
  261. if(_moved) {
  262. time= (e.timeStamp-_touch_start_time)/1000
  263. part = _diff/ that.el_effect_size
  264. velocity = that.velocity(time, part)
  265. if((velocity.x > that.swipe_velocity && part < 0) || part <= -0.7) {
  266. that.setSwipeAnimationSpeed()
  267. that.next()
  268. } else if(velocity.x > that.swipe_velocity || part >= 0.7) {
  269. that.setSwipeAnimationSpeed()
  270. that.prev()
  271. } else {
  272. that.returnSwipedSlide()
  273. }
  274. _moved = false
  275. }
  276. }, false)
  277. this.$element.addClass('vc_build')
  278. return this
  279. }
  280. // CAROUSEL PLUGIN DEFINITION
  281. // ==========================
  282. var old = $.fn.carousel
  283. $.fn.carousel = function (option, value) {
  284. return this.each(function () {
  285. var $this = $(this)
  286. var data = $this.data('vc.carousel')
  287. var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
  288. var action = typeof option == 'string' ? option : options.slide
  289. if (!data) $this.data('vc.carousel', (data = new Carousel(this, options)))
  290. if (typeof option == 'number') data.to(option)
  291. else if (action) data[action](value)
  292. else if (options.interval) data.pause().cycle()
  293. })
  294. }
  295. $.fn.carousel.Constructor = Carousel
  296. // CAROUSEL NO CONFLICT
  297. // ====================
  298. $.fn.carousel.noConflict = function () {
  299. $.fn.carousel = old
  300. return this
  301. }
  302. // CAROUSEL DATA-API
  303. // =================
  304. $(document).off('click.vc.carousel.data-api').on('click.vc.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
  305. var $this = $(this), href
  306. var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
  307. var options = $.extend({}, $target.data(), $this.data())
  308. var slideIndex = $this.attr('data-slide-to')
  309. if (slideIndex) options.interval = false
  310. $target.carousel(options)
  311. if (slideIndex = $this.attr('data-slide-to')) {
  312. $target.data('vc.carousel').to(slideIndex)
  313. }
  314. e.preventDefault()
  315. })
  316. $(window).on('load', function () {
  317. $('[data-ride="vc_carousel"]').each(function () {
  318. var $carousel = $(this)
  319. $carousel.carousel($carousel.data())
  320. })
  321. })
  322. })(window.jQuery);