main3.js 19 KB

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