2016-03-16 14:44:23 +01:00
/ * K U T E . j s - T h e L i g h t T w e e n i n g E n g i n e
* package - SVG Plugin
* desc - draw strokes , morph paths and SVG color props
* by dnp _theme
* Licensed under MIT - License
* /
( function ( factory ) {
if ( typeof define === 'function' && define . amd ) {
define ( [ "./kute.js" ] , function ( KUTE ) { factory ( KUTE ) ; return KUTE ; } ) ;
} else if ( typeof module == "object" && typeof require == "function" ) {
// We assume, that require() is sync.
var KUTE = require ( "./kute.js" ) ;
// Export the modified one. Not really required, but convenient.
module . exports = factory ( KUTE ) ;
} else if ( typeof window . KUTE !== 'undefined' ) {
// Browser globals
window . KUTE . svg = window . KUTE . svg || factory ( KUTE ) ;
} else {
throw new Error ( "SVG Plugin require KUTE.js." ) ;
}
} ( function ( KUTE ) {
'use strict' ;
2016-09-03 17:35:49 +02:00
var K = window . KUTE , S = S || { } , p , DOM = K . dom , PP = K . pp ,
2016-08-19 00:12:53 +02:00
_svg = document . querySelector ( 'path' ) || document . querySelector ( 'svg' ) ,
2016-03-16 14:44:23 +01:00
_ns = _svg && _svg . ownerSVGElement && _svg . ownerSVGElement . namespaceURI || 'http://www.w3.org/2000/svg' ,
_nm = [ 'strokeWidth' , 'strokeOpacity' , 'fillOpacity' , 'stopOpacity' ] , // numeric SVG CSS props
_cls = [ 'fill' , 'stroke' , 'stopColor' ] , // colors 'hex', 'rgb', 'rgba' -- #fff / rgb(0,0,0) / rgba(0,0,0,0)
2016-09-03 17:35:49 +02:00
pathReg = /(m[^(h|v|l)]*|[vhl][^(v|h|l|z)]*)/gmi ,
// interpolate functions
number = K . Interpolate . number , color = K . Interpolate . color ,
array = function array ( a , b , l , v ) { // array1, array2, array2.length, progress
var na = [ ] , i ;
for ( i = 0 ; i < l ; i ++ ) { na . push ( a [ i ] === b [ i ] ? b [ i ] : number ( a [ i ] , b [ i ] , v ) ) ; } // don't do math if not needed
return na ;
} ,
coords = function coords ( a , b , l , ll , o , v ) { // array1, array2, array2.length, coordinates.length, joiner, progress for SVG stuff
var s = [ ] , i ;
for ( i = 0 ; i < l ; i ++ ) { s . push ( array ( a [ i ] , b [ i ] , ll , v ) ) ; }
return s . join ( o ) ;
} ;
2016-03-16 14:44:23 +01:00
if ( _svg && ! _svg . ownerSVGElement ) { return ; } // if SVG API is not supported, return
// SVG MORPH
2016-08-20 23:11:42 +02:00
S . gPt = function ( e ) { // get path d attribute or create a path from string value
2016-03-16 14:44:23 +01:00
var p = { } , el = typeof e === 'object' ? e : /^\.|^\#/ . test ( e ) ? document . querySelector ( e ) : null ;
if ( el && /path|glyph/ . test ( el . tagName ) ) {
2016-03-24 13:27:27 +01:00
p . e = S . fPt ( el ) ;
2016-03-16 14:44:23 +01:00
p . o = el . getAttribute ( 'd' ) ;
} else if ( ! el && /[a-z][^a-z]*/ig . test ( e ) ) { // maybe it's a string path already
2016-09-03 17:35:49 +02:00
p . e = S . cP ( e . trim ( ) ) ;
2016-03-16 14:44:23 +01:00
p . o = e ;
}
return p ;
}
2016-03-24 13:27:27 +01:00
S . pCr = function ( w ) { // pathCross
2016-03-16 14:44:23 +01:00
// path tween options
2016-09-03 17:35:49 +02:00
this . _mpr = w . _ops . morphPrecision || 25 ;
this . _midx = w . _ops . morphIndex ;
this . _smi = w . _ops . showMorphInfo ;
this . _rv1 = w . _ops . reverseFirstPath ;
this . _rv2 = w . _ops . reverseSecondPath ;
2016-03-16 14:44:23 +01:00
2016-03-24 13:27:27 +01:00
var p1 = S . gOp ( w . _vS . path . o ) , p2 = S . gOp ( w . _vE . path . o ) , arr ;
this . _isp = ! /[CSQTA]/i . test ( p1 ) && ! /[CSQTA]/i . test ( p2 ) ; // both shapes are polygons
2016-03-16 14:44:23 +01:00
2016-03-24 13:27:27 +01:00
arr = S . _pCr ( p1 , p2 , w . _el . parentNode ) ;
2016-03-16 14:44:23 +01:00
w . _vS . path . d = arr [ 0 ] ;
w . _vE . path . d = arr [ 1 ] ;
}
2016-03-24 13:27:27 +01:00
S . _pCr = function ( s , e , svg ) { // _pathCross
var s1 , e1 , arr , idx , arL , sm , lg , smp , lgp , nsm = [ ] , sml , cl = [ ] , len , tl , cs ;
this . _sp = false ;
2016-03-16 14:44:23 +01:00
2016-03-24 13:27:27 +01:00
if ( ! this . _isp ) {
s = S . cP ( s ) ; e = S . cP ( e ) ;
arr = S . gSegs ( s , e , this . _mpr ) ;
s1 = arr [ 0 ] ; e1 = arr [ 1 ] ; arL = e1 . length ;
} else {
2016-08-22 16:10:26 +02:00
s = S . pTA ( s ) ; e = S . pTA ( e ) ;
2016-03-24 13:27:27 +01:00
2016-08-22 16:10:26 +02:00
if ( s . length !== e . length ) {
arL = Math . max ( s . length , e . length ) ;
if ( arL === e . length ) { sm = s ; lg = e ; } else { sm = e ; lg = s ; }
sml = sm . length ;
smp = S . cP ( 'M' + sm . join ( 'L' ) + 'z' ) ; len = smp . getTotalLength ( ) / arL ;
for ( var i = 0 ; i < arL ; i ++ ) {
tl = smp . getPointAtLength ( len * i ) ;
cs = S . gCP ( len , tl , sm ) ;
nsm . push ( [ cs [ 0 ] , cs [ 1 ] ] ) ;
}
2016-03-24 13:27:27 +01:00
2016-08-22 16:10:26 +02:00
if ( arL === e . length ) { e1 = lg ; s1 = nsm ; } else { s1 = lg ; e1 = nsm ; }
} else {
s1 = s ; e1 = e ;
}
2016-03-24 13:27:27 +01:00
}
2016-03-16 14:44:23 +01:00
// reverse arrays
if ( this . _rv1 ) { s1 . reverse ( ) ; }
if ( this . _rv2 ) { e1 . reverse ( ) ; }
// determine index for best/minimum distance between points
2016-03-24 13:27:27 +01:00
if ( this . _smi ) { idx = S . gBi ( s1 , e1 ) ; }
2016-03-16 14:44:23 +01:00
// shift second array to for smallest tween distance
if ( this . _midx ) {
var e11 = e1 . splice ( this . _midx , arL - this . _midx ) ;
e1 = e11 . concat ( e1 ) ;
}
// the console.log helper utility
if ( this . _smi ) {
2016-03-24 13:27:27 +01:00
// also show the points
S . shP ( s1 , e1 , svg ) ;
var mpi = this . _isp ? 'the polygon with the most points.\n' : ( this . _mpr === 25 ? 'the default' : 'your' ) + ' morphPrecision value of ' + this . _mpr + '.\n' ;
console . log ( 'KUTE.js Path Morph Log\nThe morph used ' + arL + ' points to draw both paths based on ' + mpi
2016-03-16 14:44:23 +01:00
+ ( this . _midx ? 'You\'ve configured the morphIndex to ' + this . _midx + ' while the recommended is ' + idx + '.\n' : 'You may also consider a morphIndex for the second path. Currently the best index seems to be ' + idx + '.\n' )
+ (
! this . _rv1 && ! this . _rv2 ? 'If the current animation is not satisfactory, consider reversing one of the paths. Maybe the paths do not intersect or they really have different draw directions.' :
'You\'ve chosen that the first path to have ' + ( this . _rv1 ? 'REVERSED draw direction, ' : 'UNCHANGED draw direction, ' ) + 'while second path is to be ' + ( this . _rv2 ? 'REVERSED.\n' : 'UNCHANGED.\n' )
)
) ;
}
s = e = null ;
return [ s1 , e1 ]
}
2016-03-24 13:27:27 +01:00
S . gSegs = function ( s , e , r ) { // getSegments returns an array of points based on a sample size morphPrecision
2016-03-16 14:44:23 +01:00
var s1 = [ ] , e1 = [ ] , le1 = s . getTotalLength ( ) , le2 = e . getTotalLength ( ) , ml = Math . max ( le1 , le2 ) ,
d = r , ar = ml / r , j = 0 , sl = ar * r ; // sl = sample length
2016-03-24 13:27:27 +01:00
while ( ( j += r ) < sl ) { // populate the points arrays based on morphPrecision as sample size
2016-03-16 14:44:23 +01:00
s1 . push ( [ s . getPointAtLength ( j ) . x , s . getPointAtLength ( j ) . y ] ) ;
e1 . push ( [ e . getPointAtLength ( j ) . x , e . getPointAtLength ( j ) . y ] ) ;
}
return [ s1 , e1 ] ;
2016-03-24 13:27:27 +01:00
}
S . gCP = function ( p , t , s ) { // getClosestPoint for polygon paths it returns a close point from the original path (length,pointAtLength,smallest); // intervalLength
var x , y , a = [ ] , l = s . length , dx , nx , pr ;
for ( i = 0 ; i < l ; i ++ ) {
x = Math . abs ( s [ i ] [ 0 ] - t . x ) ;
y = Math . abs ( s [ i ] [ 1 ] - t . y ) ;
a . push ( Math . sqrt ( x * x + y * y ) ) ;
}
dx = a . indexOf ( Math . min . apply ( null , a ) ) ;
pr = ! ! s [ dx - 1 ] ? dx - 1 : l - 1 ;
nx = ! ! s [ dx + 1 ] ? dx + 1 : 0 ;
return Math . abs ( s [ pr ] [ 0 ] - t . x ) < p && Math . abs ( s [ pr ] [ 1 ] - t . y ) < p ? s [ pr ]
: Math . abs ( s [ nx ] [ 0 ] - t . x ) < p && Math . abs ( s [ nx ] [ 1 ] - t . y ) < p ? s [ nx ]
: Math . abs ( s [ dx ] [ 0 ] - t . x ) < p && Math . abs ( s [ dx ] [ 1 ] - t . y ) < p ? s [ dx ]
: [ t . x , t . y ] ;
}
S . shP = function ( s , e , v ) { // showPoints helper function to visualize the points on the path
if ( ! this . _sp ) {
var c , a = arguments , cl , p , l ;
for ( var i = 0 ; i < 2 ; i ++ ) {
p = a [ i ] ; l = p . length ; cl = i === 0 ? { f : 'DeepPink' , o : 'HotPink' } : { f : 'Lime' , o : 'LimeGreen' } ;
for ( var j = 0 ; j < l ; j ++ ) {
c = document . createElementNS ( _ns , 'circle' ) ;
c . setAttribute ( 'cx' , p [ j ] [ 0 ] ) ; c . setAttribute ( 'cy' , p [ j ] [ 1 ] ) ;
c . setAttribute ( 'r' , j === 0 ? 20 : 10 ) ; c . setAttribute ( 'fill' , j === 0 ? cl . f : cl . o ) ;
if ( this . _isp ) { v . appendChild ( c ) ; } else if ( ! this . _isp && j === 0 ) { v . appendChild ( c ) ; }
}
}
this . _sp = true ; c = null ;
}
}
2016-03-16 14:44:23 +01:00
2016-03-24 13:27:27 +01:00
S . gBi = function ( s , e ) { // getBestIndex for shape rotation
var s1 = S . clone ( s ) , e1 = S . clone ( e ) , d = [ ] , i , l = e . length , t , ax , ay ;
2016-03-16 14:44:23 +01:00
for ( i = 0 ; i < l ; i ++ ) {
t = e1 . splice ( i , l - i ) ; e1 = t . concat ( e1 ) ;
ax = Math . abs ( s1 [ i ] [ 0 ] - e1 [ i ] [ 0 ] ) ;
ay = Math . abs ( s1 [ i ] [ 1 ] - e1 [ i ] [ 1 ] ) ;
d . push ( Math . sqrt ( ax * ax + ay * ay ) ) ;
e1 = [ ] ; e1 = S . clone ( e ) ; t = null ;
}
return d . indexOf ( Math . min . apply ( null , d ) ) ;
}
2016-03-24 13:27:27 +01:00
S . gOp = function ( p ) { // getOnePath, first path only
2016-09-03 17:35:49 +02:00
return p . split ( /z/i ) . shift ( ) + 'z' ;
2016-03-16 14:44:23 +01:00
}
2016-03-24 13:27:27 +01:00
S . cP = function ( p ) { // createPath
2016-03-16 14:44:23 +01:00
var c = document . createElementNS ( _ns , 'path' ) , d = typeof p === 'object' ? p . getAttribute ( 'd' ) : p ;
c . setAttribute ( 'd' , d ) ; return c ;
}
2016-03-24 13:27:27 +01:00
S . fPt = function ( p ) { // forcePath for glyph elements
2016-03-16 14:44:23 +01:00
if ( p . tagName === 'glyph' ) { // perhaps we can also change other SVG tags in the future
2016-03-24 13:27:27 +01:00
var c = S . cP ( p ) ; p . parentNode . appendChild ( c ) ; return c ;
2016-03-16 14:44:23 +01:00
}
return p ;
}
2016-03-24 13:27:27 +01:00
S . clone = function ( a ) {
2016-03-16 14:44:23 +01:00
var copy ;
2016-03-24 13:27:27 +01:00
if ( a instanceof Array ) {
2016-03-16 14:44:23 +01:00
copy = [ ] ;
2016-03-24 13:27:27 +01:00
for ( var i = 0 , len = a . length ; i < len ; i ++ ) {
copy [ i ] = S . clone ( a [ i ] ) ;
2016-03-16 14:44:23 +01:00
}
return copy ;
}
2016-03-24 13:27:27 +01:00
return a ;
}
2016-08-22 00:45:23 +02:00
S . pTA = function ( p ) { // simple pathToAbsolute for polygons | this is still BETA / a work in progress
2016-03-24 13:27:27 +01:00
var np = p . match ( pathReg ) , wp = [ ] , l = np . length , s , c , r , x = 0 , y = 0 ;
for ( var i = 0 ; i < l ; i ++ ) {
np [ i ] = np [ i ] ; c = np [ i ] [ 0 ] ; r = new RegExp ( c + '[^\\d|\\-]*' , 'i' ) ;
np [ i ] = np [ i ] . replace ( /(^|[^,])\s*-/g , '$1,-' ) . replace ( /(\s+\,|\s|\,)/g , ',' ) . replace ( r , '' ) . split ( ',' ) ;
np [ i ] [ 0 ] = parseFloat ( np [ i ] [ 0 ] ) ;
np [ i ] [ 1 ] = parseFloat ( np [ i ] [ 1 ] ) ;
2016-09-03 17:35:49 +02:00
if ( i === 0 ) { x += np [ i ] [ 0 ] ; y += np [ i ] [ 1 ] ; }
2016-03-24 13:27:27 +01:00
else {
x = np [ i - 1 ] [ 0 ] ;
y = np [ i - 1 ] [ 1 ] ;
if ( /l/i . test ( c ) ) {
np [ i ] [ 0 ] = c === 'l' ? np [ i ] [ 0 ] + x : np [ i ] [ 0 ] ;
np [ i ] [ 1 ] = c === 'l' ? np [ i ] [ 1 ] + y : np [ i ] [ 1 ] ;
} else if ( /h/i . test ( c ) ) {
np [ i ] [ 0 ] = c === 'h' ? np [ i ] [ 0 ] + x : np [ i ] [ 0 ] ;
np [ i ] [ 1 ] = y ;
} else if ( /v/i . test ( c ) ) {
np [ i ] [ 0 ] = x ;
np [ i ] [ 1 ] = c === 'v' ? np [ i ] [ 0 ] + y : np [ i ] [ 0 ] ;
2016-03-16 14:44:23 +01:00
}
}
}
2016-03-24 13:27:27 +01:00
return np ;
}
2016-09-03 17:35:49 +02:00
// a shortHand pathCross && SVG transform stack
K . svq = function ( w ) { if ( w . _vE . path ) S . pCr ( w ) ; if ( w . _vE . svgTransform ) S . sT ( w ) ; }
2016-03-16 14:44:23 +01:00
// register the render SVG path object
// process path object and also register the render function
2016-03-24 13:27:27 +01:00
K . pp [ 'path' ] = function ( a , o , l ) { // K.pp['path']('path',value,element);
2016-09-03 17:35:49 +02:00
if ( ! ( 'path' in DOM ) ) {
DOM [ 'path' ] = function ( l , p , a , b , v ) {
l . setAttribute ( "d" , v === 1 ? b . o : 'M' + coords ( a [ 'd' ] , b [ 'd' ] , b [ 'd' ] . length , 2 , 'L' , v ) + 'Z' ) ;
2016-03-16 14:44:23 +01:00
}
}
2016-03-24 13:27:27 +01:00
return S . gPt ( o ) ;
2016-03-16 14:44:23 +01:00
} ;
K . prS [ 'path' ] = function ( el , p , v ) {
return el . getAttribute ( 'd' ) ;
} ;
// SVG DRAW
2016-03-24 13:27:27 +01:00
S . gDr = function ( e , v ) {
2016-08-20 14:58:30 +02:00
var l = /path|glyph/ . test ( e . tagName ) ? e . getTotalLength ( ) : S . gL ( e ) , start , end , d , o ;
2016-03-16 14:44:23 +01:00
if ( v instanceof Object ) {
return v ;
} else if ( typeof v === 'string' ) {
v = v . split ( /\,|\s/ ) ;
2016-03-24 13:27:27 +01:00
start = /%/ . test ( v [ 0 ] ) ? S . pc ( v [ 0 ] . trim ( ) , l ) : parseFloat ( v [ 0 ] ) ;
end = /%/ . test ( v [ 1 ] ) ? S . pc ( v [ 1 ] . trim ( ) , l ) : parseFloat ( v [ 1 ] ) ;
2016-03-16 14:44:23 +01:00
} else if ( typeof v === 'undefined' ) {
2016-09-03 17:35:49 +02:00
o = parseFloat ( K . gCS ( e , 'stroke-dashoffset' ) ) ;
d = K . gCS ( e , 'stroke-dasharray' ) . split ( /\,/ ) ;
2016-03-16 14:44:23 +01:00
start = 0 - o ;
end = parseFloat ( d [ 0 ] ) + start || l ;
}
return { s : start , e : end , l : l }
} ;
2016-09-03 17:35:49 +02:00
S . pc = function ( v , l ) { // percent
2016-03-16 14:44:23 +01:00
return parseFloat ( v ) / 100 * l ;
} ;
2016-09-03 17:35:49 +02:00
PP [ 'draw' ] = function ( a , o , f ) { // register the draw property
if ( ! ( 'draw' in DOM ) ) {
DOM [ 'draw' ] = function ( l , p , a , b , v ) {
var ll = a . l , s = number ( a . s , b . s , v ) , e = number ( a . e , b . e , v ) , o = 0 - s ;
l . style . strokeDashoffset = o + 'px' ;
l . style . strokeDasharray = e + o < 1 ? '0px, ' + ll + 'px' : ( e + o ) + 'px, ' + ll + 'px' ;
2016-03-16 14:44:23 +01:00
}
}
2016-09-03 17:35:49 +02:00
return S . gDr ( f , o ) ;
2016-03-16 14:44:23 +01:00
}
K . prS [ 'draw' ] = function ( el , p , v ) {
2016-09-03 17:35:49 +02:00
return S . gDr ( el ) ;
2016-03-16 14:44:23 +01:00
}
2016-08-20 14:58:30 +02:00
// SVG DRAW UTILITITES
2016-09-03 17:35:49 +02:00
// http://stackoverflow.com/a/30376660
2016-08-20 14:58:30 +02:00
S . gL = function ( el ) { // getLength - returns the result of any of the below functions
if ( /rect/ . test ( el . tagName ) ) {
return S . gRL ( el ) ;
} else if ( /circle/ . test ( el . tagName ) ) {
return S . gCL ( el ) ;
2016-08-20 23:11:42 +02:00
} else if ( /ellipse/ . test ( el . tagName ) ) {
return S . gEL ( el ) ;
2016-08-20 14:58:30 +02:00
} else if ( /polygon|polyline/ . test ( el . tagName ) ) {
return S . gPL ( el ) ;
} else if ( /line/ . test ( el . tagName ) ) {
return S . gLL ( el ) ;
}
}
S . gRL = function ( el ) { // getRectLength - return the length of a Rect
var w = el . getAttribute ( 'width' ) ;
var h = el . getAttribute ( 'height' ) ;
return ( w * 2 ) + ( h * 2 ) ;
}
2016-09-03 17:35:49 +02:00
S . gPL = function ( el ) { // getPolygonLength / getPolylineLength - return the length of the Polygon / Polyline
2016-08-20 14:58:30 +02:00
var points = el . getAttribute ( 'points' ) . split ( ' ' ) , len = 0 ;
if ( points . length > 1 ) {
2016-08-22 00:45:23 +02:00
var coord = function ( p ) {
2016-08-20 14:58:30 +02:00
var c = p . split ( ',' ) ;
if ( c . length != 2 ) {
return ; // return undefined
}
if ( isNaN ( c [ 0 ] ) || isNaN ( c [ 1 ] ) ) {
return ;
}
return [ parseFloat ( c [ 0 ] ) , parseFloat ( c [ 1 ] ) ] ;
2016-08-22 00:45:23 +02:00
} ;
2016-08-20 14:58:30 +02:00
2016-08-22 00:45:23 +02:00
var dist = function ( c1 , c2 ) {
2016-08-20 14:58:30 +02:00
if ( c1 != undefined && c2 != undefined ) {
return Math . sqrt ( Math . pow ( ( c2 [ 0 ] - c1 [ 0 ] ) , 2 ) + Math . pow ( ( c2 [ 1 ] - c1 [ 1 ] ) , 2 ) ) ;
}
return 0 ;
2016-08-22 00:45:23 +02:00
} ;
2016-08-20 14:58:30 +02:00
if ( points . length > 2 ) {
for ( var i = 0 ; i < points . length - 1 ; i ++ ) {
len += dist ( coord ( points [ i ] ) , coord ( points [ i + 1 ] ) ) ;
}
}
len += dist ( coord ( points [ 0 ] ) , coord ( points [ points . length - 1 ] ) ) ;
}
return len ;
}
S . gLL = function ( el ) { // getLineLength - return the length of the line
var x1 = el . getAttribute ( 'x1' ) ;
var x2 = el . getAttribute ( 'x2' ) ;
var y1 = el . getAttribute ( 'y1' ) ;
var y2 = el . getAttribute ( 'y2' ) ;
return Math . sqrt ( Math . pow ( ( x2 - x1 ) , 2 ) + Math . pow ( ( y2 - y1 ) , 2 ) ) ;
}
S . gCL = function ( el ) { // getCircleLength - return the length of the circle
var r = el . getAttribute ( 'r' ) ;
return 2 * Math . PI * r ;
2016-08-20 23:11:42 +02:00
}
S . gEL = function ( el ) { // getEllipseLength - returns the length of an ellipse
var rx = el . getAttribute ( 'rx' ) , ry = el . getAttribute ( 'ry' ) ,
len = 2 * rx , wid = 2 * ry ;
return ( ( Math . sqrt ( . 5 * ( ( len * len ) + ( wid * wid ) ) ) ) * ( Math . PI * 2 ) ) / 2 ;
2016-09-03 17:35:49 +02:00
}
2016-08-22 00:45:23 +02:00
// SVG CSS Color Properties
for ( var i = 0 , l = _cls . length ; i < l ; i ++ ) {
p = _cls [ i ] ;
2016-09-03 17:35:49 +02:00
PP [ p ] = function ( p , v ) {
if ( ! ( p in DOM ) ) {
DOM [ p ] = function ( l , p , a , b , v , o ) {
l . style [ p ] = color ( a , b , v , o . keepHex ) ;
} ;
2016-08-22 00:45:23 +02:00
}
return K . truC ( v ) ;
}
K . prS [ p ] = function ( el , p , v ) {
return K . gCS ( el , p ) || 'rgba(0,0,0,0)' ;
}
}
2016-09-03 17:35:49 +02:00
2016-08-22 00:45:23 +02:00
for ( var i = 0 , l = _nm . length ; i < l ; i ++ ) { // for numeric CSS props from any type of SVG shape
p = _nm [ i ] ;
2016-09-03 17:35:49 +02:00
if ( p === 'strokeWidth' ) { // stroke can be unitless or unit | http://stackoverflow.com/questions/1301685/fixed-stroke-width-in-svg
PP [ p ] = function ( p , v ) {
if ( ! ( p in DOM ) ) {
DOM [ p ] = function ( l , p , a , b , v ) {
var _u = _u || typeof b === 'number' ;
l . style [ p ] = ! _u ? unit ( a . value , b . value , b . unit , v ) : number ( a , b , v ) ;
2016-08-22 00:45:23 +02:00
}
}
2016-09-03 17:35:49 +02:00
return /px|%|em|vh|vw/ . test ( v ) ? PP . box ( p , v ) : parseFloat ( v ) ;
2016-08-22 00:45:23 +02:00
}
} else {
2016-09-03 17:35:49 +02:00
PP [ p ] = function ( p , v ) {
if ( ! ( p in DOM ) ) {
DOM [ p ] = function ( l , p , a , b , v ) {
l . style [ p ] = number ( a , b , v ) ;
2016-08-22 00:45:23 +02:00
}
}
return parseFloat ( v ) ;
}
}
K . prS [ p ] = function ( el , p , v ) {
return K . gCS ( el , p ) || 0 ;
}
}
// SVG Transform
2016-09-03 17:35:49 +02:00
PP [ 'svgTransform' ] = function ( p , v , l ) {
2016-08-22 00:45:23 +02:00
// register the render function
2016-09-03 17:35:49 +02:00
if ( ! ( 'svgTransform' in DOM ) ) {
DOM [ 'svgTransform' ] = function ( l , p , a , b , v ) {
2016-08-22 00:45:23 +02:00
var tr = '' , i ;
2016-09-03 17:35:49 +02:00
for ( i in b ) {
2016-08-22 00:45:23 +02:00
tr += i + '(' ; // start string
if ( i === 'translate' ) { // translate
2016-09-03 17:35:49 +02:00
tr += ( a [ i ] [ 1 ] === b [ i ] [ 1 ] && b [ i ] [ 1 ] === 0 )
? number ( a [ i ] [ 0 ] , b [ i ] [ 0 ] , v )
: number ( a [ i ] [ 0 ] , b [ i ] [ 0 ] , v ) + ' ' + number ( a [ i ] [ 1 ] , b [ i ] [ 1 ] , v ) ;
2016-08-22 00:45:23 +02:00
} else if ( i === 'rotate' ) { // rotate
2016-09-03 17:35:49 +02:00
tr += number ( a [ i ] [ 0 ] , b [ i ] [ 0 ] , v ) + ' ' ;
tr += b [ i ] [ 1 ] + ',' + b [ i ] [ 2 ] ;
2016-08-22 00:45:23 +02:00
} else { // scale, skewX or skewY
2016-09-03 17:35:49 +02:00
tr += number ( a [ i ] , b [ i ] , v ) ;
2016-08-22 00:45:23 +02:00
}
tr += ') ' ; // end string
}
2016-09-03 17:35:49 +02:00
l . setAttribute ( 'transform' , tr . trim ( ) ) ;
2016-08-22 00:45:23 +02:00
}
}
2016-09-03 17:35:49 +02:00
// now prepare transform
var tf = { } , bb = l . getBBox ( ) , cx = bb . x + bb . width / 2 , cy = bb . y + bb . height / 2 , r , cr , t , ct ;
for ( i in v ) { // populate the valuesStart and / or valuesEnd
2016-08-22 00:45:23 +02:00
if ( i === 'rotate' ) {
2016-09-03 17:35:49 +02:00
r = v [ i ] instanceof Array ? v [ i ]
2016-08-22 00:45:23 +02:00
: /\s/ . test ( v [ i ] ) ? [ v [ i ] . split ( ' ' ) [ 0 ] * 1 , v [ i ] . split ( ' ' ) [ 1 ] . split ( ',' ) [ 0 ] * 1 , v [ i ] . split ( ' ' ) [ 1 ] . split ( ',' ) [ 1 ] * 1 ]
2016-09-03 17:35:49 +02:00
: [ v [ i ] * 1 , cx , cy ] ;
2016-08-22 00:45:23 +02:00
tf [ i ] = r ;
} else if ( i === 'translate' ) {
2016-09-03 17:35:49 +02:00
t = v [ i ] instanceof Array ? v [ i ] : /\,|\s/ . test ( v [ i ] ) ? v [ i ] . split ( /\,|\s/ ) : [ v [ i ] * 1 , 0 ] ;
tf [ i ] = [ t [ 0 ] * 1 || 0 , t [ 1 ] * 1 || 0 ] ;
2016-08-22 00:45:23 +02:00
} else if ( i === 'scale' ) {
tf [ i ] = v [ i ] * 1 || 1 ;
2016-09-03 17:35:49 +02:00
} else if ( /skew/ . test ( i ) ) {
2016-08-22 00:45:23 +02:00
tf [ i ] = v [ i ] * 1 || 0 ;
}
}
2016-09-03 17:35:49 +02:00
// try to adjust translation when scale is used, probably we should do the same when using skews, but I think it's a waste of time
2016-08-22 00:45:23 +02:00
// http://www.petercollingridge.co.uk/interactive-svg-components/pan-and-zoom-control
if ( 'scale' in tf ) {
2016-09-03 17:35:49 +02:00
! ( 'translate' in tf ) && ( tf [ 'translate' ] = [ 0 , 0 ] ) ; // if no translate is found in current value or next value, we default to 0
tf [ 'translate' ] [ 0 ] += ( 1 - tf [ 'scale' ] ) * bb . width / 2 ;
2016-08-22 00:45:23 +02:00
tf [ 'translate' ] [ 1 ] += ( 1 - tf [ 'scale' ] ) * bb . height / 2 ;
2016-09-03 17:35:49 +02:00
// adjust rotation transform origin and translation when skews are used, to make the animation look exactly the same as if we were't using svgTransform
// http://stackoverflow.com/questions/39191054/how-to-compensate-translate-when-skewx-and-skewy-are-used-on-svg/39192565#39192565
if ( 'rotate' in tf ) {
tf [ 'rotate' ] [ 1 ] -= 'skewX' in tf ? Math . tan ( tf [ 'skewX' ] ) * bb . height : 0 ;
tf [ 'rotate' ] [ 2 ] -= 'skewY' in tf ? Math . tan ( tf [ 'skewY' ] ) * bb . width : 0 ;
}
tf [ 'translate' ] [ 0 ] += 'skewX' in tf ? Math . tan ( tf [ 'skewX' ] ) * bb . height * 2 : 0 ;
tf [ 'translate' ] [ 1 ] += 'skewY' in tf ? Math . tan ( tf [ 'skewY' ] ) * bb . width * 2 : 0 ;
} // more variations here https://gist.github.com/thednp/0b93068e20adb84658b5840ead0a07f8
2016-08-22 00:45:23 +02:00
return tf ;
}
// KUTE.prepareStart K.prS[p](el,p,to[p])
// returns an obect with current transform attribute value
K . prS [ 'svgTransform' ] = function ( l , p , t ) {
2016-09-03 17:35:49 +02:00
var tr = { } , i , ctr = S . pT ( l . getAttribute ( 'transform' ) ) ;
for ( i in t ) { tr [ i ] = i in ctr ? ctr [ i ] : ( i === 'scale' ? 1 : 0 ) ; } // find a value in current attribute value or add a default value
return tr ;
}
S . sT = function ( w ) { // stackTransform - helper function that helps preserve current transform properties into the objects
var bb = w . _el . getBBox ( ) , ctr = S . pT ( w . _el . getAttribute ( 'transform' ) ) , r , t , i ,
cx = bb . x + bb . width / 2 , cy = bb . y + bb . height / 2 ;
for ( i in ctr ) { // populate the valuesStart
if ( i === 'translate' ) {
t = ctr [ i ] instanceof Array ? ctr [ i ] : /\,|\s/ . test ( ctr [ i ] ) ? ctr [ i ] . split ( /\,|\s/ ) : [ ctr [ i ] * 1 , 0 ] ;
w . _vS . svgTransform [ i ] = [ t [ 0 ] * 1 || 0 , t [ 1 ] * 1 || 0 ] ;
} else if ( i === 'scale' ) {
w . _vS . svgTransform [ i ] = ctr [ i ] * 1 || 1 ;
} else if ( i === 'rotate' ) {
r = ctr [ i ] instanceof Array ? ctr [ i ]
: /\s/ . test ( ctr [ i ] ) ? [ ctr [ i ] . split ( ' ' ) [ 0 ] * 1 , ctr [ i ] . split ( ' ' ) [ 1 ] . split ( ',' ) [ 0 ] * 1 , ctr [ i ] . split ( ' ' ) [ 1 ] . split ( ',' ) [ 1 ] * 1 ]
: [ ctr [ i ] * 1 , cx , cy ] ;
w . _vS . svgTransform [ i ] = r ;
} else if ( /skew/ . test ( i ) ) {
w . _vS . svgTransform [ i ] = ctr [ i ] * 1 || 0 ;
2016-08-22 00:45:23 +02:00
}
}
2016-09-03 17:35:49 +02:00
for ( var i in w . _vS . svgTransform ) {
if ( ! ( i in w . _vE . svgTransform ) ) { // copy existing and unused properties to the valuesEnd
w . _vE . svgTransform [ i ] = w . _vS . svgTransform [ i ] ;
}
if ( i === 'rotate' in w . _vS . svgTransform && 'rotate' in w . _vE . svgTransform ) { // make sure to use the right transform origin when rotation is used
w . _vE . svgTransform . rotate [ 1 ] = w . _vS . svgTransform . rotate [ 1 ] = cx ;
w . _vE . svgTransform . rotate [ 2 ] = w . _vS . svgTransform . rotate [ 2 ] = cy ;
}
}
}
S . pT = function ( a ) { // parseTransform - helper function that turns transform value from string to object
var d = a && /\)/ . test ( a ) ? a . split ( ')' ) : 'none' , j , c = { } , p ;
if ( d instanceof Array ) {
for ( j = 0 ; j < d . length ; j ++ ) {
p = d [ j ] . split ( '(' ) ; p [ 0 ] !== '' && ( c [ p [ 0 ] . replace ( /\s/ , '' ) ] = p [ 1 ] ) ;
}
}
return c ;
2016-08-22 00:45:23 +02:00
}
2016-09-03 17:35:49 +02:00
2016-03-16 14:44:23 +01:00
return S ;
} ) ) ;