kute.js/coffeeScript.js

1064 lines
34 KiB
JavaScript

### KUTE.js - The Light Tweening Engine
# by dnp_theme
# Licensed under MIT-License
###
((root, factory) ->
if typeof define == 'function' and define.amd
define [], factory
# AMD. Register as an anonymous module.
else if typeof exports == 'object'
module.exports = factory()
# Node, not strict CommonJS
else
root.KUTE = factory()
return
) this, ->
'use strict'
# set a custom scope for KUTE.js
g = if typeof global != 'undefined' then global else window
time = g.performance
tweens = []
tick = null
# tick must be null!!
#supported properties
_colors = [
'color'
'backgroundColor'
]
_boxModel = [
'top'
'left'
'width'
'height'
]
_transform = [
'translate3d'
'translateX'
'translateY'
'translateZ'
'rotate'
'translate'
'rotateX'
'rotateY'
'rotateZ'
'skewX'
'skewY'
'scale'
]
_scroll = [ 'scroll' ]
_opacity = [ 'opacity' ]
_all = _colors.concat(_opacity, _boxModel, _transform)
al = _all.length
_defaults = {}
#all properties default values
#populate default values object
i = 0
while i < al
p = _all[i]
if _colors.indexOf(p) != -1
_defaults[p] = 'rgba(0,0,0,0)'
# _defaults[p] = {r:0,g:0,b:0,a:1}; // no unit/suffix
else if _boxModel.indexOf(p) != -1
_defaults[p] = 0
else if p == 'translate3d'
# px
_defaults[p] = [
0
0
0
]
else if p == 'translate'
# px
_defaults[p] = [
0
0
]
else if p == 'rotate' or /X|Y|Z/.test(p)
# deg
_defaults[p] = 0
else if p == 'scale' or p == 'opacity'
# unitless
_defaults[p] = 1
p = null
i++
# default tween options, since 1.6.1
defaultOptions =
duration: 700
delay: 0
offset: 0
repeat: 0
repeatDelay: 0
yoyo: false
easing: 'linear'
keepHex: false
getPrefix = ->
`var i`
`var i`
#returns browser prefix
div = document.createElement('div')
i = 0
pf = [
'Moz'
'moz'
'Webkit'
'webkit'
'O'
'o'
'Ms'
'ms'
]
s = [
'MozTransform'
'mozTransform'
'WebkitTransform'
'webkitTransform'
'OTransform'
'oTransform'
'MsTransform'
'msTransform'
]
i = 0
pl = pf.length
while i < pl
if s[i] of div.style
return pf[i]
i++
div = null
return
property = (p) ->
# returns prefixed property | property
r = if !(p of document.body.style) then true else false
f = getPrefix()
# is prefix required for property | prefix
if r then f + p.charAt(0).toUpperCase() + p.slice(1) else p
selector = (el, multi) ->
# a public selector utility
nl = undefined
if multi
nl = if el instanceof Object or typeof el == 'object' then el else document.querySelectorAll(el)
else
nl = if typeof el == 'object' then el else if /^#/.test(el) then document.getElementById(el.replace('#', '')) else document.querySelector(el)
if nl == null and el != 'window'
throw new TypeError('Element not found or incorrect selector: ' + el)
nl
radToDeg = (a) ->
a * 180 / Math.PI
trueDimension = (d, p) ->
`var i`
#true dimension returns { v = value, u = unit }
x = parseInt(d) or 0
mu = [
'px'
'%'
'deg'
'rad'
'em'
'rem'
'vh'
'vw'
]
y = undefined
i = 0
l = mu.length
while i < l
if typeof d == 'string' and d.indexOf(mu[i]) != -1
y = mu[i]
break
i++
y = if y != undefined then y else if p then 'deg' else 'px'
{
v: x
u: y
}
trueColor = (v) ->
# replace transparent and transform any color to rgba()/rgb()
if /rgb|rgba/.test(v)
# first check if it's a rgb string
vrgb = v.replace(/\s|\)/, '').split('(')[1].split(',')
y = if vrgb[3] then vrgb[3] else null
if !y
return {
r: parseInt(vrgb[0])
g: parseInt(vrgb[1])
b: parseInt(vrgb[2])
}
else
return {
r: parseInt(vrgb[0])
g: parseInt(vrgb[1])
b: parseInt(vrgb[2])
a: parseFloat(y)
}
else if /^#/.test(v)
fromHex = hexToRGB(v)
return {
r: fromHex.r
g: fromHex.g
b: fromHex.b
}
else if /transparent|none|initial|inherit/.test(v)
return {
r: 0
g: 0
b: 0
a: 0
}
else if !/^#|^rgb/.test(v)
# maybe we can check for web safe colors
h = document.getElementsByTagName('head')[0]
h.style.color = v
webColor = g.getComputedStyle(h, null).color
webColor = if /rgb/.test(webColor) then webColor.replace(/[^\d,]/g, '').split(',') else [
0
0
0
]
h.style.color = ''
return {
r: parseInt(webColor[0])
g: parseInt(webColor[1])
b: parseInt(webColor[2])
}
return
rgbToHex = (r, g, b) ->
# transform rgb to hex or vice-versa | webkit browsers ignore HEX, always use RGB/RGBA
'#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
hexToRGB = (hex) ->
shr = /^#?([a-f\d])([a-f\d])([a-f\d])$/i
# Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
hex = hex.replace(shr, (m, r, g, b) ->
r + r + g + g + b + b
)
result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
if result then
r: parseInt(result[1], 16)
g: parseInt(result[2], 16)
b: parseInt(result[3], 16) else null
getInlineStyle = (el, p) ->
`var i`
# get transform style for element from cssText for .to() method, the sp is for transform property
if !el
return
# if the scroll applies to `window` it returns as it has no styling
css = el.style.cssText.replace(/\s/g, '').split(';')
trsf = {}
#the transform object
# if we have any inline style in the cssText attribute, usually it has higher priority
i = 0
csl = css.length
while i < csl
if /transform/i.test(css[i])
tps = css[i].split(':')[1].split(')')
#all transform properties
k = 0
tpl = tps.length - 1
while k < tpl
tpv = tps[k].split('(')
tp = tpv[0]
tv = tpv[1]
#each transform property
if _transform.indexOf(tp) != -1
trsf[tp] = if /translate3d/.test(tp) then tv.split(',') else tv
k++
i++
trsf
getCurrentStyle = (el, p) ->
# get computed style property for element for .to() method
styleAttribute = el.style
computedStyle = g.getComputedStyle(el, null) or el.currentStyle
pp = property(p)
styleValue = if styleAttribute[p] and !/auto|initial|none|unset/.test(styleAttribute[p]) then styleAttribute[p] else computedStyle[pp]
# s the property style value
if p != 'transform' and (pp of computedStyle or pp of styleAttribute)
if styleValue
if pp == 'filter'
# handle IE8 opacity
filterValue = parseInt(styleValue.split('=')[1].replace(')', ''))
return parseFloat(filterValue / 100)
else
return styleValue
else
return _defaults[p]
return
getAll = ->
tweens
removeAll = ->
tweens = []
return
add = (tw) ->
tweens.push tw
return
remove = (tw) ->
`var i`
i = tweens.indexOf(tw)
if i != -1
tweens.splice i, 1
return
stop = ->
if tick
_cancelAnimationFrame tick
tick = null
return
canTouch = 'ontouchstart' of g or navigator and navigator.msMaxTouchPoints or false
touchOrWheel = if canTouch then 'touchstart' else 'mousewheel'
mouseEnter = 'mouseenter'
_requestAnimationFrame = g.requestAnimationFrame or g.webkitRequestAnimationFrame or (c) ->
setTimeout c, 16
_cancelAnimationFrame = g.cancelAnimationFrame or g.webkitCancelRequestAnimationFrame or (c) ->
clearTimeout c
transformProperty = property('transform')
body = document.body
html = document.getElementsByTagName('HTML')[0]
scrollContainer = if navigator and /webkit/i.test(navigator.userAgent) or document.compatMode == 'BackCompat' then body else html
isIE = if navigator and new RegExp('MSIE ([0-9]{1,}[.0-9]{0,})').exec(navigator.userAgent) != null then parseFloat(RegExp.$1) else false
isIE8 = isIE == 8
isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
# we optimize morph depending on device type
# KUTE.js INTERPOLATORS
interpolate = g.Interpolate = {}
number =
interpolate.number = (a, b, v) ->
# number1, number2, progress
a = +a
b -= a
a + b * v
unit =
interpolate.unit = (a, b, u, v) ->
# number1, number2, unit, progress
a = +a
b -= a
a + b * v + u
color =
interpolate.color = (a, b, v, h) ->
# rgba1, rgba2, progress, convertToHex(true/false)
_c = {}
c = undefined
n = number
ep = ')'
cm = ','
r = 'rgb('
ra = 'rgba('
for c of b
`c = c`
_c[c] = if c != 'a' then number(a[c], b[c], v) >> 0 or 0 else if a[c] and b[c] then (number(a[c], b[c], v) * 100 >> 0) / 100 else null
if h then rgbToHex(_c.r, _c.g, _c.b) else if !_c.a then r + _c.r + cm + _c.g + cm + _c.b + ep else ra + _c.r + cm + _c.g + cm + _c.b + cm + _c.a + ep
translate = interpolate.translate = if isMobile then ((a, b, u, v) ->
translation = {}
for ax of b
translation[ax] = (if a[ax] == b[ax] then b[ax] else a[ax] + (b[ax] - (a[ax])) * v >> 0) + u
if translation.x or translation.y then 'translate(' + translation.x + ',' + translation.y + ')' else 'translate3d(' + translation.translateX + ',' + translation.translateY + ',' + translation.translateZ + ')'
) else ((a, b, u, v) ->
translation = {}
for ax of b
translation[ax] = (if a[ax] == b[ax] then b[ax] else ((a[ax] + (b[ax] - (a[ax])) * v) * 100 >> 0) / 100) + u
if translation.x or translation.y then 'translate(' + translation.x + ',' + translation.y + ')' else 'translate3d(' + translation.translateX + ',' + translation.translateY + ',' + translation.translateZ + ')'
)
rotate =
interpolate.rotate = (a, b, u, v) ->
rotation = {}
for rx of b
rotation[rx] = if rx == 'z' then 'rotate(' + ((a[rx] + (b[rx] - (a[rx])) * v) * 100 >> 0) / 100 + u + ')' else rx + '(' + ((a[rx] + (b[rx] - (a[rx])) * v) * 100 >> 0) / 100 + u + ')'
if rotation.z then rotation.z else (rotation.rotateX or '') + (rotation.rotateY or '') + (rotation.rotateZ or '')
skew =
interpolate.skew = (a, b, u, v) ->
skewProp = {}
for sx of b
skewProp[sx] = sx + '(' + ((a[sx] + (b[sx] - (a[sx])) * v) * 10 >> 0) / 10 + u + ')'
(skewProp.skewX or '') + (skewProp.skewY or '')
scale =
interpolate.scale = (a, b, v) ->
'scale(' + ((a + (b - a) * v) * 1000 >> 0) / 1000 + ')'
DOM = {}
ticker = (t) ->
`var i`
i = 0
while i < tweens.length
if update.call(tweens[i], t)
i++
else
tweens.splice i, 1
tick = _requestAnimationFrame(ticker)
return
update = (t) ->
`var p`
`var i`
t = t or time.now()
if t < @_startTime and @playing
return true
elapsed = Math.min((t - (@_startTime)) / @options.duration, 1)
progress = @options.easing(elapsed)
# calculate progress
for p of @valuesEnd
DOM[p] @element, p, @valuesStart[p], @valuesEnd[p], progress, @options
#render the CSS update
if @options.update
@options.update.call()
# fire the updateCallback
if elapsed == 1
if @options.repeat > 0
if isFinite(@options.repeat)
@options.repeat--
if @options.yoyo
# handle yoyo
@reversed = !@reversed
reverse.call this
@_startTime = if @options.yoyo and !@reversed then t + @options.repeatDelay else t
#set the right time for delay
return true
else
if @options.complete
@options.complete.call()
scrollOut.call this
# unbind preventing scroll when scroll tween finished
i = 0
ctl = @options.chain.length
while i < ctl
# start animating chained tweens
@options.chain[i].start()
i++
#stop ticking when finished
close.call this
return false
true
perspective = ->
el = @element
ops = @options
if ops.perspective != undefined and transformProperty of @valuesEnd
# element perspective
@valuesStart[transformProperty]['perspective'] = @valuesEnd[transformProperty]['perspective']
# element transform origin / we filter it out for svgTransform to fix the Firefox transformOrigin bug https://bugzilla.mozilla.org/show_bug.cgi?id=923193
if ops.transformOrigin != undefined and !('svgTransform' of @valuesEnd)
el.style[property('transformOrigin')] = ops.transformOrigin
# set transformOrigin for CSS3 transforms only
if ops.perspectiveOrigin != undefined
el.style[property('perspectiveOrigin')] = ops.perspectiveOrigin
# element perspective origin
if ops.parentPerspective != undefined
el.parentNode.style[property('perspective')] = ops.parentPerspective + 'px'
# parent perspective
if ops.parentPerspectiveOrigin != undefined
el.parentNode.style[property('perspectiveOrigin')] = ops.parentPerspectiveOrigin
# parent perspective origin
return
prepareStart = {}
crossCheck = {}
parseProperty =
boxModel: (p, v) ->
if !(p of DOM)
DOM[p] = (l, p, a, b, v) ->
l.style[p] = (if v > 0.99 or v < 0.01 then (number(a, b, v) * 10 >> 0) / 10 else number(a, b, v) >> 0) + 'px'
return
boxValue = trueDimension(v)
if boxValue.u == '%' then boxValue.v * @element.offsetWidth / 100 else boxValue.v
transform: (p, v) ->
if !(transformProperty of DOM)
DOM[transformProperty] = (l, p, a, b, v, o) ->
l.style[p] = (a.perspective or '') + (if 'translate' of a then translate(a.translate, b.translate, 'px', v) else '') + (if 'rotate' of a then rotate(a.rotate, b.rotate, 'deg', v) else '') + (if 'skew' of a then skew(a.skew, b.skew, 'deg', v) else '') + (if 'scale' of a then scale(a.scale, b.scale, v) else '')
return
# process each transform property
if /translate/.test(p)
if p == 'translate3d'
t3d = v.split(',')
t3d0 = trueDimension(t3d[0])
t3d1 = trueDimension(t3d[1], t3d2 = trueDimension(t3d[2]))
return {
translateX: if t3d0.u == '%' then t3d0.v * @element.offsetWidth / 100 else t3d0.v
translateY: if t3d1.u == '%' then t3d1.v * @element.offsetHeight / 100 else t3d1.v
translateZ: if t3d2.u == '%' then t3d2.v * (@element.offsetHeight + @element.offsetWidth) / 200 else t3d2.v
}
else if /^translate(?:[XYZ])$/.test(p)
t1d = trueDimension(v)
percentOffset = if /X/.test(p) then @element.offsetWidth / 100 else if /Y/.test(p) then @element.offsetHeight / 100 else (@element.offsetWidth + @element.offsetHeight) / 200
return if t1d.u == '%' then t1d.v * percentOffset else t1d.v
else if p == 'translate'
tv = if typeof v == 'string' then v.split(',') else v
t2d = {}
t2dv = undefined
t2d0 = trueDimension(tv[0])
t2d1 = if tv.length then trueDimension(tv[1]) else
v: 0
u: 'px'
if tv instanceof Array
t2d.x = if t2d0.u == '%' then t2d0.v * @element.offsetWidth / 100 else t2d0.v
t2d.y = if t2d1.u == '%' then t2d1.v * @element.offsetHeight / 100 else t2d1.v
else
t2dv = trueDimension(tv)
t2d.x = if t2dv.u == '%' then t2dv.v * @element.offsetWidth / 100 else t2dv.v
t2d.y = 0
return t2d
else if /rotate|skew/.test(p)
if /^rotate(?:[XYZ])$|skew(?:[XY])$/.test(p)
r3d = trueDimension(v, true)
return if r3d.u == 'rad' then radToDeg(r3d.v) else r3d.v
else if p == 'rotate'
r2d = {}
r2dv = trueDimension(v, true)
r2d.z = if r2dv.u == 'rad' then radToDeg(r2dv.v) else r2dv.v
return r2d
else if p == 'scale'
return parseFloat(v)
# this must be parseFloat(v)
return
unitless: (p, v) ->
# scroll | opacity
if /scroll/.test(p) and !(p of DOM)
DOM[p] = (l, p, a, b, v) ->
l.scrollTop = number(a, b, v) >> 0
return
else if p == 'opacity'
if !(p of DOM)
if isIE8
DOM[p] = (l, p, a, b, v) ->
st = 'alpha(opacity='
ep = ')'
l.style.filter = st + (number(a, b, v) * 100 >> 0) + ep
return
else
DOM[p] = (l, p, a, b, v) ->
l.style.opacity = (number(a, b, v) * 100 >> 0) / 100
return
parseFloat v
colors: (p, v) ->
# colors
if !(p of DOM)
DOM[p] = (l, p, a, b, v, o) ->
l.style[p] = color(a, b, v, o.keepHex)
return
trueColor v
preparePropertiesObject = (obj, fn) ->
# this, props object, type: start/end
element = @element
propertiesObject = if fn == 'start' then @valuesStart else @valuesEnd
skewObject = {}
rotateObject = {}
translateObject = {}
transformObject = {}
for x of obj
if _transform.indexOf(x) != -1
# transform object gets built here
if /^translate(?:[XYZ]|3d)$/.test(x)
#process translate3d
ta = [
'X'
'Y'
'Z'
]
#coordinates // translate[x] = pp(x, obj[x]);
f = 0
while f < 3
a = ta[f]
if /3d/.test(x)
translateObject['translate' + a] = parseProperty.transform.call(this, 'translate' + a, obj[x][f])
else
translateObject['translate' + a] = if 'translate' + a of obj then parseProperty.transform.call(this, 'translate' + a, obj['translate' + a]) else 0
f++
transformObject['translate'] = translateObject
else if /^rotate(?:[XYZ])$|^skew(?:[XY])$/.test(x)
#process rotation/skew
ap = if /rotate/.test(x) then 'rotate' else 'skew'
ra = [
'X'
'Y'
'Z'
]
rtp = if ap == 'rotate' then rotateObject else skewObject
r = 0
while r < 3
v = ra[r]
if obj[ap + v] != undefined and x != 'skewZ'
rtp[ap + v] = parseProperty.transform.call(this, ap + v, obj[ap + v])
r++
transformObject[ap] = rtp
else if /(rotate|translate|scale)$/.test(x)
#process 2d translation / rotation
transformObject[x] = parseProperty.transform.call(this, x, obj[x])
propertiesObject[transformProperty] = transformObject
else
if _boxModel.indexOf(x) != -1
propertiesObject[x] = parseProperty.boxModel.call(this, x, obj[x])
else if _opacity.indexOf(x) != -1 or x == 'scroll'
propertiesObject[x] = parseProperty.unitless.call(this, x, obj[x])
else if _colors.indexOf(x) != -1
propertiesObject[x] = parseProperty.colors.call(this, x, obj[x])
else if x of parseProperty
# or any other property from css/ attr / svg / third party plugins
propertiesObject[x] = parseProperty[x].call(this, x, obj[x])
return
reverse = ->
`var p`
if @options.yoyo
for p of @valuesEnd
tmp = @valuesRepeat[p]
@valuesRepeat[p] = @valuesEnd[p]
@valuesEnd[p] = tmp
@valuesStart[p] = @valuesRepeat[p]
return
close = ->
# when animation is finished reset repeat, yoyo&reversed tweens
if @repeat > 0
@options.repeat = @repeat
if @options.yoyo and @reversed == true
reverse.call this
@reversed = false
@playing = false
setTimeout (->
if !tweens.length
stop()
return
), 48
# when all animations are finished, stop ticking after ~3 frames
return
preventScroll = (e) ->
# prevent mousewheel or touch events while tweening scroll
data = document.body.getAttribute('data-tweening')
if data and data == 'scroll'
e.preventDefault()
return
scrollOut = ->
#prevent scroll when tweening scroll
if 'scroll' of @valuesEnd and document.body.getAttribute('data-tweening')
document.removeEventListener touchOrWheel, preventScroll, false
document.removeEventListener mouseEnter, preventScroll, false
document.body.removeAttribute 'data-tweening'
return
scrollIn = ->
if 'scroll' of @valuesEnd and !document.body.getAttribute('data-tweening')
document.addEventListener touchOrWheel, preventScroll, false
document.addEventListener mouseEnter, preventScroll, false
document.body.setAttribute 'data-tweening', 'scroll'
return
processEasing = (fn) ->
#process easing function
if typeof fn == 'function'
return fn
else if typeof fn == 'string'
return easing[fn]
# regular Robert Penner Easing Functions
return
getStartValues = ->
`var p`
`var p`
# stack transform props for .to() chains
startValues = {}
currentStyle = getInlineStyle(@element, 'transform')
deg = [
'rotate'
'skew'
]
ax = [
'X'
'Y'
'Z'
]
for p of @valuesStart
if _transform.indexOf(p) != -1
r2d = /(rotate|translate|scale)$/.test(p)
if /translate/.test(p) and p != 'translate'
startValues['translate3d'] = currentStyle['translate3d'] or _defaults[p]
else if r2d
# 2d transforms
startValues[p] = currentStyle[p] or _defaults[p]
else if !r2d and /rotate|skew/.test(p)
# all angles
d = 0
while d < 2
a = 0
while a < 3
s = deg[d] + ax[a]
if _transform.indexOf(s) != -1 and s of @valuesStart
startValues[s] = currentStyle[s] or _defaults[s]
a++
d++
else
if p != 'scroll'
if p == 'opacity' and isIE8
# handle IE8 opacity
currentOpacity = getCurrentStyle(@element, 'filter')
startValues['opacity'] = if typeof currentOpacity == 'number' then currentOpacity else _defaults['opacity']
else
if _all.indexOf(p) != -1
startValues[p] = getCurrentStyle(@element, p) or d[p]
else
# plugins register here
startValues[p] = if p of prepareStart then prepareStart[p].call(this, p, @valuesStart[p]) else 0
else
startValues[p] = if @element == scrollContainer then g.pageYOffset or scrollContainer.scrollTop else @element.scrollTop
for p of currentStyle
# also add to startValues values from previous tweens
if _transform.indexOf(p) != -1 and !(p of @valuesStart)
startValues[p] = currentStyle[p] or _defaults[p]
@valuesStart = {}
preparePropertiesObject.call this, startValues, 'start'
if transformProperty of @valuesEnd
# let's stack transform
for sp of @valuesStart[transformProperty]
# sp is the object corresponding to the transform function objects translate / rotate / skew / scale
if sp != 'perspective'
if typeof @valuesStart[transformProperty][sp] == 'object'
for spp of @valuesStart[transformProperty][sp]
# 3rd level
if typeof @valuesEnd[transformProperty][sp] == 'undefined'
@valuesEnd[transformProperty][sp] = {}
if typeof @valuesStart[transformProperty][sp][spp] == 'number' and typeof @valuesEnd[transformProperty][sp][spp] == 'undefined'
@valuesEnd[transformProperty][sp][spp] = @valuesStart[transformProperty][sp][spp]
else if typeof @valuesStart[transformProperty][sp] == 'number'
if typeof @valuesEnd[transformProperty][sp] == 'undefined'
# scale
@valuesEnd[transformProperty][sp] = @valuesStart[transformProperty][sp]
return
# core easing functions
easing = g.Easing = {}
easing.linear = (t) ->
t
easing.easingSinusoidalIn = (t) ->
-Math.cos(t * Math.PI / 2) + 1
easing.easingSinusoidalOut = (t) ->
Math.sin t * Math.PI / 2
easing.easingSinusoidalInOut = (t) ->
-0.5 * (Math.cos(Math.PI * t) - 1)
easing.easingQuadraticIn = (t) ->
t * t
easing.easingQuadraticOut = (t) ->
t * (2 - t)
easing.easingQuadraticInOut = (t) ->
if t < .5 then 2 * t * t else -1 + (4 - (2 * t)) * t
easing.easingCubicIn = (t) ->
t * t * t
easing.easingCubicOut = (t) ->
--t * t * t + 1
easing.easingCubicInOut = (t) ->
if t < .5 then 4 * t * t * t else (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
easing.easingQuarticIn = (t) ->
t * t * t * t
easing.easingQuarticOut = (t) ->
1 - (--t * t * t * t)
easing.easingQuarticInOut = (t) ->
if t < .5 then 8 * t * t * t * t else 1 - (8 * --t * t * t * t)
easing.easingQuinticIn = (t) ->
t * t * t * t * t
easing.easingQuinticOut = (t) ->
1 + --t * t * t * t * t
easing.easingQuinticInOut = (t) ->
if t < .5 then 16 * t * t * t * t * t else 1 + 16 * --t * t * t * t * t
easing.easingCircularIn = (t) ->
-(Math.sqrt(1 - (t * t)) - 1)
easing.easingCircularOut = (t) ->
Math.sqrt 1 - ((t = t - 1) * t)
easing.easingCircularInOut = (t) ->
if (t *= 2) < 1 then -0.5 * (Math.sqrt(1 - (t * t)) - 1) else 0.5 * (Math.sqrt(1 - ((t -= 2) * t)) + 1)
easing.easingExponentialIn = (t) ->
2 ** (10 * (t - 1)) - 0.001
easing.easingExponentialOut = (t) ->
1 - 2 ** (-10 * t)
easing.easingExponentialInOut = (t) ->
if (t *= 2) < 1 then 0.5 * 2 ** (10 * (t - 1)) else 0.5 * (2 - 2 ** (-10 * (t - 1)))
easing.easingBackIn = (t) ->
s = 1.70158
t * t * ((s + 1) * t - s)
easing.easingBackOut = (t) ->
s = 1.70158
--t * t * ((s + 1) * t + s) + 1
easing.easingBackInOut = (t) ->
s = 1.70158 * 1.525
if (t *= 2) < 1
return 0.5 * t * t * ((s + 1) * t - s)
0.5 * ((t -= 2) * t * ((s + 1) * t + s) + 2)
easing.easingElasticIn = (t) ->
s = undefined
_kea = 0.1
_kep = 0.4
if t == 0
return 0
if t == 1
return 1
if !_kea or _kea < 1
_kea = 1
s = _kep / 4
else
s = _kep * Math.asin(1 / _kea) / Math.PI * 2
-(_kea * 2 ** (10 * (t -= 1)) * Math.sin((t - s) * Math.PI * 2 / _kep))
easing.easingElasticOut = (t) ->
s = undefined
_kea = 0.1
_kep = 0.4
if t == 0
return 0
if t == 1
return 1
if !_kea or _kea < 1
_kea = 1
s = _kep / 4
else
s = _kep * Math.asin(1 / _kea) / Math.PI * 2
_kea * 2 ** (-10 * t) * Math.sin((t - s) * Math.PI * 2 / _kep) + 1
easing.easingElasticInOut = (t) ->
s = undefined
_kea = 0.1
_kep = 0.4
if t == 0
return 0
if t == 1
return 1
if !_kea or _kea < 1
_kea = 1
s = _kep / 4
else
s = _kep * Math.asin(1 / _kea) / Math.PI * 2
if (t *= 2) < 1
return -0.5 * _kea * 2 ** (10 * (t -= 1)) * Math.sin((t - s) * Math.PI * 2 / _kep)
_kea * 2 ** (-10 * (t -= 1)) * Math.sin((t - s) * Math.PI * 2 / _kep) * 0.5 + 1
easing.easingBounceIn = (t) ->
1 - easing.easingBounceOut(1 - t)
easing.easingBounceOut = (t) ->
if t < 1 / 2.75
7.5625 * t * t
else if t < 2 / 2.75
7.5625 * (t -= 1.5 / 2.75) * t + 0.75
else if t < 2.5 / 2.75
7.5625 * (t -= 2.25 / 2.75) * t + 0.9375
else
7.5625 * (t -= 2.625 / 2.75) * t + 0.984375
easing.easingBounceInOut = (t) ->
if t < 0.5
return easing.easingBounceIn(t * 2) * 0.5
easing.easingBounceOut(t * 2 - 1) * 0.5 + 0.5
# single Tween object construct
Tween = (targetElement, startObject, endObject, options) ->
@element = if 'scroll' of endObject and (targetElement == undefined or targetElement == null) then scrollContainer else targetElement
# element animation is applied to
@playing = false
@reversed = false
@paused = false
@_startTime = null
@_pauseTime = null
@_startFired = false
@options = {}
for o of options
@options[o] = options[o]
@options.rpr = options.rpr or false
# internal option to process inline/computed style at start instead of init true/false
@valuesRepeat = {}
# internal valuesRepeat
@valuesEnd = {}
# valuesEnd
@valuesStart = {}
# valuesStart
preparePropertiesObject.call this, endObject, 'end'
# valuesEnd
if @options.rpr
@valuesStart = startObject
else
preparePropertiesObject.call this, startObject, 'start'
# valuesStart
if @options.perspective != undefined and transformProperty of @valuesEnd
# element transform perspective
perspectiveString = 'perspective(' + parseInt(@options.perspective) + 'px)'
@valuesEnd[transformProperty].perspective = perspectiveString
for e of @valuesEnd
if e of crossCheck and !@options.rpr
crossCheck[e].call this
# this is where we do the valuesStart and valuesEnd check for fromTo() method
@options.chain = []
# chained Tweens
@options.easing = if options.easing and typeof processEasing(options.easing) == 'function' then processEasing(options.easing) else easing[defaultOptions.easing]
# you can only set a core easing function as default
@options.repeat = options.repeat or defaultOptions.repeat
@options.repeatDelay = options.repeatDelay or defaultOptions.repeatDelay
@options.yoyo = options.yoyo or defaultOptions.yoyo
@options.duration = options.duration or defaultOptions.duration
# duration option | default
@options.delay = options.delay or defaultOptions.delay
# delay option | default
@repeat = @options.repeat
# we cache the number of repeats to be able to put it back after all cycles finish
return
TweenProto = Tween.prototype =
start: (t) ->
# move functions that use the ticker outside the prototype to be in the same scope with it
scrollIn.call this
if @options.rpr
getStartValues.apply this
# on start we reprocess the valuesStart for TO() method
perspective.apply this
# apply the perspective and transform origin
for e of @valuesEnd
if e of crossCheck and @options.rpr
crossCheck[e].call this
# this is where we do the valuesStart and valuesEnd check for to() method
@valuesRepeat[e] = @valuesStart[e]
# now it's a good time to start
tweens.push this
@playing = true
@paused = false
@_startFired = false
@_startTime = t or time.now()
@_startTime += @options.delay
if !@_startFired
if @options.start
@options.start.call()
@_startFired = true
!tick and ticker()
this
play: ->
if @paused and @playing
@paused = false
if @options.resume
@options.resume.call()
@_startTime += time.now() - (@_pauseTime)
add this
!tick and ticker()
# restart ticking if stopped
this
resume: ->
@play()
pause: ->
if !@paused and @playing
remove this
@paused = true
@_pauseTime = time.now()
if @options.pause
@options.pause.call()
this
stop: ->
if !@paused and @playing
remove this
@playing = false
@paused = false
scrollOut.call this
if @options.stop
@options.stop.call()
@stopChainedTweens()
close.call this
this
chain: ->
@options.chain = arguments
this
stopChainedTweens: ->
`var i`
i = 0
ctl = @options.chain.length
while i < ctl
@options.chain[i].stop()
i++
return
TweensTO = (els, vE, o) ->
`var i`
# .to
@tweens = []
options = []
i = 0
tl = els.length
while i < tl
options[i] = o or {}
o.delay = o.delay or defaultOptions.delay
options[i].delay = if i > 0 then o.delay + (o.offset or defaultOptions.offset) else o.delay
@tweens.push to(els[i], vE, options[i])
i++
return
TweensFT = (els, vS, vE, o) ->
`var i`
# .fromTo
@tweens = []
options = []
i = 0
l = els.length
while i < l
options[i] = o or {}
o.delay = o.delay or defaultOptions.delay
options[i].delay = if i > 0 then o.delay + (o.offset or defaultOptions.offset) else o.delay
@tweens.push fromTo(els[i], vS, vE, options[i])
i++
return
ws = TweensTO.prototype = TweensFT.prototype =
start: (t) ->
`var i`
t = t or time.now()
i = 0
tl = @tweens.length
while i < tl
@tweens[i].start t
i++
this
stop: ->
`var i`
i = 0
tl = @tweens.length
while i < tl
@tweens[i].stop()
i++
this
pause: ->
`var i`
i = 0
tl = @tweens.length
while i < tl
@tweens[i].pause()
i++
this
chain: ->
@tweens[@tweens.length - 1].options.chain = arguments
this
play: ->
`var i`
i = 0
tl = @tweens.length
while i < tl
@tweens[i].play()
i++
this
resume: ->
@play()
to = (element, endObject, options) ->
options = options or {}
options.rpr = true
new Tween(selector(element), endObject, endObject, options)
fromTo = (element, startObject, endObject, options) ->
options = options or {}
new Tween(selector(element), startObject, endObject, options)
allTo = (elements, endObject, options) ->
new TweensTO(selector(elements, true), endObject, options)
allFromTo = (elements, f, endObject, options) ->
new TweensFT(selector(elements, true), f, endObject, options)
{
property: property
getPrefix: getPrefix
selector: selector
processEasing: processEasing
defaultOptions: defaultOptions
to: to
fromTo: fromTo
allTo: allTo
allFromTo: allFromTo
ticker: ticker
tick: tick
tweens: tweens
update: update
dom: DOM
parseProperty: parseProperty
prepareStart: prepareStart
crossCheck: crossCheck
Tween: Tween
truD: trueDimension
truC: trueColor
rth: rgbToHex
htr: hexToRGB
getCurrentStyle: getCurrentStyle
}