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.
 
 
 
 

508 lines
15 KiB

  1. import $ from 'jquery';
  2. import Hammer from 'hammerjs';
  3. import 'jquery.easing';
  4. class IHKSlider {
  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. if (this.section.closest('.steps').length) {
  41. this.setupSteps();
  42. }
  43. $(window).on('resize', () => {
  44. this.checkSize();
  45. })
  46. }
  47. initSlides() {
  48. this.slides.each(function (i) {
  49. const slide = $(this).attr('data-index', i);
  50. const h3 = slide.find('h3, .like-h2, .like-h3').first();
  51. const p = h3.next('p');
  52. const dotsString = '&hellip;'
  53. if (!slide.find('.image-box img').length) {
  54. slide.addClass('text-only');
  55. } else {
  56. if (h3.text().length > 48) {
  57. h3.html(h3.text().substring(0, 48) + dotsString);
  58. }
  59. if (p.text().length > 148) {
  60. p.html(p.text().substring(0, 148) + dotsString);
  61. }
  62. }
  63. })
  64. }
  65. checkSize() {
  66. if (this.controls.find('.tabs').width() > this.slideOverflow.width() * 0.6) {
  67. this.section.addClass('many-slides');
  68. } else {
  69. this.section.removeClass('many-slides');
  70. }
  71. }
  72. initInfinity() {
  73. this.nextWrapper = this.slideWrapper.clone();
  74. this.prevWrapper = this.slideWrapper.clone();
  75. this.nextWrapper
  76. .removeClass('slide-wrapper')
  77. .addClass('next-clone')
  78. .appendTo(this.slideWrapper)
  79. .css({'left': this.slides.length + '%'});
  80. this.prevWrapper
  81. .removeClass('slide-wrapper')
  82. .addClass('prev-clone')
  83. .appendTo(this.slideWrapper);
  84. this.nextWrapper.find('a, button, input, select, textarea').attr('tabindex', -1);
  85. this.prevWrapper.find('a, button, input, select, textarea').attr('tabindex', -1);
  86. this.nextWrapper.find('img').removeClass('loading');
  87. this.prevWrapper.find('img').removeClass('loading');
  88. }
  89. initUI() {
  90. this.count = $('<span />');
  91. this.prevButton = $('<button class="prev" aria-label="Zurück" />');
  92. this.nextButton = $('<button class="next" aria-label="Weiter" />');
  93. const tabsWrapper = $('<ul class="slider-tabs" />');
  94. if (!this.section.closest('.steps').length) {
  95. tabsWrapper.addClass('variant-base');
  96. }
  97. const countWrapper = $('<span class="count" />')
  98. .html('/<span class="total">' + this.slides.length + '</span></span>')
  99. .prepend(this.count);
  100. this.tabs = tabsWrapper.children();
  101. if (this.slides.length > 1) {
  102. this.slides.each(function (i) {
  103. const s = $(this);
  104. const li = $('<li/>').appendTo(tabsWrapper);
  105. var button = $('<button />').addClass('btn').appendTo(li);
  106. const span = $('<span/>').appendTo(button);
  107. span.text(s.data('title') ? s.data('title') : i + 1);
  108. });
  109. this.tabs = tabsWrapper.children();
  110. tabsWrapper.find('button').on('click', (e) => {
  111. e.preventDefault();
  112. this.sectionInitialClicked = true;
  113. this.changeSlide($(e.currentTarget).parent().index());
  114. });
  115. this.prevButton.on('click', (e) => {
  116. e.preventDefault();
  117. this.sectionInitialClicked = true;
  118. this.onPrev();
  119. });
  120. this.nextButton.on('click', (e) => {
  121. e.preventDefault();
  122. this.sectionInitialClicked = true;
  123. this.onNext();
  124. });
  125. this.controls = $('<div class="controls" />')
  126. .append(this.prevButton)
  127. .append(tabsWrapper)
  128. .append(countWrapper)
  129. .append(this.nextButton)
  130. .appendTo(this.section);
  131. } else {
  132. this.controls = $('<div class="controls" />')
  133. .append(countWrapper)
  134. .appendTo(this.section);
  135. }
  136. }
  137. changeSlide(index, offset, transition = true) {
  138. const t = this;
  139. const prevIndex = index === 0 ? this.slides.length - 1 : index - 1;
  140. const nextIndex = index === this.slides.length - 1 ? 0 : index + 1;
  141. const allWidth = this.settings.infinite
  142. ? this.slideWrapper.width() * this.slides.length
  143. : 0;
  144. if (!offset) {
  145. offset = 0;
  146. }
  147. if (index === this.currentSlide && this.slideOverflow.outerHeight() === 0) {
  148. transition = false;
  149. }
  150. t.positionAnimation = {
  151. x: this.slideWrapper.position()?.left ?? 0
  152. }
  153. let target = (index + offset) / -100 * this.slideWrapper.width();
  154. let time = Math.round(Math.abs(this.positionAnimation.x - target) / 5 + 200);
  155. if (time > 500) {
  156. time = 500;
  157. }
  158. if (!transition) {
  159. time = 0;
  160. }
  161. t.slideOverflow.css({
  162. 'transition-duration': time + 'ms', 'height': this.slideOverflow.outerHeight() + 'px'
  163. });
  164. t.slideWrapper.find('.current').removeClass('current');
  165. t.slideWrapper.find('[data-index="' + index + '"]').addClass('current');
  166. t.slideWrapper.find('.is-prev').removeClass('is-prev');
  167. t.slideWrapper.find('[data-index="' + prevIndex + '"]').addClass('is-prev');
  168. t.slideWrapper.find('.is-next').removeClass('is-next');
  169. t.slideWrapper.find('[data-index="' + nextIndex + '"]').addClass('is-next');
  170. if (target > 0) {
  171. target = target - allWidth;
  172. this.positionAnimation.x = this.positionAnimation.x - allWidth;
  173. }
  174. if (target <= allWidth * -1 + 1) {
  175. target = target + allWidth;
  176. this.positionAnimation.x = this.positionAnimation.x + allWidth;
  177. }
  178. requestAnimationFrame(() => {
  179. this.slideOverflow.css({
  180. 'height': this.slides.eq(index).outerHeight() + 'px'
  181. });
  182. })
  183. $(this.positionAnimation).animate({
  184. x: target
  185. }, {
  186. duration: time, easing: 'easeOutCubic', step: (now) => {
  187. this.slideWrapper.css({'transform': 'translate3d(' + Math.round(now) + 'px, 0, 0)'});
  188. }, complete: () => {
  189. requestAnimationFrame(() => {
  190. this.slideWrapper.css({'transform': 'translate3d(' + (index * -1) + '%, 0, 0)'});
  191. this.slideOverflow.css('height', 'auto');
  192. });
  193. }
  194. })
  195. this.tabs.eq(index).addClass('active').siblings('.active').removeClass('active');
  196. this.count.text(index + 1);
  197. if (this.settings.autoplay && !this.isHovered) {
  198. this.handleAutoplay();
  199. }
  200. if (offset !== 0) {
  201. setTimeout(() => {
  202. this.slideWrapper.addClass('no-transition');
  203. this.slideWrapper.css({'transform': 'translate3d(' + (index * -1) + '%, 0, 0)'});
  204. setTimeout(() => {
  205. this.slideWrapper.removeClass('no-transition');
  206. }, 20);
  207. }, this.settings.animationSpeed);
  208. }
  209. this.currentSlide = index;
  210. this.section.trigger('slide-change');
  211. if (this.section.closest('.steps').length && this.sectionInitialClicked) {
  212. this.scrollToTop();
  213. }
  214. }
  215. goTo(index, transition) {
  216. if (transition === false) {
  217. this.slideWrapper.addClass('no-transition');
  218. }
  219. this.sectionInitialClicked = true;
  220. this.changeSlide(index, 0, transition);
  221. setTimeout(() => {
  222. this.slideWrapper.removeClass('no-transition');
  223. }, 20);
  224. }
  225. onNext() {
  226. const nextSlide = this.currentSlide === this.slides.length - 1 ? 0 : this.currentSlide + 1;
  227. const offset = this.settings.infinite && nextSlide === 0 ? this.slides.length : 0;
  228. this.changeSlide(nextSlide, offset);
  229. }
  230. onPrev() {
  231. const prevSlide = this.currentSlide === 0 ? this.slides.length - 1 : this.currentSlide - 1;
  232. const offset = this.settings.infinite && prevSlide === this.slides.length - 1 ? this.slides.length * -1 : 0;
  233. this.changeSlide(prevSlide, offset);
  234. }
  235. initAutoplay() {
  236. this.section.on('mouseenter', () => {
  237. this.isHovered = true;
  238. clearTimeout(this.autoplayTimeout);
  239. this.section.removeClass("btnanimation");
  240. })
  241. this.section.on('mouseleave', () => {
  242. this.isHovered = false;
  243. this.handleAutoplay();
  244. this.section.addClass("btnanimation");
  245. })
  246. this.initScrollCheck();
  247. this.handleAutoplay();
  248. }
  249. handleAutoplay() {
  250. if (this.dragging) {
  251. return false;
  252. }
  253. clearTimeout(this.autoplayTimeout);
  254. if (this.settings.autoplaySpeed > 0 && this.settings.autoplay) {
  255. this.autoplayTimeout = setTimeout(() => {
  256. if (this.inViewport) {
  257. this.onNext();
  258. } else {
  259. this.handleAutoplay();
  260. }
  261. }, this.settings.autoplaySpeed);
  262. }
  263. }
  264. initScrollCheck() {
  265. window.addEventListener("scroll", () => {
  266. window.requestAnimationFrame(() => {
  267. this.scrollCheck();
  268. })
  269. }, {passive: true});
  270. window.requestAnimationFrame(() => {
  271. this.scrollCheck();
  272. })
  273. }
  274. scrollCheck() {
  275. const w = $(window);
  276. const offset = this.section.offset();
  277. if (!offset) return;
  278. if (w.scrollTop() + w.height() - 200 > this.section.offset().top && w.scrollTop() < this.section.offset().top + this.section.outerHeight() / 2) {
  279. if (!this.inViewport) {
  280. this.inViewport = true;
  281. this.section.addClass('in-viewport').trigger('in-viewport');
  282. }
  283. } else if (this.inViewport) {
  284. this.inViewport = false;
  285. this.section.removeClass('in-viewport');
  286. }
  287. }
  288. initHammerDragging() {
  289. const hammer = new Hammer(this.slideWrapper.get(0));
  290. let sliderWidth = 0;
  291. let startValue = 0;
  292. this.dragging = false;
  293. this.slideWrapper.find('img, a').attr('draggable', 'false').attr('ondragstart', 'return false;');
  294. hammer.on('panstart', (e) => {
  295. sliderWidth = this.slideOverflow.width();
  296. // Disable click on drag
  297. this.slideWrapper.find('a').css('pointer-events', 'none');
  298. this.dragging = true;
  299. startValue = this.slideWrapper.position().left;
  300. $(this.positionAnimation).stop();
  301. requestAnimationFrame(() => {
  302. $('html').addClass('slider-dragging');
  303. })
  304. if (e.center.x !== 0 && e.center.y !== 0) {
  305. clearTimeout(this.autoplayTimeout);
  306. }
  307. });
  308. hammer.on('pan', (e) => {
  309. if (e.center.x !== 0 && e.center.y !== 0) {
  310. const transformX = Math.round(startValue + e.deltaX);
  311. this.slideWrapper.css({'transform': 'translate3d(' + transformX + 'px, 0, 0)'});
  312. }
  313. });
  314. hammer.on('panend', (e) => {
  315. this.slideWrapper.find('a').addClass('dragging');
  316. this.dragging = false;
  317. requestAnimationFrame(() => {
  318. $('html').removeClass('slider-dragging');
  319. if (e.deltaX > sliderWidth / 5) {
  320. this.onPrev();
  321. } else if (e.deltaX < sliderWidth / -5) {
  322. this.onNext();
  323. } else {
  324. this.changeSlide(this.currentSlide);
  325. }
  326. });
  327. // Enable click on drag
  328. this.slideWrapper.find('a').css('pointer-events', 'all');
  329. });
  330. }
  331. initTabbing() {
  332. $(window).keydown((e) => {
  333. const code = (e.keyCode ? e.keyCode : e.which);
  334. if (code === 9 && this.slideWrapper.find(':focus').length) {
  335. this.tabScrollActive = true;
  336. this.resetTabScroll();
  337. }
  338. })
  339. $(window).keyup((e) => {
  340. const code = (e.keyCode ? e.keyCode : e.which);
  341. if (code === 9 && this.slideWrapper.find(':focus').length) {
  342. const slide = this.section.find(':focus').closest('.slide');
  343. this.slideOverflow.scrollLeft(0).scrollTop(0).find('.outer, .text-box').scrollLeft(0).scrollTop(0);
  344. this.goTo(parseInt(slide.attr('data-index')), true);
  345. this.tabScrollActive = false;
  346. }
  347. });
  348. }
  349. loadImage(index) {
  350. const t = this;
  351. if (!index) {
  352. index = this.currentSlide;
  353. }
  354. if (t.slides.eq(index).hasClass('preload') && !t.slides.eq(index).find('img').length) {
  355. const ib = t.slides.eq(index).find('.image-box');
  356. const img = $('<img/>').one('load', function (e) {
  357. const loadedImage = $(this).removeClass('loading');
  358. const slide = loadedImage.closest('.slide').removeClass('preload');
  359. const index = parseInt(slide.attr('data-index'));
  360. const nextItem = t.nextWrapper.find('[data-index="' + index + '"]').removeClass('preload').find('.image-box');
  361. nextItem.append(loadedImage.clone());
  362. nextItem.find('img').attr('alt', ib.attr('data-alt') || '');
  363. // Copyright für Next-Wrapper
  364. if (ib.attr('data-copyright')) {
  365. $('<span class="copyright" aria-hidden="true">').html(ib.attr('data-copyright')).appendTo(nextItem);
  366. $('<span class="sr-only">').html(ib.attr('data-copyright')).appendTo(nextItem);
  367. }
  368. const prevItem = t.prevWrapper.find('[data-index="' + index + '"]').removeClass('preload').find('.image-box');
  369. prevItem.append(loadedImage.clone());
  370. prevItem.find('img').attr('alt', ib.attr('data-alt') || '');
  371. // Copyright für Prev-Wrapper
  372. if (ib.attr('data-copyright')) {
  373. $('<span class="copyright">').html(ib.attr('data-copyright')).appendTo(prevItem);
  374. }
  375. //t.nextWrapper.find('[data-index="' + index + '"]').removeClass('preload').find('.image-box').append(loadedImage.clone());
  376. //t.prevWrapper.find('[data-index="' + index + '"]').removeClass('preload').find('.image-box').append(loadedImage.clone());
  377. });
  378. img.attr('src', ib.attr('data-src')).attr('alt', ib.attr('data-alt') || '').appendTo(ib);
  379. img.attr('data-download', ib.attr('data-download'));
  380. img.attr('draggable', 'false').attr('ondragstart', 'return false;');
  381. // Copyright für das Hauptbild hinzufügen
  382. if (ib.attr('data-copyright')) {
  383. $('<span class="copyright">').html(ib.attr('data-copyright')).appendTo(ib);
  384. }
  385. }
  386. }
  387. resetTabScroll() {
  388. this.slideOverflow.scrollLeft(0).scrollTop(0).find('.outer, .text-box').scrollLeft(0).scrollTop(0);
  389. if (this.tabScrollActive) {
  390. window.requestAnimationFrame(() => {
  391. this.resetTabScroll();
  392. })
  393. }
  394. }
  395. scrollToTop() {
  396. const target = this.section.closest('.steps').offset().top - 120;
  397. const time = 600;
  398. $('html, body').animate({'scrollTop': target}, time, 'easeOutQuad');
  399. };
  400. setupSteps() {
  401. this.nextButton.text('Weiter').addClass('btn').addClass('has-icon').addClass('icon-right').addClass('icon-pfeil-rechts');
  402. }
  403. }
  404. export default IHKSlider;
  405. $('body').on('ihk-init dynamic-component-loaded gfi-dynamic-init', () => {
  406. $('.rotation .slider:not(.initiated), .steps .slider:not(.initiated)').each((i, el) => {
  407. const selector = $(el);
  408. if (!selector.find('dynamic-content').length) {
  409. new IHKSlider(selector);
  410. }
  411. })
  412. /*
  413. let slidersSelector;
  414. if (e.type === "ihk-init") {
  415. slidersSelector = $('.rotation .slider:not(.initiated):not(:has(dynamic-content)), .steps .slider:not(.initiated):not(:has(dynamic-content))')
  416. } else {
  417. slidersSelector = $('.rotation .slider:not(.initiated), .steps .slider:not(.initiated)');
  418. }
  419. slidersSelector.each(function () {
  420. new Slider($(this));
  421. });
  422. */
  423. });