diff options
| author | Jules Laplace <jules@okfoc.us> | 2017-04-27 15:39:35 -0400 |
|---|---|---|
| committer | Jules Laplace <jules@okfoc.us> | 2017-04-27 15:39:35 -0400 |
| commit | 52596435d4839ef4026db8441598b32c3fd83c5c (patch) | |
| tree | dfbd56021a274961f63e974195f69bd98e21aab2 /client/lib | |
| parent | 2761ba1231686407af2e11786e29e84c69b0183f (diff) | |
make it work mobile
Diffstat (limited to 'client/lib')
| -rw-r--r-- | client/lib/startAudioContext.js | 181 | ||||
| -rw-r--r-- | client/lib/util.js | 60 |
2 files changed, 240 insertions, 1 deletions
diff --git a/client/lib/startAudioContext.js b/client/lib/startAudioContext.js new file mode 100644 index 0000000..f3a9793 --- /dev/null +++ b/client/lib/startAudioContext.js @@ -0,0 +1,181 @@ +/** + * StartAudioContext.js + * @author Yotam Mann + * @license http://opensource.org/licenses/MIT MIT License + * @copyright 2016 Yotam Mann + */ +(function (root, factory) { + if (typeof define === "function" && define.amd) { + define([], factory); + } else if (typeof module === 'object' && module.exports) { + module.exports = factory(); + } else { + root.StartAudioContext = factory(); + } +}(this, function () { + + /** + * The StartAudioContext object + */ + var StartAudioContext = { + /** + * The audio context passed in by the user + * @type {AudioContext} + */ + context : null, + /** + * The TapListeners bound to the elements + * @type {Array} + * @private + */ + _tapListeners : [], + /** + * Callbacks to invoke when the audio context is started + * @type {Array} + * @private + */ + _onStarted : [], + }; + + + /** + * Set the context + * @param {AudioContext} ctx + * @returns {StartAudioContext} + */ + StartAudioContext.setContext = function(ctx){ + StartAudioContext.context = ctx; + return StartAudioContext; + }; + + /** + * Add a tap listener to the audio context + * @param {Array|Element|String|jQuery} element + * @returns {StartAudioContext} + */ + StartAudioContext.on = function(element){ + if (Array.isArray(element) || (NodeList && element instanceof NodeList)){ + for (var i = 0; i < element.length; i++){ + StartAudioContext.on(element[i]); + } + } else if (typeof element === "string"){ + StartAudioContext.on(document.querySelectorAll(element)); + } else if (element.jquery && typeof element.toArray === "function"){ + StartAudioContext.on(element.toArray()); + } else if (Element && element instanceof Element){ + //if it's an element, create a TapListener + var tap = new TapListener(element, onTap); + StartAudioContext._tapListeners.push(tap); + } + return StartAudioContext; + }; + + /** + * Bind a callback to when the audio context is started. + * @param {Function} cb + * @return {StartAudioContext} + */ + StartAudioContext.onStarted = function(cb){ + //if it's already started, invoke the callback + if (StartAudioContext.isStarted()){ + cb(); + } else { + StartAudioContext._onStarted.push(cb); + } + return StartAudioContext; + }; + + /** + * returns true if the context is started + * @return {Boolean} + */ + StartAudioContext.isStarted = function(){ + return (StartAudioContext.context !== null && StartAudioContext.context.state === "running"); + }; + + /** + * @class Listens for non-dragging tap ends on the given element + * @param {Element} element + * @internal + */ + var TapListener = function(element){ + + this._dragged = false; + + this._element = element; + + this._bindedMove = this._moved.bind(this); + this._bindedEnd = this._ended.bind(this); + + element.addEventListener("touchmove", this._bindedMove); + element.addEventListener("touchend", this._bindedEnd); + element.addEventListener("mouseup", this._bindedEnd); + }; + + /** + * drag move event + */ + TapListener.prototype._moved = function(e){ + this._dragged = true; + }; + + /** + * tap ended listener + */ + TapListener.prototype._ended = function(e){ + if (!this._dragged){ + onTap(); + } + this._dragged = false; + }; + + /** + * remove all the bound events + */ + TapListener.prototype.dispose = function(){ + this._element.removeEventListener("touchmove", this._bindedMove); + this._element.removeEventListener("touchend", this._bindedEnd); + this._element.removeEventListener("mouseup", this._bindedEnd); + this._bindedMove = null; + this._bindedEnd = null; + this._element = null; + }; + + /** + * Invoked the first time of the elements is tapped. + * Creates a silent oscillator when a non-dragging touchend + * event has been triggered. + */ + function onTap(){ + //start the audio context with a silent oscillator + if (StartAudioContext.context && !StartAudioContext.isStarted()){ + var osc = StartAudioContext.context.createOscillator(); + var silent = StartAudioContext.context.createGain(); + silent.gain.value = 0; + osc.connect(silent); + silent.connect(StartAudioContext.context.destination); + var now = StartAudioContext.context.currentTime; + osc.start(now); + osc.stop(now+0.5); + } + + //dispose all the tap listeners + if (StartAudioContext._tapListeners){ + for (var i = 0; i < StartAudioContext._tapListeners.length; i++){ + StartAudioContext._tapListeners[i].dispose(); + } + StartAudioContext._tapListeners = null; + } + //the onstarted callbacks + if (StartAudioContext._onStarted){ + for (var j = 0; j < StartAudioContext._onStarted.length; j++){ + StartAudioContext._onStarted[j](); + } + StartAudioContext._onStarted = null; + } + } + + return StartAudioContext; +})); + + diff --git a/client/lib/util.js b/client/lib/util.js index b2d95f5..4d9d038 100644 --- a/client/lib/util.js +++ b/client/lib/util.js @@ -1,3 +1,61 @@ +import Tone from 'tone' +import StartAudioContext from './startAudioContext' + +const isIphone = (navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) +const isIpad = (navigator.userAgent.match(/iPad/i)) +const isAndroid = (navigator.userAgent.match(/Android/i)) +const isMobile = isIphone || isIpad || isAndroid +const isDesktop = ! isMobile + +document.body.classList.add(isMobile ? 'mobile' : 'desktop') + +const browser = { isIphone, isIpad, isMobile, isDesktop } + function choice (a){ return a[ Math.floor(Math.random() * a.length) ] } +function mod(n,m){ return n-(m * Math.floor(n/m)) } + +function requestAudioContext (fn) { + if (isMobile) { + const container = document.createElement('div') + const button = document.createElement('div') + button.innerHTML = 'Tap to start - please unmute your phone' + Object.assign(container.style, { + display: 'block', + position: 'absolute', + width: '100%', + height: '100%', + zIndex: '10000', + top: '0px', + left: '0px', + backgroundColor: 'rgba(0, 0, 0, 0.8)', + }) + Object.assign(button.style, { + display: 'block', + position: 'absolute', + left: '50%', + top: '50%', + padding: '20px', + backgroundColor: '#7F33ED', + color: 'white', + fontFamily: 'monospace', + borderRadius: '3px', + transform: 'translate3D(-50%,-50%,0)', + textAlign: 'center', + lineHeight: '1.5', + width: '150px', + }) + container.appendChild(button) + document.body.appendChild(container) + StartAudioContext.setContext(Tone.context) + StartAudioContext.on(button) + StartAudioContext.onStarted(_ => { + container.remove() + fn() + }) + } else { + fn() + } +} + +export { choice, mod, browser, requestAudioContext } -export { choice }
\ No newline at end of file |
