You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

444 line
13 KiB

  1. // import $ from 'jquery';
  2. import Hammer from 'hammerjs';
  3. const $ = require('jquery');
  4. class Slider {
  5. constructor(section) {
  6. this.section = section.addClass('initiated');
  7. this.slides = section.children();
  8. this.slideOverflow = $('<div class="slide-overflow" />').appendTo(section);
  9. this.slideWrapper = $('<div class="slide-wrapper" />').appendTo(this.slideOverflow).append(this.slides);
  10. this.currentSlide = 0;
  11. this.autoplayTimeout = 0;
  12. this.isHovered = false;
  13. this.sectionInitialClicked = false;
  14. this.dragging = false;
  15. this.inViewport = false;
  16. this.positionAnimation = {
  17. x: 0
  18. }
  19. this.settings = {
  20. loop: false,
  21. autoplay: this.section.data('autoplay'),
  22. infinite: true,
  23. autoplaySpeed: this.section.data('autoplay-speed') ? this.section.data('autoplay-speed') : 5000,
  24. animationSpeed: 600,
  25. startSlide: 0,
  26. };
  27. this.initSlides();
  28. if (this.settings.infinite) {
  29. this.initInfinity();
  30. }
  31. this.initUI();
  32. this.checkSize();
  33. if (this.slides.length > 1) {
  34. this.initHammerDragging();
  35. this.initTabbing();
  36. }
  37. this.changeSlide(this.settings.startSlide);
  38. this.initAutoplay();
  39. this.checkSize();
  40. $(window).on('resize', () => {
  41. this.checkSize();
  42. })
  43. }
  44. initSlides() {
  45. this.slides.each(function (i) {
  46. const slide = $(this).attr('data-index', i);
  47. const h3 = slide.find('h3');
  48. const p = h3.next('p');
  49. const dotsString = '&hellip;'
  50. if (!slide.find('.image-box').length) {
  51. slide.addClass('text-only');
  52. }
  53. if (h3.text().length > 48) {
  54. h3.html( h3.text().substr(0, 48) + dotsString );
  55. }
  56. if (p.text().length > 148) {
  57. p.html( p.text().substr(0, 148) + dotsString );
  58. }
  59. })
  60. }
  61. checkSize () {
  62. if (this.controls.find('.tabs').width() > this.slideOverflow.width() * 0.6) {
  63. this.section.addClass('many-slides');
  64. }
  65. else {
  66. this.section.removeClass('many-slides');
  67. }
  68. }
  69. initInfinity() {
  70. this.nextWrapper = this.slideWrapper.clone();
  71. this.prevWrapper = this.slideWrapper.clone();
  72. this.nextWrapper
  73. .removeClass('slide-wrapper')
  74. .addClass('next-clone')
  75. .appendTo(this.slideWrapper)
  76. .css({'left': this.slides.length + '%'});
  77. this.prevWrapper
  78. .removeClass('slide-wrapper')
  79. .addClass('prev-clone')
  80. .appendTo(this.slideWrapper);
  81. this.nextWrapper.find('a, button, input, select, textarea').attr('tabindex', -1);
  82. this.prevWrapper.find('a, button, input, select, textarea').attr('tabindex', -1);
  83. }
  84. initUI() {
  85. this.count = $('<span />');
  86. const prevButton = $('<button class="prev" aria-label="Previous Slide" />');
  87. const nextButton = $('<button class="next" aria-label="Next Slide" />');
  88. const tabs = $('<ul class="tabs" />');
  89. const count = $('<span class="count" />')
  90. .html('/<span class="total">' + this.slides.length + '</span></span>')
  91. .prepend(this.count);
  92. this.slides.each(function (i) {
  93. const s = $(this);
  94. const li = $('<li/>').appendTo(tabs);
  95. $('<button />').appendTo(li).text(s.data('title') ? s.data('title') : i + 1);
  96. });
  97. this.tabs = tabs.children();
  98. tabs.find('button').on('click', (e) => {
  99. e.preventDefault();
  100. this.sectionInitialClicked = true;
  101. this.changeSlide($(e.currentTarget).parent().index());
  102. });
  103. prevButton.on('click', (e) => {
  104. e.preventDefault();
  105. this.sectionInitialClicked = true;
  106. this.onPrev();
  107. });
  108. nextButton.on('click', (e) => {
  109. e.preventDefault();
  110. this.sectionInitialClicked = true;
  111. this.onNext();
  112. });
  113. this.controls = $('<div class="controls" />').append(prevButton).append(tabs).append(count).append(nextButton).appendTo(this.section);
  114. }
  115. changeSlide(index, offset, transition = true) {
  116. const t = this;
  117. const prevIndex = index === 0 ? this.slides.length - 1 : index - 1;
  118. const nextIndex = index === this.slides.length - 1 ? 0 : index + 1;
  119. const allWidth = t.nextWrapper.position().left;
  120. if (!offset) {
  121. offset = 0;
  122. }
  123. t.positionAnimation = {
  124. x: this.slideWrapper.position().left
  125. }
  126. let target = (index + offset) / -100 * this.slideWrapper.width();
  127. let time = Math.round(Math.abs(this.positionAnimation.x - target) / 5 + 300);
  128. if (time > 800) {
  129. time = 800;
  130. }
  131. if (!transition) {
  132. time = 0;
  133. }
  134. t.slideOverflow.css({
  135. 'transition-duration': time + 'ms',
  136. 'height': this.slideOverflow.outerHeight() + 'px'
  137. });
  138. t.slideWrapper.find('.current').removeClass('current');
  139. t.slideWrapper.find('[data-index="' + index + '"]').addClass('current');
  140. t.slideWrapper.find('.is-prev').removeClass('is-prev');
  141. t.slideWrapper.find('[data-index="' + prevIndex + '"]').addClass('is-prev');
  142. t.slideWrapper.find('.is-next').removeClass('is-next');
  143. t.slideWrapper.find('[data-index="' + nextIndex + '"]').addClass('is-next');
  144. if (target > 0) {
  145. target = target - allWidth;
  146. this.positionAnimation.x = this.positionAnimation.x - allWidth;
  147. }
  148. if (target <= allWidth * -1 + 1) {
  149. target = target + allWidth;
  150. this.positionAnimation.x = this.positionAnimation.x + allWidth;
  151. }
  152. requestAnimationFrame(() => {
  153. this.slideOverflow.css({
  154. 'height': this.slides.eq(index).outerHeight() + 'px'
  155. });
  156. })
  157. $(this.positionAnimation).animate({
  158. x: target
  159. }, {
  160. duration: time,
  161. easing: 'easeOutCubic',
  162. step: (now) => {
  163. this.slideWrapper.css({'transform': 'translate3d(' + Math.round(now) + 'px, 0, 0)'});
  164. },
  165. complete: () => {
  166. this.slideWrapper.css({'transform': 'translate3d(' + (index * -1) + '%, 0, 0)'});
  167. this.slideOverflow.css('height', 'auto');
  168. }
  169. })
  170. this.tabs.eq(index).addClass('active').siblings('.active').removeClass('active');
  171. this.count.text(index + 1);
  172. if (this.settings.autoplay && !this.isHovered) {
  173. this.handleAutoplay();
  174. }
  175. if (offset !== 0) {
  176. setTimeout(() => {
  177. this.slideWrapper.addClass('no-transition');
  178. this.slideWrapper.css({'transform': 'translate3d(' + (index * -1) + '%, 0, 0)'});
  179. setTimeout(() => {
  180. this.slideWrapper.removeClass('no-transition');
  181. },20);
  182. }, this.settings.animationSpeed);
  183. }
  184. this.currentSlide = index;
  185. this.section.trigger('slide-change');
  186. if (this.section.closest('.steps').length && this.sectionInitialClicked) {
  187. this.scrollToTop();
  188. }
  189. }
  190. goTo(index, transition) {
  191. if (transition === false) {
  192. this.slideWrapper.addClass('no-transition');
  193. }
  194. this.sectionInitialClicked = true;
  195. this.changeSlide(index,0, transition);
  196. setTimeout(() => {
  197. this.slideWrapper.removeClass('no-transition');
  198. }, 20);
  199. }
  200. onNext() {
  201. const nextSlide = this.currentSlide === this.slides.length - 1 ? 0 : this.currentSlide + 1;
  202. const offset = this.settings.infinite && nextSlide === 0 ? this.slides.length : 0;
  203. this.changeSlide(nextSlide, offset);
  204. }
  205. onPrev() {
  206. const prevSlide = this.currentSlide === 0 ? this.slides.length - 1 : this.currentSlide - 1;
  207. const offset = this.settings.infinite && prevSlide === this.slides.length - 1 ? this.slides.length * -1 : 0;
  208. this.changeSlide(prevSlide, offset);
  209. }
  210. initAutoplay() {
  211. this.section.on('mouseenter', () => {
  212. this.isHovered = true;
  213. clearTimeout(this.autoplayTimeout);
  214. })
  215. this.section.on('mouseleave', () => {
  216. this.isHovered = false;
  217. this.handleAutoplay();
  218. })
  219. this.initScrollCheck();
  220. this.handleAutoplay();
  221. }
  222. handleAutoplay() {
  223. if (this.dragging) {
  224. return false;
  225. }
  226. clearTimeout(this.autoplayTimeout);
  227. if (this.settings.autoplaySpeed > 0 && this.settings.autoplay) {
  228. this.autoplayTimeout = setTimeout(() => {
  229. if (this.inViewport) {
  230. this.onNext();
  231. } else {
  232. this.handleAutoplay();
  233. }
  234. }, this.settings.autoplaySpeed);
  235. }
  236. }
  237. initScrollCheck() {
  238. window.addEventListener("scroll", () => {
  239. window.requestAnimationFrame(() => {
  240. this.scrollCheck();
  241. })
  242. }, {passive: true});
  243. window.requestAnimationFrame(() => {
  244. this.scrollCheck();
  245. })
  246. }
  247. scrollCheck() {
  248. const w = $(window);
  249. if (w.scrollTop() + w.height() - 200 > this.section.offset().top && w.scrollTop() < this.section.offset().top + this.section.outerHeight() / 2) {
  250. if (!this.inViewport) {
  251. this.inViewport = true;
  252. this.section.addClass('in-viewport').trigger('in-viewport');
  253. }
  254. }
  255. else if (this.inViewport) {
  256. this.inViewport = false;
  257. this.section.removeClass('in-viewport');
  258. }
  259. }
  260. initHammerDragging() {
  261. const hammer = new Hammer(this.slideWrapper.get(0));
  262. let sliderWidth = 0;
  263. let startValue = 0;
  264. this.dragging = false;
  265. this.slideWrapper.find('img, a').attr('draggable', 'false').attr('ondragstart', 'return false;');
  266. hammer.on('panstart', (e) => {
  267. sliderWidth = this.slideOverflow.width();
  268. this.dragging = true;
  269. startValue = this.slideWrapper.position().left;
  270. $(this.positionAnimation).stop();
  271. requestAnimationFrame(() => {
  272. $('html').addClass('slider-dragging');
  273. })
  274. if (e.center.x !== 0 && e.center.y !== 0) {
  275. clearTimeout(this.autoplayTimeout);
  276. }
  277. });
  278. hammer.on('pan', (e) => {
  279. if (e.center.x !== 0 && e.center.y !== 0) {
  280. const transformX = Math.round(startValue + e.deltaX);
  281. this.slideWrapper.css({'transform': 'translate3d(' + transformX + 'px, 0, 0)'});
  282. }
  283. });
  284. hammer.on('panend',(e) => {
  285. this.dragging = false;
  286. requestAnimationFrame(() => {
  287. $('html').removeClass('slider-dragging');
  288. if (e.deltaX > sliderWidth / 5) {
  289. this.onPrev();
  290. } else if (e.deltaX < sliderWidth / -5) {
  291. this.onNext();
  292. } else {
  293. this.changeSlide(this.currentSlide);
  294. }
  295. })
  296. });
  297. }
  298. initTabbing() {
  299. $(window).keydown((e) => {
  300. const code = (e.keyCode ? e.keyCode : e.which);
  301. if (code === 9 && this.slideWrapper.find(':focus').length) {
  302. this.tabScrollActive = true;
  303. this.resetTabScroll();
  304. }
  305. })
  306. $(window).keyup((e) => {
  307. const code = (e.keyCode ? e.keyCode : e.which);
  308. if (code === 9 && this.slideWrapper.find(':focus').length) {
  309. const slide = this.section.find(':focus').closest('.slide');
  310. this.slideOverflow.scrollLeft(0).scrollTop(0).find('.outer, .text-box').scrollLeft(0).scrollTop(0);
  311. this.goTo(parseInt(slide.attr('data-index')), true);
  312. this.tabScrollActive = false;
  313. }
  314. });
  315. }
  316. loadImage(index) {
  317. const t = this;
  318. if (!index) {
  319. index = this.currentSlide;
  320. }
  321. if (t.slides.eq(index).hasClass('preload') && !t.slides.eq(index).find('img').length) {
  322. const ib = t.slides.eq(index).find('.image-box');
  323. const img = $('<img/>').one('load', function (e) {
  324. const loadedImage = $(this);
  325. const slide = loadedImage.closest('.slide').removeClass('preload');
  326. const index = parseInt(slide.attr('data-index'));
  327. t.nextWrapper.find('[data-index="' + index + '"]').removeClass('preload').find('.image-box').append(loadedImage.clone());
  328. t.prevWrapper.find('[data-index="' + index + '"]').removeClass('preload').find('.image-box').append(loadedImage.clone());
  329. })
  330. img.attr('src', ib.attr('data-src')).appendTo(ib);
  331. img.attr('data-download', ib.attr('data-download')).appendTo(ib);
  332. img.attr('draggable', 'false').attr('ondragstart', 'return false;');
  333. }
  334. }
  335. resetTabScroll() {
  336. this.slideOverflow.scrollLeft(0).scrollTop(0).find('.outer, .text-box').scrollLeft(0).scrollTop(0);
  337. if (this.tabScrollActive) {
  338. window.requestAnimationFrame(() => {
  339. this.resetTabScroll();
  340. })
  341. }
  342. }
  343. scrollToTop() {
  344. const target = this.section.closest('.steps').offset().top - 120;
  345. const time = 600;
  346. $('html, body').animate({'scrollTop': target}, time, 'easeOutQuad');
  347. };
  348. }
  349. export default Slider;
  350. $('body').on('ihk-init dynamic-component-loaded gfi-dynamic-init', function (e) {
  351. $('.rotation .slider:not(.initiated), .steps .slider:not(.initiated)').each((i, el) => {
  352. const selector = $(el);
  353. if (!selector.find('dynamic-content').length) {
  354. new Slider(selector);
  355. }
  356. })
  357. /*
  358. let slidersSelector;
  359. if (e.type === "ihk-init") {
  360. slidersSelector = $('.rotation .slider:not(.initiated):not(:has(dynamic-content)), .steps .slider:not(.initiated):not(:has(dynamic-content))')
  361. } else {
  362. slidersSelector = $('.rotation .slider:not(.initiated), .steps .slider:not(.initiated)');
  363. }
  364. slidersSelector.each(function () {
  365. new Slider($(this));
  366. });
  367. */
  368. });