main2.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. /**
  2. * main.js
  3. * http://www.codrops.com
  4. *
  5. * Licensed under the MIT license.
  6. * http://www.opensource.org/licenses/mit-license.php
  7. *
  8. * Copyright 2016, Codrops
  9. * http://www.codrops.com
  10. */
  11. ;(function(window) {
  12. 'use strict';
  13. // Helper vars and functions.
  14. function extend( a, b ) {
  15. for( var key in b ) {
  16. if( b.hasOwnProperty( key ) ) {
  17. a[key] = b[key];
  18. }
  19. }
  20. return a;
  21. }
  22. // Equation of a line.
  23. function lineEq(y2, y1, x2, x1, currentVal) {
  24. // y = mx + b
  25. var m = (y2 - y1) / (x2 - x1),
  26. b = y1 - m * x1;
  27. return m * currentVal + b;
  28. }
  29. // From http://www.quirksmode.org/js/events_properties.html#position
  30. function getMousePos(e) {
  31. var posx = 0;
  32. var posy = 0;
  33. if (!e) var e = window.event;
  34. if (e.pageX || e.pageY) {
  35. posx = e.pageX;
  36. posy = e.pageY;
  37. }
  38. else if (e.clientX || e.clientY) {
  39. posx = e.clientX + document.body.scrollLeft
  40. + document.documentElement.scrollLeft;
  41. posy = e.clientY + document.body.scrollTop
  42. + document.documentElement.scrollTop;
  43. }
  44. return {
  45. x : posx,
  46. y : posy
  47. }
  48. }
  49. // Detect mobile. From: http://stackoverflow.com/a/11381730/989439
  50. function mobilecheck() {
  51. var check = false;
  52. (function(a){if(/(android|ipad|playbook|silk|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
  53. return check;
  54. }
  55. // The Day obj.
  56. function Day(options) {
  57. this.options = extend({}, this.options);
  58. extend(this.options, options);
  59. this.number = this.options.number;
  60. this.color = this.options.color;
  61. this.previewTitle = this.options.previewTitle;
  62. this.isActive = !this.options.inactive;
  63. this._layout();
  64. }
  65. Day.prototype.options = {
  66. number: 0,
  67. color: '#f1f1f1',
  68. previewTitle: '',
  69. inactive: false
  70. };
  71. // Build the 3D cube element.
  72. Day.prototype._layout = function() {
  73. /* The Cube structure:
  74. <div class="cube">
  75. <div class="cube__side cube__side--front"></div>
  76. <div class="cube__side cube__side--back"></div>
  77. <div class="cube__side cube__side--right"></div>
  78. <div class="cube__side cube__side--left"></div>
  79. <div class="cube__side cube__side--top"></div>
  80. <div class="cube__side cube__side--bottom"></div>
  81. </div>
  82. */
  83. this.cube = document.createElement('div');
  84. this.cube.className = this.isActive ? 'cube' : 'cube cube--inactive';
  85. this.cube.innerHTML = '<div class="cube__side cube__side--back"></div><div class="cube__side cube__side--left"></div><div class="cube__side cube__side--right"></div><div class="cube__side cube__side--bottom"></div><div class="cube__side cube__side--top"></div><div class="cube__side cube__side--front"><div class="cube__number">' + (this.number+1) + '</div></div>';
  86. this.currentTransform = {translateZ: 0, rotateX: 0, rotateY: 0};
  87. };
  88. Day.prototype._rotate = function(ev) {
  89. anime.remove(this.cube);
  90. var dir = this._getDirection(ev),
  91. type = ev.type.toLowerCase() === 'mouseenter' ? 1 : -1,
  92. animationSettings = {
  93. targets: this.cube,
  94. duration: 1000,
  95. easing: 'easeOutElastic',
  96. translateZ: type === 1 ? 100 : 0
  97. };
  98. switch(dir) {
  99. case 0 : // from/to top
  100. animationSettings.rotateX = type === 1 ? -180 : 0;
  101. animationSettings.rotateY = 0;
  102. break;
  103. case 1 : // from/to right
  104. animationSettings.rotateY = type === 1 ? -180 : 0;
  105. animationSettings.rotateX = 0;
  106. break;
  107. case 2 : // from/to bottom
  108. animationSettings.rotateX = type === 1 ? 180 : 0;
  109. animationSettings.rotateY = 0;
  110. break;
  111. case 3 : // from/to left
  112. animationSettings.rotateY = type === 1 ? 180 : 0;
  113. animationSettings.rotateX = 0;
  114. break;
  115. };
  116. this.currentTransform = {
  117. translateZ: animationSettings.translateZ,
  118. rotateX: animationSettings.rotateX,
  119. rotateY: animationSettings.rotateY
  120. };
  121. anime(animationSettings);
  122. };
  123. Day.prototype._setContentTitleFx = function(contentTitleEl) {
  124. this.titlefx = new TextFx(contentTitleEl);
  125. this.titlefxSettings = {
  126. in: {
  127. duration: 800,
  128. delay: 650,
  129. easing: 'easeOutExpo',
  130. opacity: {
  131. duration: 200,
  132. value: [0,1],
  133. easing:'linear'
  134. },
  135. translateY: function(el, index) {
  136. var p = el.parentNode,
  137. lastElOffW = p.lastElementChild.offsetWidth,
  138. firstElOffL = p.firstElementChild.offsetLeft,
  139. w = p.offsetWidth - lastElOffW - firstElOffL - (p.offsetWidth - lastElOffW - p.lastElementChild.offsetLeft),
  140. tyVal = lineEq(0, 200, firstElOffL + w/2, firstElOffL, el.offsetLeft);
  141. return [Math.abs(tyVal)+50,0];
  142. },
  143. rotateZ: function(el, index) {
  144. var p = el.parentNode,
  145. lastElOffW = p.lastElementChild.offsetWidth,
  146. firstElOffL = p.firstElementChild.offsetLeft,
  147. w = p.offsetWidth - lastElOffW - p.firstElementChild.offsetLeft - (p.offsetWidth - lastElOffW - p.lastElementChild.offsetLeft),
  148. rz = lineEq(90, -90,firstElOffL + w, firstElOffL, el.offsetLeft);
  149. return [rz,0];
  150. }
  151. },
  152. out: {
  153. duration: 800,
  154. delay: 400,
  155. easing: 'easeInExpo',
  156. opacity: 0,
  157. translateY: 350
  158. }
  159. };
  160. };
  161. // From: https://codepen.io/noeldelgado/pen/pGwFx?editors=0110 by Noel Delgado (@noeldelgado).
  162. Day.prototype._getDirection = function(ev) {
  163. var obj = this.cube.querySelector('.cube__side--front'),
  164. w = obj.offsetWidth,
  165. h = obj.offsetHeight,
  166. bcr = obj.getBoundingClientRect(),
  167. x = (ev.pageX - (bcr.left + window.pageXOffset) - (w / 2) * (w > h ? (h / w) : 1)),
  168. y = (ev.pageY - (bcr.top + window.pageYOffset) - (h / 2) * (h > w ? (w / h) : 1)),
  169. d = Math.round( Math.atan2(y, x) / 1.57079633 + 5 ) % 4;
  170. return d;
  171. };
  172. // The Calendar obj.
  173. function Calendar(el) {
  174. this.el = el;
  175. this.calendarDays = [].slice.call(this.el.querySelectorAll('.cube'));
  176. // Let´s build the days/cubes structure.
  177. this.cubes = document.createElement('div');
  178. this.cubes.className = 'cubes';
  179. this.el.appendChild(this.cubes);
  180. // Array of days (each day is represented by a 3D Cube)
  181. this.days = [];
  182. var self = this;
  183. this.calendarDays.forEach(function(d, pos) {
  184. // Get the bg color defined in the data-bg-color of each division.
  185. var day = new Day({
  186. number: pos,
  187. color: d.getAttribute('data-bg-color') || '#f1f1f1',
  188. previewTitle: d.getAttribute('data-title') || '',
  189. inactive: d.hasAttribute('data-inactive')
  190. }),
  191. content = contents[pos];
  192. if( content !== undefined ) {
  193. var contentTitle = contents[pos].querySelector('.content__title');
  194. day._setContentTitleFx(contentTitle);
  195. }
  196. self.days.push(day);
  197. self.cubes.appendChild(day.cube);
  198. self.el.removeChild(d);
  199. self._initDayEvents(day);
  200. });
  201. // structure to show each day preview (day + title + subtitle etc, when the user hovers one day/cube).
  202. this.dayPreview = document.createElement('h2');
  203. this.dayPreview.className = 'title';
  204. this.el.appendChild(this.dayPreview);
  205. this._initEvents();
  206. }
  207. Calendar.prototype._initEvents = function() {
  208. var self = this;
  209. // Mousemove event / tilt functionality
  210. if( settings.tilt ) {
  211. this.mousemoveFn = function(ev) {
  212. if( self.isOpen ) {
  213. return false;
  214. };
  215. requestAnimationFrame(function() {
  216. // Mouse position relative to the document.
  217. var mousepos = getMousePos(ev);
  218. self._rotateCalendar(mousepos);
  219. });
  220. };
  221. this.handleOrientation = function() {
  222. if( self.isOpen ) {
  223. return false;
  224. };
  225. var y = event.gamma;
  226. // To make computation easier we shift the range of x and y to [0,180]
  227. y += 90;
  228. requestAnimationFrame(function() {
  229. // Transform values.
  230. var movement = {ry:40},
  231. rotY = 2*movement.ry / 180 * y - movement.ry;
  232. self.cubes.style.WebkitTransform = self.cubes.style.transform = 'rotate3d(0,-1,0,' + rotY + 'deg)';
  233. });
  234. };
  235. if( isMobile ) {
  236. window.addEventListener('deviceorientation', this.handleOrientation);
  237. }
  238. else {
  239. document.addEventListener('mousemove', this.mousemoveFn);
  240. }
  241. }
  242. this.backToCalendarFn = function(ev) {
  243. // If the calendar is currently animating then do nothing.
  244. if( !self.isOpen || self.isAnimating ) {
  245. return false;
  246. }
  247. self.isAnimating = true;
  248. self._hidePreviewTitle();
  249. self._hideContent();
  250. // Show the main container
  251. anime({
  252. targets: self.el,
  253. duration: 1400,
  254. easing: 'easeInOutExpo',
  255. opacity: 1,
  256. complete: function() {
  257. self.isOpen = false;
  258. self.isAnimating = false;
  259. }
  260. });
  261. var win = {w:window.innerWidth, h:window.innerHeight};
  262. for(var i = 0, totalDays = self.days.length; i < totalDays; ++i) {
  263. var day = self.days[i];
  264. if( self.currentDayIdx === i ) {
  265. anime({
  266. targets: day.cube,
  267. duration: 1200,
  268. delay: 1000,
  269. easing: 'easeOutExpo',
  270. scale: [0,1],
  271. translateY: 0,
  272. translateZ: [-1500,0],
  273. rotateX: 0,
  274. rotateY: 0
  275. });
  276. day.isRotated = false;
  277. }
  278. else {
  279. anime({
  280. targets: day.cube,
  281. duration: 700,
  282. delay: function(el, index) {
  283. return 1000 + anime.random(0,100);
  284. },
  285. easing: 'easeOutExpo',
  286. translateX: [day.currentTransform.translateX < 0 ? -win.w : win.w,0],
  287. translateY: [day.currentTransform.translateY < 0 ? -win.h : win.h,0],
  288. rotateY: [day.currentTransform.rotateY,0]
  289. });
  290. }
  291. }
  292. };
  293. backCtrl.addEventListener('click', this.backToCalendarFn);
  294. };
  295. Calendar.prototype._initDayEvents = function(day) {
  296. var self = this;
  297. // Day/Cube mouseenter/mouseleave event.
  298. var instance = day;
  299. if( !isMobile ) {
  300. instance.mouseenterFn = function(ev) {
  301. if( instance.isRotated || self.isOpen ) {
  302. return false;
  303. };
  304. clearTimeout(colortimeout);
  305. instance.rotatetimeout = setTimeout(function() {
  306. colortimeout = setTimeout(function() { self._changeBGColor(instance.color); }, 30);
  307. instance._rotate(ev);
  308. self._showPreviewTitle(instance.previewTitle, instance.number);
  309. instance.isRotated = true;
  310. }, 30);
  311. };
  312. instance.mouseleaveFn = function(ev) {
  313. if( self.isOpen ) {
  314. return false;
  315. };
  316. clearTimeout(instance.rotatetimeout);
  317. clearTimeout(colortimeout);
  318. if( instance.isRotated ) {
  319. colortimeout = setTimeout(function() { self._resetBGColor(); }, 35);
  320. instance._rotate(ev);
  321. self._hidePreviewTitle();
  322. instance.isRotated = false;
  323. }
  324. };
  325. }
  326. // Day/Cube click event.
  327. instance.clickFn = function(ev) {
  328. // If the day is inactive or if the calendar is currently animating then do nothing.
  329. if( !instance.isActive || self.isAnimating ) {
  330. return false;
  331. }
  332. self.isAnimating = true;
  333. self.isOpen = true;
  334. self.currentDayIdx = instance.number;
  335. // Hide the main container
  336. anime({
  337. targets: self.el,
  338. duration: 1400,
  339. easing: 'easeInOutExpo',
  340. opacity: 0,
  341. complete: function() {
  342. self.isAnimating = false;
  343. }
  344. });
  345. var win = {w:window.innerWidth, h:window.innerHeight};
  346. for(var i = 0, totalDays = self.days.length; i < totalDays; ++i) {
  347. var day = self.days[i];
  348. if( self.currentDayIdx === i ) {
  349. anime({
  350. targets: day.cube,
  351. duration: 1000,
  352. easing: 'easeInOutExpo',
  353. scale: 0,
  354. translateZ: -1500,
  355. rotateX: day.currentTransform.rotateX,
  356. rotateY: day.currentTransform.rotateY
  357. });
  358. self._showContent(instance);
  359. }
  360. else {
  361. var bcr = day.cube.getBoundingClientRect(),
  362. l = bcr.left + window.pageXOffset,
  363. t = bcr.top + window.pageYOffset;
  364. anime({
  365. targets: day.cube,
  366. duration: 1200,
  367. easing: 'easeInOutExpo',
  368. translateX: function(el, index) {
  369. day.currentTransform.translateX = l <= win.w/2 && t <= win.h/2 || l <= win.w/2 && t > win.h/2 ? anime.random(-800,-400) : anime.random(800,400);
  370. return day.currentTransform.translateX;
  371. },
  372. translateY: function(el, index) {
  373. day.currentTransform.translateY = l <= win.w/2 && t <= win.h/2 || l > win.w/2 && t <= win.h/2 ? anime.random(-800,400) : anime.random(800,400);
  374. return day.currentTransform.translateY;
  375. },
  376. rotateY: function(el, index) {
  377. day.currentTransform.rotateY = l <= win.w/2 && t <= win.h/2 || l <= win.w/2 && t > win.h/2 ? anime.random(90,40) : anime.random(-90,-40);
  378. return day.currentTransform.rotateY;
  379. }
  380. });
  381. }
  382. }
  383. };
  384. instance.cube.querySelector('.cube__side--front').addEventListener('mouseenter', instance.mouseenterFn);
  385. instance.cube.addEventListener('mouseleave', instance.mouseleaveFn);
  386. instance.cube.addEventListener('click', instance.clickFn);
  387. instance.cube.addEventListener('mousedown', function() {
  388. clearTimeout(instance.rotatetimeout );
  389. });
  390. };
  391. Calendar.prototype._rotateCalendar = function(mousepos) {
  392. // Transform values.
  393. var movement = {rx:3, ry:3},
  394. rotX = 2 * movement.rx / this.cubes.offsetHeight * mousepos.y - movement.rx,
  395. rotY = 2 * movement.ry / this.cubes.offsetWidth * mousepos.x - movement.ry;
  396. this.cubes.style.WebkitTransform = this.cubes.style.transform = 'rotate3d(-1,0,0,' + rotX + 'deg) rotate3d(0,1,0,' + rotY + 'deg)';
  397. };
  398. Calendar.prototype._showPreviewTitle = function(text, number) {
  399. this.dayPreview.innerHTML = text;
  400. this.dayPreview.setAttribute('data-number', parseInt(number+1));
  401. this.txtfx = new TextFx(this.dayPreview);
  402. this.txtfx.hide();
  403. this.dayPreview.style.opacity = 1;
  404. this.txtfx.show({
  405. in: {
  406. duration: 800,
  407. delay: function(el, index, total) { return index*40; },
  408. easing: 'easeOutElastic',
  409. opacity: 1,
  410. translateY: function(el, index) {
  411. return index%2 === 0 ? [-25, 0] : [25, 0];
  412. },
  413. rotateZ: [45,0]
  414. }
  415. });
  416. };
  417. Calendar.prototype._hidePreviewTitle = function() {
  418. var self = this;
  419. if( this.txtfx ) {
  420. this.txtfx.hide();
  421. }
  422. this.dayPreview.style.opacity = 0;
  423. };
  424. Calendar.prototype._showContent = function(day) {
  425. // The content box for the clicked day.
  426. var content = contents[this.currentDayIdx],
  427. title = content.querySelector('.content__title'),
  428. description = content.querySelector('.content__description'),
  429. meta = content.querySelector('.content__meta');
  430. content.classList.add('content__block--current');
  431. day.titlefx.hide();
  432. day.titlefx.show(day.titlefxSettings);
  433. anime({
  434. targets: [description, meta],
  435. duration: 800,
  436. delay: function(el, index) { return index === 0 ? 900 : 1000 },
  437. easing: 'easeOutExpo',
  438. opacity: [0,1],
  439. translateY: [100,0]
  440. });
  441. anime({
  442. targets: backCtrl,
  443. duration: 1000,
  444. delay: 800,
  445. easing: 'easeOutExpo',
  446. opacity: [0,1],
  447. translateY: [50,0]
  448. });
  449. contentNumber.innerHTML = this.currentDayIdx + 1;
  450. anime({
  451. targets: contentNumber,
  452. duration: 500,
  453. delay: 800,
  454. easing: 'easeOutExpo',
  455. opacity: [0,1],
  456. translateX: ['-50%','-50%'],
  457. translateY: ['-100%','-50%']
  458. });
  459. };
  460. Calendar.prototype._hideContent = function() {
  461. var day = this.days[this.currentDayIdx],
  462. // The content box for the clicked day.
  463. content = contents[this.currentDayIdx],
  464. // The content title, description and meta for this day.
  465. title = content.querySelector('.content__title'),
  466. description = content.querySelector('.content__description'),
  467. meta = content.querySelector('.content__meta');
  468. // The content number placeholder animation.
  469. anime({
  470. targets: contentNumber,
  471. duration: 500,
  472. easing: 'easeInExpo',
  473. opacity: 0,
  474. translateX: ['-50%','-50%'],
  475. translateY: ['-50%','100%']
  476. });
  477. // The back button animation.
  478. anime({
  479. targets: backCtrl,
  480. duration: 1000,
  481. easing: 'easeInExpo',
  482. opacity: 0,
  483. translateY: 50
  484. });
  485. // The description and meta animation.
  486. anime({
  487. targets: [description, meta],
  488. duration: 800,
  489. delay: function(el, index) { return index === 0 ? 150 : 0 },
  490. easing: 'easeInExpo',
  491. opacity: [1,0],
  492. translateY: [0,200]
  493. });
  494. // The content title animation.
  495. day.titlefx.hide(day.titlefxSettings, function() {
  496. content.classList.remove('content__block--current');
  497. });
  498. };
  499. Calendar.prototype._changeBGColor = function(color) {
  500. bgEl.style.backgroundColor = color;
  501. };
  502. Calendar.prototype._resetBGColor = function() {
  503. bgEl.style.backgroundColor = defaultBgColor;
  504. };
  505. // Snow obj. Based on // https://gist.github.com/OmShiv/4368164.
  506. function Snow() {
  507. this.canvas = document.createElement('canvas');
  508. this.canvas.id = 'snow';
  509. this.canvas.className = 'background';
  510. this.canvas.style.background = defaultBgColor;
  511. document.body.insertBefore(this.canvas, document.body.firstElementChild);
  512. this.flakes = [];
  513. this.ctx = this.canvas.getContext('2d');
  514. this.flakeCount = 300;
  515. this.mX = -100,
  516. this.mY = -100
  517. this.width = this.canvas.width = window.innerWidth;
  518. this.height = this.canvas.height = window.innerHeight;
  519. this._init();
  520. }
  521. Snow.prototype._init = function() {
  522. var self = this;
  523. window.addEventListener('resize', function() {
  524. self.width = self.canvas.width = window.innerWidth;
  525. self.height = self.canvas.height = window.innerHeight;
  526. });
  527. for(var i = 0; i < this.flakeCount; ++i) {
  528. var x = Math.floor(Math.random() * this.width),
  529. y = Math.floor(Math.random() * this.height),
  530. size = (Math.random()*3.5) + .5,
  531. speed = size*.5,
  532. opacity = (Math.random() * 0.5) + 0.1;
  533. this.flakes.push({
  534. speed: speed,
  535. velY: speed,
  536. velX: 0,
  537. x: x,
  538. y: y,
  539. size: size,
  540. stepSize: (Math.random()) / 30,
  541. step: 0,
  542. opacity: opacity
  543. });
  544. }
  545. this._snow();
  546. };
  547. Snow.prototype._snow = function() {
  548. this.ctx.clearRect(0, 0, this.width, this.height);
  549. for(var i = 0; i < this.flakeCount; ++i) {
  550. var flake = this.flakes[i],
  551. x = this.mX,
  552. y = this.mY,
  553. minDist = 150,
  554. x2 = flake.x,
  555. y2 = flake.y,
  556. dist = Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)),
  557. dx = x2 - x,
  558. dy = y2 - y;
  559. if( dist < minDist ) {
  560. var force = minDist / (dist * dist),
  561. xcomp = (x - x2) / dist,
  562. ycomp = (y - y2) / dist,
  563. deltaV = force / 2;
  564. flake.velX -= deltaV * xcomp;
  565. flake.velY -= deltaV * ycomp;
  566. }
  567. else {
  568. flake.velX *= .98;
  569. if( flake.velY <= flake.speed ) {
  570. flake.velY = flake.speed
  571. }
  572. flake.velX += Math.cos(flake.step += .05) * flake.stepSize;
  573. }
  574. this.ctx.fillStyle = "rgba(255,255,255," + flake.opacity + ")";
  575. flake.y += flake.velY;
  576. flake.x += flake.velX;
  577. if( flake.y >= this.height || flake.y <= 0 ) {
  578. this._reset(flake);
  579. }
  580. if( flake.x >= this.width || flake.x <= 0 ) {
  581. this._reset(flake);
  582. }
  583. this.ctx.beginPath();
  584. this.ctx.arc(flake.x, flake.y, flake.size, 0, Math.PI * 2);
  585. this.ctx.fill();
  586. }
  587. requestAnimationFrame(this._snow.bind(this));
  588. };
  589. Snow.prototype._reset = function(flake) {
  590. flake.x = Math.floor(Math.random() * this.width);
  591. flake.y = 0;
  592. flake.size = (Math.random() * 3.5) + .5;
  593. flake.speed = flake.size*.5,
  594. flake.velY = flake.speed;
  595. flake.velX = 0;
  596. flake.opacity = (Math.random() * 0.5) + 0.1;
  597. };
  598. var calendarEl = document.querySelector('.calendar'),
  599. calendarDays = [].slice.call(calendarEl.children),
  600. settings = {
  601. snow: false,
  602. tilt: true
  603. },
  604. bgEl = document.body,
  605. defaultBgColor = bgEl.style.backgroundColor,
  606. colortimeout,
  607. contentEl = document.querySelector('.content'),
  608. contents = contentEl.querySelectorAll('.content__block'),
  609. backCtrl = contentEl.querySelector('.btn-back'),
  610. contentNumber = contentEl.querySelector('.content__number'),
  611. isMobile = mobilecheck();
  612. function init() {
  613. layout();
  614. }
  615. function layout() {
  616. new Calendar(calendarEl);
  617. // If settings.snow === true then create the canvas element for the snow effect.
  618. if( settings.snow ) {
  619. var snow = new Snow();
  620. bgEl = snow.canvas;
  621. }
  622. }
  623. init();
  624. })(window);