main.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  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. // From http://www.quirksmode.org/js/events_properties.html#position
  23. function getMousePos(e) {
  24. var posx = 0;
  25. var posy = 0;
  26. if (!e) var e = window.event;
  27. if (e.pageX || e.pageY) {
  28. posx = e.pageX;
  29. posy = e.pageY;
  30. }
  31. else if (e.clientX || e.clientY) {
  32. posx = e.clientX + document.body.scrollLeft
  33. + document.documentElement.scrollLeft;
  34. posy = e.clientY + document.body.scrollTop
  35. + document.documentElement.scrollTop;
  36. }
  37. return {
  38. x : posx,
  39. y : posy
  40. }
  41. }
  42. // Detect mobile. From: http://stackoverflow.com/a/11381730/989439
  43. function mobilecheck() {
  44. var check = false;
  45. (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);
  46. return check;
  47. }
  48. // The Day obj.
  49. function Day(options) {
  50. this.options = extend({}, this.options);
  51. extend(this.options, options);
  52. this.number = this.options.number;
  53. this.color = this.options.color;
  54. this.previewTitle = this.options.previewTitle;
  55. this.isActive = !this.options.inactive;
  56. this._layout();
  57. }
  58. Day.prototype.options = {
  59. number: 0,
  60. color: '#f1f1f1',
  61. previewTitle: '',
  62. inactive: false
  63. };
  64. // Build the 3D cube element.
  65. Day.prototype._layout = function() {
  66. /* The Cube structure:
  67. <div class="cube">
  68. <div class="cube__side cube__side--front"></div>
  69. <div class="cube__side cube__side--back"></div>
  70. <div class="cube__side cube__side--right"></div>
  71. <div class="cube__side cube__side--left"></div>
  72. <div class="cube__side cube__side--top"></div>
  73. <div class="cube__side cube__side--bottom"></div>
  74. </div>
  75. */
  76. this.cube = document.createElement('div');
  77. this.cube.className = this.isActive ? 'cube' : 'cube cube--inactive';
  78. 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>';
  79. this.currentTransform = {translateZ: 0, rotateX: 0, rotateY: 0};
  80. };
  81. Day.prototype._rotate = function(ev) {
  82. anime.remove(this.cube);
  83. var dir = this._getDirection(ev),
  84. type = ev.type.toLowerCase() === 'mouseenter' ? 1 : -1,
  85. animationSettings = {
  86. targets: this.cube,
  87. duration: 500,
  88. easing: 'easeOutExpo'
  89. };
  90. animationSettings.translateZ = {
  91. value: type === 1 ? 100 : 0,
  92. duration: 900,
  93. easing: 'easeOutExpo'
  94. };
  95. switch(dir) {
  96. case 0 : // from/to top
  97. animationSettings.rotateX = type === 1 ? -180 : 0;
  98. animationSettings.rotateY = 0;
  99. break;
  100. case 1 : // from/to right
  101. animationSettings.rotateY = type === 1 ? -180 : 0;
  102. animationSettings.rotateX = 0;
  103. break;
  104. case 2 : // from/to bottom
  105. animationSettings.rotateX = type === 1 ? 180 : 0;
  106. animationSettings.rotateY = 0;
  107. break;
  108. case 3 : // from/to left
  109. animationSettings.rotateY = type === 1 ? 180 : 0;
  110. animationSettings.rotateX = 0;
  111. break;
  112. };
  113. this.currentTransform = {
  114. translateZ: animationSettings.translateZ,
  115. rotateX: animationSettings.rotateX,
  116. rotateY: animationSettings.rotateY
  117. };
  118. anime(animationSettings);
  119. };
  120. Day.prototype._setContentTitleFx = function(contentTitleEl) {
  121. this.titlefx = new TextFx(contentTitleEl);
  122. this.titlefxSettings = {
  123. in: {
  124. duration: 800,
  125. delay: function(el, index) { return 650 + index*10; },
  126. easing: 'easeOutExpo',
  127. opacity: {
  128. duration: 200,
  129. value: [0,1],
  130. easing:'linear'
  131. },
  132. translateY: [100,0],
  133. rotateZ: function(el, index) { return [anime.random(-20,20), 0]; }
  134. },
  135. out: {
  136. duration: 800,
  137. delay: 400,
  138. easing: 'easeInExpo',
  139. opacity: 0,
  140. translateY: 350
  141. }
  142. };
  143. };
  144. // From: https://codepen.io/noeldelgado/pen/pGwFx?editors=0110 by Noel Delgado (@noeldelgado).
  145. Day.prototype._getDirection = function(ev) {
  146. var obj = this.cube.querySelector('.cube__side--front'),
  147. w = obj.offsetWidth,
  148. h = obj.offsetHeight,
  149. bcr = obj.getBoundingClientRect(),
  150. x = (ev.pageX - (bcr.left + window.pageXOffset) - (w / 2) * (w > h ? (h / w) : 1)),
  151. y = (ev.pageY - (bcr.top + window.pageYOffset) - (h / 2) * (h > w ? (w / h) : 1)),
  152. d = Math.round( Math.atan2(y, x) / 1.57079633 + 5 ) % 4;
  153. return d;
  154. };
  155. // The Calendar obj.
  156. function Calendar(el) {
  157. this.el = el;
  158. this.calendarDays = [].slice.call(this.el.querySelectorAll('.cube'));
  159. // Let´s build the days/cubes structure.
  160. this.cubes = document.createElement('div');
  161. this.cubes.className = 'cubes';
  162. this.el.appendChild(this.cubes);
  163. // Array of days (each day is represented by a 3D Cube)
  164. this.days = [];
  165. var self = this;
  166. this.calendarDays.forEach(function(d, pos) {
  167. // Get the bg color defined in the data-bg-color of each division.
  168. var day = new Day({
  169. number: pos,
  170. color: d.getAttribute('data-bg-color') || '#f1f1f1',
  171. previewTitle: d.getAttribute('data-title') || '',
  172. inactive: d.hasAttribute('data-inactive')
  173. }),
  174. content = contents[pos];
  175. if( content !== undefined ) {
  176. var contentTitle = contents[pos].querySelector('.content__title');
  177. day._setContentTitleFx(contentTitle);
  178. }
  179. self.days.push(day);
  180. self.cubes.appendChild(day.cube);
  181. self.el.removeChild(d);
  182. self._initDayEvents(day);
  183. });
  184. // structure to show each day preview (day + title + subtitle etc, when the user hovers one day/cube).
  185. this.dayPreview = document.createElement('h2');
  186. this.dayPreview.className = 'title';
  187. this.el.appendChild(this.dayPreview);
  188. this._initEvents();
  189. }
  190. Calendar.prototype._initEvents = function() {
  191. var self = this;
  192. // Mousemove event / tilt functionality
  193. if( settings.tilt ) {
  194. this.mousemoveFn = function(ev) {
  195. if( self.isOpen ) {
  196. return false;
  197. };
  198. requestAnimationFrame(function() {
  199. // Mouse position relative to the document.
  200. var mousepos = getMousePos(ev);
  201. self._rotateCalendar(mousepos);
  202. });
  203. };
  204. this.handleOrientation = function() {
  205. if( self.isOpen ) {
  206. return false;
  207. };
  208. var y = event.gamma;
  209. // To make computation easier we shift the range of x and y to [0,180]
  210. y += 90;
  211. requestAnimationFrame(function() {
  212. // Transform values.
  213. var movement = {ry:40},
  214. rotY = 2*movement.ry / 180 * y - movement.ry;
  215. self.cubes.style.WebkitTransform = self.cubes.style.transform = 'rotate3d(0,-1,0,' + rotY + 'deg)';
  216. });
  217. };
  218. if( isMobile ) {
  219. window.addEventListener('deviceorientation', this.handleOrientation);
  220. }
  221. else {
  222. document.addEventListener('mousemove', this.mousemoveFn);
  223. }
  224. }
  225. this.backToCalendarFn = function(ev) {
  226. // If the calendar is currently animating then do nothing.
  227. if( !self.isOpen || self.isAnimating ) {
  228. return false;
  229. }
  230. self.isAnimating = true;
  231. self._hidePreviewTitle();
  232. self._hideContent();
  233. // Show the main container
  234. anime({
  235. targets: self.el,
  236. duration: 1200,
  237. easing: 'easeInOutExpo',
  238. opacity: 1,
  239. complete: function() {
  240. self.isOpen = false;
  241. self.isAnimating = false;
  242. }
  243. });
  244. for(var i = 0, totalDays = self.days.length; i < totalDays; ++i) {
  245. var day = self.days[i];
  246. if( self.currentDayIdx === i ) {
  247. anime({
  248. targets: day.cube,
  249. duration: 1200,
  250. delay: 1000,
  251. easing: 'easeOutExpo',
  252. scale: 1,
  253. translateY: 0,
  254. translateZ: [-1500,0],
  255. rotateX: 0,
  256. rotateY: 0
  257. });
  258. day.isRotated = false;
  259. }
  260. else {
  261. anime({
  262. targets: day.cube,
  263. duration: 1200,
  264. delay: 1000,
  265. easing: 'easeOutExpo',
  266. scale: 1,
  267. translateX: 0,
  268. translateY: 0,
  269. translateZ: [3000,0],
  270. rotateY: 0
  271. });
  272. }
  273. }
  274. };
  275. backCtrl.addEventListener('click', this.backToCalendarFn);
  276. };
  277. Calendar.prototype._initDayEvents = function(day) {
  278. var self = this;
  279. // Day/Cube mouseenter/mouseleave event.
  280. var instance = day;
  281. if( !isMobile ) {
  282. instance.mouseenterFn = function(ev) {
  283. if( instance.isRotated || self.isOpen ) {
  284. return false;
  285. };
  286. clearTimeout(colortimeout);
  287. instance.rotatetimeout = setTimeout(function() {
  288. colortimeout = setTimeout(function() { self._changeBGColor(instance.color); }, 30);
  289. instance._rotate(ev);
  290. self._showPreviewTitle(instance.previewTitle, instance.number);
  291. instance.isRotated = true;
  292. }, 30);
  293. };
  294. instance.mouseleaveFn = function(ev) {
  295. if( self.isOpen ) {
  296. return false;
  297. };
  298. clearTimeout(instance.rotatetimeout);
  299. clearTimeout(colortimeout);
  300. if( instance.isRotated ) {
  301. colortimeout = setTimeout(function() { self._resetBGColor(); }, 35);
  302. self._resetBGColor();
  303. instance._rotate(ev);
  304. self._hidePreviewTitle();
  305. instance.isRotated = false;
  306. }
  307. };
  308. }
  309. // Day/Cube click event.
  310. instance.clickFn = function(ev) {
  311. // If the day is inactive or if the calendar is currently animating then do nothing.
  312. if( !instance.isActive || self.isAnimating ) {
  313. return false;
  314. }
  315. self.isAnimating = true;
  316. self.isOpen = true;
  317. self.currentDayIdx = instance.number;
  318. // Hide the main container
  319. anime({
  320. targets: self.el,
  321. duration: 1200,
  322. easing: 'easeInOutExpo',
  323. opacity: 0,
  324. complete: function() {
  325. self.isAnimating = false;
  326. }
  327. });
  328. for(var i = 0, totalDays = self.days.length; i < totalDays; ++i) {
  329. var day = self.days[i];
  330. if( self.currentDayIdx === i ) {
  331. anime({
  332. targets: day.cube,
  333. duration: 600,
  334. delay: 200,
  335. easing: 'easeInExpo',
  336. scale: 1.1,
  337. translateY: -window.innerHeight*2,
  338. translateZ: day.currentTransform.translateZ,
  339. rotateX: day.currentTransform.rotateX,
  340. rotateY: day.currentTransform.rotateY
  341. });
  342. self._showContent(instance);
  343. }
  344. else {
  345. var bcr = day.cube.getBoundingClientRect();
  346. anime({
  347. targets: day.cube,
  348. duration: 1200,
  349. easing: 'easeInOutExpo',
  350. scale: 0.1,
  351. translateX: function(el, index) {
  352. return bcr.left + window.pageXOffset <= window.innerWidth/2 ? anime.random(-800,0) : anime.random(0,800);
  353. },
  354. translateY: function(el, index) {
  355. return bcr.top + window.pageYOffset <= window.innerHeight/2 ? anime.random(-1400,-200) : anime.random(-200,600);
  356. },
  357. translateZ: -1500,
  358. rotateY: function(el, index) {
  359. return bcr.left + window.pageXOffset <= window.innerWidth/2 ? anime.random(-40,0) : anime.random(0,40);
  360. }
  361. });
  362. }
  363. }
  364. };
  365. instance.cube.querySelector('.cube__side--front').addEventListener('mouseenter', instance.mouseenterFn);
  366. instance.cube.addEventListener('mouseleave', instance.mouseleaveFn);
  367. instance.cube.addEventListener('click', instance.clickFn);
  368. instance.cube.addEventListener('mousedown', function() {
  369. clearTimeout(instance.rotatetimeout );
  370. });
  371. };
  372. Calendar.prototype._rotateCalendar = function(mousepos) {
  373. // Transform values.
  374. var movement = {rx:3, ry:3},
  375. rotX = 2 * movement.rx / this.cubes.offsetHeight * mousepos.y - movement.rx,
  376. rotY = 2 * movement.ry / this.cubes.offsetWidth * mousepos.x - movement.ry;
  377. this.cubes.style.WebkitTransform = this.cubes.style.transform = 'rotate3d(-1,0,0,' + rotX + 'deg) rotate3d(0,1,0,' + rotY + 'deg)';
  378. };
  379. Calendar.prototype._showPreviewTitle = function(text, number) {
  380. this.dayPreview.innerHTML = text;
  381. this.dayPreview.setAttribute('data-number', parseInt(number+1));
  382. this.txtfx = new TextFx(this.dayPreview);
  383. this.txtfx.hide();
  384. this.dayPreview.style.opacity = 1;
  385. this.txtfx.show({
  386. in: {
  387. duration: 800,
  388. delay: function(el, index) { return index*20; },
  389. easing: 'easeOutElastic',
  390. opacity: 1,
  391. translateY: function(el, index) {
  392. return index%2 === 0 ? [-25, 0] : [25, 0];
  393. }
  394. }
  395. });
  396. };
  397. Calendar.prototype._hidePreviewTitle = function() {
  398. var self = this;
  399. if( this.txtfx ) {
  400. this.txtfx.hide();
  401. }
  402. this.dayPreview.style.opacity = 0;
  403. };
  404. Calendar.prototype._showContent = function(day) {
  405. // The content box for the clicked day.
  406. var content = contents[this.currentDayIdx],
  407. title = content.querySelector('.content__title'),
  408. description = content.querySelector('.content__description'),
  409. meta = content.querySelector('.content__meta');
  410. content.classList.add('content__block--current');
  411. day.titlefx.hide();
  412. day.titlefx.show(day.titlefxSettings);
  413. anime({
  414. targets: [description, meta],
  415. duration: 800,
  416. delay: function(el, index) { return index === 0 ? 900 : 1000 },
  417. easing: 'easeOutExpo',
  418. opacity: [0,1],
  419. translateY: [100,0]
  420. });
  421. anime({
  422. targets: backCtrl,
  423. duration: 1000,
  424. delay: 800,
  425. easing: 'easeOutExpo',
  426. opacity: [0,1],
  427. translateY: [50,0]
  428. });
  429. contentNumber.innerHTML = this.currentDayIdx + 1;
  430. anime({
  431. targets: contentNumber,
  432. duration: 500,
  433. delay: 800,
  434. easing: 'easeOutExpo',
  435. opacity: [0,1],
  436. translateX: [100,0]
  437. });
  438. };
  439. Calendar.prototype._hideContent = function() {
  440. var day = this.days[this.currentDayIdx],
  441. // The content box for the clicked day.
  442. content = contents[this.currentDayIdx],
  443. // The content title, description and meta for this day.
  444. title = content.querySelector('.content__title'),
  445. description = content.querySelector('.content__description'),
  446. meta = content.querySelector('.content__meta');
  447. // The content number placeholder animation.
  448. anime({
  449. targets: contentNumber,
  450. duration: 500,
  451. easing: 'easeInExpo',
  452. opacity: 0,
  453. translateX: 100
  454. });
  455. // The back button animation.
  456. anime({
  457. targets: backCtrl,
  458. duration: 1000,
  459. easing: 'easeInExpo',
  460. opacity: 0,
  461. translateY: 50
  462. });
  463. // The description and meta animation.
  464. anime({
  465. targets: [description, meta],
  466. duration: 800,
  467. delay: function(el, index) { return index === 0 ? 150 : 0 },
  468. easing: 'easeInExpo',
  469. opacity: [1,0],
  470. translateY: [0,200]
  471. });
  472. // The content title animation.
  473. day.titlefx.hide(day.titlefxSettings, function() {
  474. content.classList.remove('content__block--current');
  475. });
  476. };
  477. Calendar.prototype._changeBGColor = function(color) {
  478. bgEl.style.backgroundColor = color;
  479. };
  480. Calendar.prototype._resetBGColor = function() {
  481. bgEl.style.backgroundColor = defaultBgColor;
  482. };
  483. // Snow obj. Based on // https://gist.github.com/OmShiv/4368164.
  484. function Snow() {
  485. this.canvas = document.createElement('canvas');
  486. this.canvas.id = 'snow';
  487. this.canvas.className = 'background';
  488. this.canvas.style.background = defaultBgColor;
  489. document.body.insertBefore(this.canvas, document.body.firstElementChild);
  490. this.flakes = [];
  491. this.ctx = this.canvas.getContext('2d');
  492. this.flakeCount = 300;
  493. this.mX = -100,
  494. this.mY = -100
  495. this.width = this.canvas.width = window.innerWidth;
  496. this.height = this.canvas.height = window.innerHeight;
  497. this._init();
  498. }
  499. Snow.prototype._init = function() {
  500. var self = this;
  501. window.addEventListener('resize', function() {
  502. self.width = self.canvas.width = window.innerWidth;
  503. self.height = self.canvas.height = window.innerHeight;
  504. });
  505. for(var i = 0; i < this.flakeCount; ++i) {
  506. var x = Math.floor(Math.random() * this.width),
  507. y = Math.floor(Math.random() * this.height),
  508. size = (Math.random()*3.5) + .5,
  509. speed = size*.5,
  510. opacity = (Math.random() * 0.5) + 0.1;
  511. this.flakes.push({
  512. speed: speed,
  513. velY: speed,
  514. velX: 0,
  515. x: x,
  516. y: y,
  517. size: size,
  518. stepSize: (Math.random()) / 30,
  519. step: 0,
  520. opacity: opacity
  521. });
  522. }
  523. this._snow();
  524. };
  525. Snow.prototype._snow = function() {
  526. this.ctx.clearRect(0, 0, this.width, this.height);
  527. for(var i = 0; i < this.flakeCount; ++i) {
  528. var flake = this.flakes[i],
  529. x = this.mX,
  530. y = this.mY,
  531. minDist = 150,
  532. x2 = flake.x,
  533. y2 = flake.y,
  534. dist = Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)),
  535. dx = x2 - x,
  536. dy = y2 - y;
  537. if( dist < minDist ) {
  538. var force = minDist / (dist * dist),
  539. xcomp = (x - x2) / dist,
  540. ycomp = (y - y2) / dist,
  541. deltaV = force / 2;
  542. flake.velX -= deltaV * xcomp;
  543. flake.velY -= deltaV * ycomp;
  544. }
  545. else {
  546. flake.velX *= .98;
  547. if( flake.velY <= flake.speed ) {
  548. flake.velY = flake.speed
  549. }
  550. flake.velX += Math.cos(flake.step += .05) * flake.stepSize;
  551. }
  552. this.ctx.fillStyle = "rgba(255,255,255," + flake.opacity + ")";
  553. flake.y += flake.velY;
  554. flake.x += flake.velX;
  555. if( flake.y >= this.height || flake.y <= 0 ) {
  556. this._reset(flake);
  557. }
  558. if( flake.x >= this.width || flake.x <= 0 ) {
  559. this._reset(flake);
  560. }
  561. this.ctx.beginPath();
  562. this.ctx.arc(flake.x, flake.y, flake.size, 0, Math.PI * 2);
  563. this.ctx.fill();
  564. }
  565. requestAnimationFrame(this._snow.bind(this));
  566. };
  567. Snow.prototype._reset = function(flake) {
  568. flake.x = Math.floor(Math.random() * this.width);
  569. flake.y = 0;
  570. flake.size = (Math.random() * 3.5) + .5;
  571. flake.speed = flake.size*.5,
  572. flake.velY = flake.speed;
  573. flake.velX = 0;
  574. flake.opacity = (Math.random() * 0.5) + 0.1;
  575. };
  576. var calendarEl = document.querySelector('.calendar'),
  577. calendarDays = [].slice.call(calendarEl.children),
  578. settings = {
  579. snow: false,
  580. tilt: true
  581. },
  582. bgEl = document.body,
  583. defaultBgColor = bgEl.style.backgroundColor,
  584. colortimeout,
  585. contentEl = document.querySelector('.content'),
  586. contents = contentEl.querySelectorAll('.content__block'),
  587. backCtrl = contentEl.querySelector('.btn-back'),
  588. contentNumber = contentEl.querySelector('.content__number'),
  589. isMobile = mobilecheck();
  590. function init() {
  591. layout();
  592. }
  593. function layout() {
  594. new Calendar(calendarEl);
  595. // If settings.snow === true then create the canvas element for the snow effect.
  596. if( settings.snow ) {
  597. var snow = new Snow();
  598. bgEl = snow.canvas;
  599. }
  600. }
  601. init();
  602. })(window);