diff --git a/app.json b/app.json index 8fac74ac..abef42a1 100644 --- a/app.json +++ b/app.json @@ -113,7 +113,10 @@ { "path": "resources/js/GALocalStorage.js" }, - { + { + "path": "resources/js/loadscreen.js" + }, + { "path": "env.js" }, { diff --git a/index.html b/index.html index 22c60550..f315b2d1 100644 --- a/index.html +++ b/index.html @@ -21,6 +21,14 @@ -
+
+
+
diff --git a/packages/local/rambox-default-theme/resources/fonts/icomoon/icomoon.eot b/packages/local/rambox-default-theme/resources/fonts/icomoon/icomoon.eot new file mode 100644 index 00000000..0d9f71a5 Binary files /dev/null and b/packages/local/rambox-default-theme/resources/fonts/icomoon/icomoon.eot differ diff --git a/packages/local/rambox-default-theme/resources/fonts/icomoon/icomoon.svg b/packages/local/rambox-default-theme/resources/fonts/icomoon/icomoon.svg new file mode 100644 index 00000000..360b0b0a --- /dev/null +++ b/packages/local/rambox-default-theme/resources/fonts/icomoon/icomoon.svg @@ -0,0 +1,21 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/local/rambox-default-theme/resources/fonts/icomoon/icomoon.ttf b/packages/local/rambox-default-theme/resources/fonts/icomoon/icomoon.ttf new file mode 100644 index 00000000..fc7e5642 Binary files /dev/null and b/packages/local/rambox-default-theme/resources/fonts/icomoon/icomoon.ttf differ diff --git a/packages/local/rambox-default-theme/resources/fonts/icomoon/icomoon.woff b/packages/local/rambox-default-theme/resources/fonts/icomoon/icomoon.woff new file mode 100644 index 00000000..5b3c470a Binary files /dev/null and b/packages/local/rambox-default-theme/resources/fonts/icomoon/icomoon.woff differ diff --git a/packages/local/rambox-default-theme/sass/etc/_loadscreen.scss b/packages/local/rambox-default-theme/sass/etc/_loadscreen.scss new file mode 100644 index 00000000..dddb9092 --- /dev/null +++ b/packages/local/rambox-default-theme/sass/etc/_loadscreen.scss @@ -0,0 +1,229 @@ +/* Main component wrapper */ +body { + overflow: hidden; +} +.component { + position: absolute;; + z-index: 1; + width: 200px; + height: 200px; + margin: -100px 0 0 -100px; + top: 50%; + left: 50%; +} + + +/* Actual buttons (laid over shapes) */ + +.button { + font-weight: bold; + position: absolute; + bottom: 4px; + top: 50%; + left: 50%; + width: 200px; + height: 200px; + margin: -100px 0 0 -100px; + padding: 0; + text-align: center; + color: #00a7e7; + border: none; + background: none; + -webkit-transition: opacity 0.3s; + transition: opacity 0.3s; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +.button:hover, +.button:focus { + outline: none; + color: #048abd; +} + +.button--listen { + pointer-events: none; +} + +.button--close { + z-index: 10; + top: 0px; + right: 0px; + left: auto; + width: 40px; + height: 40px; + padding: 10px; + color: #fff; +} + +.button--close:hover, +.button--close:focus { + color: #ddd; +} + +.button--hidden { + pointer-events: none; + opacity: 0; +} + + +/* Inner content of the start/*/ + +.button__content { + position: absolute; + opacity: 0; + -webkit-transition: -webkit-transform 0.4s, opacity 0.4s; + transition: transform 0.4s, opacity 0.4s; +} + +.button__content--listen { + font-size: 1.75em; + line-height: 64px; + bottom: 0; + left: 50%; + width: 60px; + height: 60px; + margin: 0 0 0 -30px; + border-radius: 50%; + -webkit-transform: translate3d(0, 25px, 0); + transform: translate3d(0, 25px, 0); + -webkit-transition-timing-function: cubic-bezier(0.8, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0.8, 0, 0.2, 1); +} + +.button__content--listen::before, +.button__content--listen::after { + content: ''; + position: absolute; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + opacity: 0; + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 50%; +} + +.button--animate .button__content--listen::before, +.button--animate .button__content--listen::after { + -webkit-animation: anim-ripple 1.2s ease-out infinite forwards; + animation: anim-ripple 1.2s ease-out infinite forwards; +} + +.button--animate .button__content--listen::after { + -webkit-animation-delay: 0.6s; + animation-delay: 0.6s; +} + +@-webkit-keyframes anim-ripple { + 0% { + opacity: 0; + -webkit-transform: scale3d(3, 3, 1); + transform: scale3d(3, 3, 1); + } + 50% { + opacity: 1; + } + 100% { + opacity: 0; + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes anim-ripple { + 0% { + opacity: 0; + -webkit-transform: scale3d(3, 3, 1); + transform: scale3d(3, 3, 1); + } + 50% { + opacity: 1; + } + 100% { + opacity: 0; + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.notes { + position: absolute; + z-index: -1; + bottom: 0; + left: 50%; + width: 200px; + height: 100px; + margin: 0 0 0 -100px; +} + +.note { + font-size: 2.8em; + position: absolute; + left: 50%; + width: 1em; + margin: 0 0 0 -0.5em; + opacity: 0; + color: rgba(255, 255, 255, 0.75); +} + +.note:nth-child(odd) { + color: rgba(0, 0, 0, 0.1); +} + +.note:nth-child(4n) { + font-size: 2em; +} + +.note:nth-child(6n) { + color: rgba(255, 255, 255, 0.3); +} + +/* ICONS */ +@font-face { + font-family: 'icomoon'; + src:url('../resources/fonts/icomoon/icomoon.eot?4djz1y'); + src:url('../resources/fonts/icomoon/icomoon.eot?4djz1y#iefix') format('embedded-opentype'), + url('../resources/fonts/icomoon/icomoon.ttf?4djz1y') format('truetype'), + url('../resources/fonts/icomoon/icomoon.woff?4djz1y') format('woff'), + url('../resources/fonts/icomoon/icomoon.svg?4djz1y#icomoon') format('svg'); + font-weight: normal; + font-style: normal; +} + +.icon { + font-family: 'icomoon'; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + + +.icon--microphone:before { + content: "\ea95"; +} +.icon--cross:before { + content: "\e90c"; +} +.icon--note1:before { + content: "\ea83"; +} +.icon--note2:before { + content: "\eaad"; +} +.icon--note3:before { + content: "\eac5"; +} +.icon--note4:before { + content: "\ea93"; +} +.icon--note5:before { + content: "\ea95"; +} +.icon--note6:before { + content: "\ea96"; +} diff --git a/packages/local/rambox-default-theme/sass/etc/all.scss b/packages/local/rambox-default-theme/sass/etc/all.scss index 851e7101..1eda5991 100644 --- a/packages/local/rambox-default-theme/sass/etc/all.scss +++ b/packages/local/rambox-default-theme/sass/etc/all.scss @@ -1,6 +1,7 @@ @import url(../resources/fonts/font-awesome/css/font-awesome.min.css); @import url(https://fonts.googleapis.com/css?family=Josefin+Sans:400,700,600); @import url(https://fonts.googleapis.com/css?family=Roboto:400,100,100italic,300,300italic,700italic,700,500italic,500,400italic); +@import 'loadscreen'; $base-color: #2E658E; $font-family: 'Roboto', sans-serif; diff --git a/resources/js/loadscreen.js b/resources/js/loadscreen.js new file mode 100644 index 00000000..049d2675 --- /dev/null +++ b/resources/js/loadscreen.js @@ -0,0 +1,263 @@ +/*! modernizr 3.2.0 (Custom Build) | MIT * + * http://modernizr.com/download/?-csstransitions-prefixedcss !*/ +!function(e,n,t){function r(e,n){return typeof e===n}function o(){var e,n,t,o,i,s,a;for(var f in C)if(C.hasOwnProperty(f)){if(e=[],n=C[f],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(t=0;td;d++)if(v=e[d],h=N.style[v],f(v,"-")&&(v=a(v)),N.style[v]!==t){if(i||r(o,"undefined"))return s(),"pfx"==n?v:!0;try{N.style[v]=o}catch(g){}if(N.style[v]!=h)return s(),"pfx"==n?v:!0}return s(),!1}function h(e,n,t,o,i){var s=e.charAt(0).toUpperCase()+e.slice(1),a=(e+" "+b.join(s+" ")+s).split(" ");return r(n,"string")||r(n,"undefined")?v(a,n,o,i):(a=(e+" "+P.join(s+" ")+s).split(" "),p(a,n,t))}function y(e,n,r){return h(e,t,t,n,r)}var g=[],C=[],x={_version:"3.2.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,n){var t=this;setTimeout(function(){n(t[e])},0)},addTest:function(e,n,t){C.push({name:e,fn:n,options:t})},addAsyncTest:function(e){C.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=x,Modernizr=new Modernizr;var _=n.documentElement,w="svg"===_.nodeName.toLowerCase(),S="Moz O ms Webkit",b=x._config.usePrefixes?S.split(" "):[];x._cssomPrefixes=b;var E=function(n){var r,o=prefixes.length,i=e.CSSRule;if("undefined"==typeof i)return t;if(!n)return!1;if(n=n.replace(/^@/,""),r=n.replace(/-/g,"_").toUpperCase()+"_RULE",r in i)return"@"+n;for(var s=0;o>s;s++){var a=prefixes[s],f=a.toUpperCase()+"_"+r;if(f in i)return"@-"+a.toLowerCase()+"-"+n}return!1};x.atRule=E;var P=x._config.usePrefixes?S.toLowerCase().split(" "):[];x._domPrefixes=P;var z={elem:l("modernizr")};Modernizr._q.push(function(){delete z.elem});var N={style:z.elem.style};Modernizr._q.unshift(function(){delete N.style}),x.testAllProps=h;var T=x.prefixed=function(e,n,t){return 0===e.indexOf("@")?E(e):(-1!=e.indexOf("-")&&(e=a(e)),n?h(e,n,t):h(e,"pfx"))};x.prefixedCSS=function(e){var n=T(e);return n&&s(n)};x.testAllProps=y,Modernizr.addTest("csstransitions",y("transition","all",!0)),o(),i(g),delete x.addTest,delete x.addAsyncTest;for(var j=0;j t1) return curveY(t1); + + // Fallback to the bisection method for reliability. + while (t0 < t1){ + x2 = curveX(t2); + if (Math.abs(x2 - x) < epsilon) return curveY(t2); + if (x > x2) t0 = t2; + else t1 = t2; + t2 = (t1 - t0) * .5 + t0; + } + // Failure + return curveY(t2); + }; + }, + getRandomNumber = function(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + }, + throttle = function(fn, delay) { + var allowSample = true; + + return function(e) { + if (allowSample) { + allowSample = false; + setTimeout(function() { allowSample = true; }, delay); + fn(e); + } + }; + }, + // from https://davidwalsh.name/vendor-prefix + prefix = (function () { + var styles = window.getComputedStyle(document.documentElement, ''), + pre = (Array.prototype.slice.call(styles).join('').match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o']))[1], + dom = ('WebKit|Moz|MS|O').match(new RegExp('(' + pre + ')', 'i'))[1]; + + return { + dom: dom, + lowercase: pre, + css: '-' + pre + '-', + js: pre[0].toUpperCase() + pre.substr(1) + }; + })(); + + var support = {transitions : Modernizr.csstransitions}, + transEndEventNames = { 'WebkitTransition': 'webkitTransitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd', 'msTransition': 'MSTransitionEnd', 'transition': 'transitionend' }, + transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ], + onEndTransition = function( el, callback, propTest ) { + var onEndCallbackFn = function( ev ) { + if( support.transitions ) { + if( ev.target != this || propTest && ev.propertyName !== propTest && ev.propertyName !== prefix.css + propTest ) return; + this.removeEventListener( transEndEventName, onEndCallbackFn ); + } + if( callback && typeof callback === 'function' ) { callback.call(this); } + }; + if( support.transitions ) { + el.addEventListener( transEndEventName, onEndCallbackFn ); + } + else { + onEndCallbackFn(); + } + }, + // the main component element/wrapper + shzEl = document.querySelector('.component'), + // the initial button + shzCtrl = shzEl.querySelector('div.button--start'), + // total number of notes/symbols moving towards the listen button + totalNotes = 50, + // the notes elements + notes, + // the note“s speed factor relative to the distance from the note element to the button. + // if notesSpeedFactor = 1, then the speed equals the distance (in ms) + notesSpeedFactor = 4.5, + // window sizes + winsize = {width: window.innerWidth, height: window.innerHeight}, + // button offset + shzCtrlOffset = shzCtrl.getBoundingClientRect(), + // button sizes + shzCtrlSize = {width: shzCtrl.offsetWidth, height: shzCtrl.offsetHeight}, + // tells us if the listening animation is taking place + isListening = false, + // audio player element + playerEl = shzEl.querySelector('.player'); + // close player control + //playerCloseCtrl = playerEl.querySelector('.button--close'); + + function init() { + // create the music notes elements - the musical symbols that will animate/move towards the listen button + createNotes(); + // star animation + listen(); + } + + /** + * creates [totalNotes] note elements (the musical symbols that will animate/move towards the listen button) + */ + function createNotes() { + var notesEl = document.createElement('div'), notesElContent = ''; + notesEl.className = 'notes'; + for(var i = 0; i < totalNotes; ++i) { + // we have 6 different types of symbols (icon--note1, icon--note2 ... icon--note6) + var j = (i + 1) - 6 * Math.floor(i/6); + notesElContent += '
'; + } + notesEl.innerHTML = notesElContent; + shzEl.insertBefore(notesEl, shzEl.firstChild) + + // reference to the notes elements + notes = [].slice.call(notesEl.querySelectorAll('.note')); + } + + /** + * transform the initial button into a circle shaped one that "listens" to the current song.. + */ + function listen() { + isListening = true; + + showNotes(); + } + + /** + * stop the ripples and notes animations + */ + function stopListening() { + isListening = false; + // music notes animation stops... + hideNotes(); + } + + /** + * show the notes elements: first set a random position and then animate them towards the button + */ + function showNotes() { + notes.forEach(function(note) { + // first position the notes randomly on the page + positionNote(note); + // now, animate the notes torwards the button + animateNote(note); + }); + } + + /** + * fade out the notes elements + */ + function hideNotes() { + notes.forEach(function(note) { + note.style.opacity = 0; + }); + } + + /** + * positions a note/symbol randomly on the page. The area is restricted to be somewhere outside of the viewport. + * @param {Element Node} note - the note element + */ + function positionNote(note) { + // we want to position the notes randomly (translation and rotation) outside of the viewport + var x = getRandomNumber(-2*(shzCtrlOffset.left + shzCtrlSize.width/2), 2*(winsize.width - (shzCtrlOffset.left + shzCtrlSize.width/2))), y, + rotation = getRandomNumber(-30, 30); + + if( x > -1*(shzCtrlOffset.top + shzCtrlSize.height/2) && x < shzCtrlOffset.top + shzCtrlSize.height/2 ) { + y = getRandomNumber(0,1) > 0 ? getRandomNumber(-2*(shzCtrlOffset.top + shzCtrlSize.height/2), -1*(shzCtrlOffset.top + shzCtrlSize.height/2)) : getRandomNumber(winsize.height - (shzCtrlOffset.top + shzCtrlSize.height/2), winsize.height + winsize.height - (shzCtrlOffset.top + shzCtrlSize.height/2)); + } + else { + y = getRandomNumber(-2*(shzCtrlOffset.top + shzCtrlSize.height/2), winsize.height + winsize.height - (shzCtrlOffset.top + shzCtrlSize.height/2)); + } + + // first reset transition if any + note.style.WebkitTransition = note.style.transition = 'none'; + + // apply the random transforms + note.style.WebkitTransform = note.style.transform = 'translate3d(' + x + 'px,' + y + 'px,0) rotate3d(0,0,1,' + rotation + 'deg)'; + + // save the translation values for later + note.setAttribute('data-tx', Math.abs(x)); + note.setAttribute('data-ty', Math.abs(y)); + } + + /** + * animates a note torwards the button. Once that's done, it repositions the note and animates it again until the component is no longer listening. + * @param {Element Node} note - the note element + */ + function animateNote(note) { + setTimeout(function() { + if(!isListening) return; + // the transition speed of each note will be proportional to the its distance to the button + // speed = notesSpeedFactor * distance + var noteSpeed = notesSpeedFactor * Math.sqrt(Math.pow(note.getAttribute('data-tx'),2) + Math.pow(note.getAttribute('data-ty'),2)); + + // apply the transition + note.style.WebkitTransition = '-webkit-transform ' + noteSpeed + 'ms ease, opacity 0.8s'; + note.style.transition = 'transform ' + noteSpeed + 'ms ease-in, opacity 0.8s'; + + // now apply the transform (reset the transform so the note moves to its original position) and fade in the note + note.style.WebkitTransform = note.style.transform = 'translate3d(0,0,0)'; + note.style.opacity = 1; + + // after the animation is finished, + var onEndTransitionCallback = function() { + // reset transitions and styles + note.style.WebkitTransition = note.style.transition = 'none'; + note.style.opacity = 0; + + if(!isListening) return; + + positionNote(note); + animateNote(note); + }; + + onEndTransition(note, onEndTransitionCallback, 'transform'); + }, 60); + } + + init(); + +})(window);