diff --git a/ease.js b/ease.js new file mode 100644 index 0000000..3fd4c29 --- /dev/null +++ b/ease.js @@ -0,0 +1,198 @@ +// easing functions from "Tween.js" +exports.linear = function(n){ + return n; +}; + +exports.inQuad = function(n){ + return n * n; +}; + +exports.outQuad = function(n){ + return n * (2 - n); +}; + +exports.inOutQuad = function(n){ + n *= 2; + if (n < 1) return 0.5 * n * n; + return - 0.5 * (--n * (n - 2) - 1); +}; + +exports.inCube = function(n){ + return n * n * n; +}; + +exports.outCube = function(n){ + return --n * n * n + 1; +}; + +exports.inOutCube = function(n){ + n *= 2; + if (n < 1) return 0.5 * n * n * n; + return 0.5 * ((n -= 2 ) * n * n + 2); +}; + +exports.inQuart = function(n){ + return n * n * n * n; +}; + +exports.outQuart = function(n){ + return 1 - (--n * n * n * n); +}; + +exports.inOutQuart = function(n){ + n *= 2; + if (n < 1) return 0.5 * n * n * n * n; + return -0.5 * ((n -= 2) * n * n * n - 2); +}; + +exports.inQuint = function(n){ + return n * n * n * n * n; +} + +exports.outQuint = function(n){ + return --n * n * n * n * n + 1; +} + +exports.inOutQuint = function(n){ + n *= 2; + if (n < 1) return 0.5 * n * n * n * n * n; + return 0.5 * ((n -= 2) * n * n * n * n + 2); +}; + +exports.inSine = function(n){ + return 1 - Math.cos(n * Math.PI / 2 ); +}; + +exports.outSine = function(n){ + return Math.sin(n * Math.PI / 2); +}; + +exports.inOutSine = function(n){ + return .5 * (1 - Math.cos(Math.PI * n)); +}; + +exports.inExpo = function(n){ + return 0 == n ? 0 : Math.pow(1024, n - 1); +}; + +exports.outExpo = function(n){ + return 1 == n ? n : 1 - Math.pow(2, -10 * n); +}; + +exports.inOutExpo = function(n){ + if (0 == n) return 0; + if (1 == n) return 1; + if ((n *= 2) < 1) return .5 * Math.pow(1024, n - 1); + return .5 * (-Math.pow(2, -10 * (n - 1)) + 2); +}; + +exports.inCirc = function(n){ + return 1 - Math.sqrt(1 - n * n); +}; + +exports.outCirc = function(n){ + return Math.sqrt(1 - (--n * n)); +}; + +exports.inOutCirc = function(n){ + n *= 2 + if (n < 1) return -0.5 * (Math.sqrt(1 - n * n) - 1); + return 0.5 * (Math.sqrt(1 - (n -= 2) * n) + 1); +}; + +exports.inBack = function(n){ + var s = 1.70158; + return n * n * (( s + 1 ) * n - s); +}; + +exports.outBack = function(n){ + var s = 1.70158; + return --n * n * ((s + 1) * n + s) + 1; +}; + +exports.inOutBack = function(n){ + var s = 1.70158 * 1.525; + if ( ( n *= 2 ) < 1 ) return 0.5 * ( n * n * ( ( s + 1 ) * n - s ) ); + return 0.5 * ( ( n -= 2 ) * n * ( ( s + 1 ) * n + s ) + 2 ); +}; + +exports.inBounce = function(n){ + return 1 - exports.outBounce(1 - n); +}; + +exports.outBounce = function(n){ + if ( n < ( 1 / 2.75 ) ) { + return 7.5625 * n * n; + } else if ( n < ( 2 / 2.75 ) ) { + return 7.5625 * ( n -= ( 1.5 / 2.75 ) ) * n + 0.75; + } else if ( n < ( 2.5 / 2.75 ) ) { + return 7.5625 * ( n -= ( 2.25 / 2.75 ) ) * n + 0.9375; + } else { + return 7.5625 * ( n -= ( 2.625 / 2.75 ) ) * n + 0.984375; + } +}; + +exports.inOutBounce = function(n){ + if (n < .5) return exports.inBounce(n * 2) * .5; + return exports.outBounce(n * 2 - 1) * .5 + .5; +}; + +exports.inElastic = function(n){ + var s, a = 0.1, p = 0.4; + if ( n === 0 ) return 0; + if ( n === 1 ) return 1; + if ( !a || a < 1 ) { a = 1; s = p / 4; } + else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); + return - ( a * Math.pow( 2, 10 * ( n -= 1 ) ) * Math.sin( ( n - s ) * ( 2 * Math.PI ) / p ) ); +}; + +exports.outElastic = function(n){ + var s, a = 0.1, p = 0.4; + if ( n === 0 ) return 0; + if ( n === 1 ) return 1; + if ( !a || a < 1 ) { a = 1; s = p / 4; } + else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); + return ( a * Math.pow( 2, - 10 * n) * Math.sin( ( n - s ) * ( 2 * Math.PI ) / p ) + 1 ); +}; + +exports.inOutElastic = function(n){ + var s, a = 0.1, p = 0.4; + if ( n === 0 ) return 0; + if ( n === 1 ) return 1; + if ( !a || a < 1 ) { a = 1; s = p / 4; } + else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); + if ( ( n *= 2 ) < 1 ) return - 0.5 * ( a * Math.pow( 2, 10 * ( n -= 1 ) ) * Math.sin( ( n - s ) * ( 2 * Math.PI ) / p ) ); + return a * Math.pow( 2, -10 * ( n -= 1 ) ) * Math.sin( ( n - s ) * ( 2 * Math.PI ) / p ) * 0.5 + 1; +}; + +// aliases +exports['in-quad'] = exports.inQuad; +exports['out-quad'] = exports.outQuad; +exports['in-out-quad'] = exports.inOutQuad; +exports['in-cube'] = exports.inCube; +exports['out-cube'] = exports.outCube; +exports['in-out-cube'] = exports.inOutCube; +exports['in-quart'] = exports.inQuart; +exports['out-quart'] = exports.outQuart; +exports['in-out-quart'] = exports.inOutQuart; +exports['in-quint'] = exports.inQuint; +exports['out-quint'] = exports.outQuint; +exports['in-out-quint'] = exports.inOutQuint; +exports['in-sine'] = exports.inSine; +exports['out-sine'] = exports.outSine; +exports['in-out-sine'] = exports.inOutSine; +exports['in-expo'] = exports.inExpo; +exports['out-expo'] = exports.outExpo; +exports['in-out-expo'] = exports.inOutExpo; +exports['in-circ'] = exports.inCirc; +exports['out-circ'] = exports.outCirc; +exports['in-out-circ'] = exports.inOutCirc; +exports['in-back'] = exports.inBack; +exports['out-back'] = exports.outBack; +exports['in-out-back'] = exports.inOutBack; +exports['in-bounce'] = exports.inBounce; +exports['out-bounce'] = exports.outBounce; +exports['in-out-bounce'] = exports.inOutBounce; +exports['in-elastic'] = exports.inElastic; +exports['out-elastic'] = exports.outElastic; +exports['in-out-elastic'] = exports.inOutElastic; diff --git a/emitter.js b/emitter.js new file mode 100644 index 0000000..98df20b --- /dev/null +++ b/emitter.js @@ -0,0 +1,98 @@ +function Emitter(obj) { + if (obj) return mixin(obj); +}; + +function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; + } + return obj; +} + +Emitter.prototype.on = +Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks['$' + event] = this._callbacks['$' + event] || []) + .push(fn); + return this; +}; + +Emitter.prototype.once = function(event, fn){ + function on() { + this.off(event, on); + fn.apply(this, arguments); + } + + on.fn = fn; + this.on(event, on); + return this; +}; + +Emitter.prototype.off = +Emitter.prototype.removeListener = +Emitter.prototype.removeAllListeners = +Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; + } + + // specific event + var callbacks = this._callbacks['$' + event]; + if (!callbacks) return this; + + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks['$' + event]; + return this; + } + + // remove specific handler + var cb; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + if (cb === fn || cb.fn === fn) { + callbacks.splice(i, 1); + break; + } + } + + // Remove event specific arrays for event types that no + // one is subscribed for to avoid memory leak. + if (callbacks.length === 0) { + delete this._callbacks['$' + event]; + } + + return this; +}; + +Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks['$' + event]; + + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); + } + } + + return this; +}; + +Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks['$' + event] || []; +}; + +Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; +}; + +if (typeof module !== 'undefined') { + module.exports = Emitter; +} diff --git a/index.js b/index.js index 3d4e139..68eb4e6 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,7 @@ function calculateScrollOffset(elem, additionalOffset, alignment) { var elemRect = elem.getBoundingClientRect(); var clientHeight = html.clientHeight; - var documentHeight = Math.max( body.scrollHeight, body.offsetHeight, + var documentHeight = Math.max( body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight ); additionalOffset = additionalOffset || 0; diff --git a/package-lock.json b/package-lock.json index 3be1ac2..c092a8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -343,35 +343,6 @@ "source-map": "0.5.7" } }, - "component-clone": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/component-clone/-/component-clone-0.2.2.tgz", - "integrity": "sha1-x/WXmCKID62M+wliuikYbQYe4E8=", - "requires": { - "component-type": "1.1.0" - } - }, - "component-emitter": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.0.tgz", - "integrity": "sha1-zNETqGOI0GSC0D3j/H35hSa6jv4=" - }, - "component-tween": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/component-tween/-/component-tween-1.2.0.tgz", - "integrity": "sha1-zDnOXbqwW1KCX0HRlHY4oLAbK4o=", - "requires": { - "component-clone": "0.2.2", - "component-emitter": "1.2.0", - "component-type": "1.1.0", - "ease-component": "1.0.0" - } - }, - "component-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/component-type/-/component-type-1.1.0.tgz", - "integrity": "sha1-lbZmqtU+XI0fK+E1xFtdSZGXwMU=" - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -544,11 +515,6 @@ "readable-stream": "2.3.6" } }, - "ease-component": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ease-component/-/ease-component-1.0.0.tgz", - "integrity": "sha1-s3VybbC1sEWVt3RAOW/sfapdd8k=" - }, "elliptic": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", diff --git a/package.json b/package.json index 70b6c95..628d974 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "scroll-to-element", "description": "Smooth scrolling to an element via selector or node reference", - "version": "2.0.1", + "version": "2.0.2", "author": { "name": "Will Hoag", "email": "dev@willhoag.com" @@ -14,7 +14,6 @@ "prepublish": "npm run build" }, "dependencies": { - "component-tween": "^1.2.0", "raf": "^3.4.0" }, "keywords": [ diff --git a/scroll-to.js b/scroll-to.js index 7bd995e..3059391 100644 --- a/scroll-to.js +++ b/scroll-to.js @@ -1,4 +1,4 @@ -var Tween = require('component-tween'); +var Tween = require('./tween'); var raf = require('raf'); function scroll() { diff --git a/tween.js b/tween.js new file mode 100644 index 0000000..523d220 --- /dev/null +++ b/tween.js @@ -0,0 +1,93 @@ +var ease = require('./ease'); +var Emitter = require('./emitter'); + +function Tween(obj) { + if (!(this instanceof Tween)) return new Tween(obj); + this._from = obj; + this.ease('linear'); + this.duration(500); +} + +Emitter(Tween.prototype); + +Tween.prototype.reset = function(){ + this.isArray = Object.prototype.toString.call(this._from) === '[object Array]'; + this._curr = Object.assign({}, this._from); + this._done = false; + this._start = Date.now(); + return this; +}; + +Tween.prototype.to = function(obj){ + this.reset(); + this._to = obj; + return this; +}; + +Tween.prototype.duration = function(ms){ + this._duration = ms; + return this; +}; + +Tween.prototype.ease = function(fn){ + fn = 'function' == typeof fn ? fn : ease[fn]; + if (!fn) throw new TypeError('invalid easing function'); + this._ease = fn; + return this; +}; + +Tween.prototype.stop = function(){ + this.stopped = true; + this._done = true; + this.emit('stop'); + this.emit('end'); + return this; +}; + +Tween.prototype.step = function(){ + if (this._done) return; + + var duration = this._duration; + var now = Date.now(); + var delta = now - this._start; + var done = delta >= duration; + + if (done) { + this._from = this._to; + this._update(this._to); + this._done = true; + this.emit('end'); + return this; + } + + var from = this._from; + var to = this._to; + var curr = this._curr; + var fn = this._ease; + var p = (now - this._start) / duration; + var n = fn(p); + + if (this.isArray) { + for (var i = 0; i < from.length; ++i) { + curr[i] = from[i] + (to[i] - from[i]) * n; + } + + this._update(curr); + return this; + } + + for (var k in from) { + curr[k] = from[k] + (to[k] - from[k]) * n; + } + + this._update(curr); + return this; +}; + +Tween.prototype.update = function(fn){ + if (0 == arguments.length) return this.step(); + this._update = fn; + return this; +}; + +module.exports = Tween; \ No newline at end of file