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
2016-11-25 21:54:27 +01:00
* desc - draw SVG strokes , morph SVG and SVG transforms
2016-03-16 14:44:23 +01:00
* by dnp _theme
* Licensed under MIT - License
* /
2016-10-03 18:26:17 +02:00
( function ( root , factory ) {
2016-03-16 14:44:23 +01:00
if ( typeof define === 'function' && define . amd ) {
2017-01-27 18:16:14 +01:00
define ( [ './kute.js' ] , factory ) ;
2016-10-07 22:10:34 +02:00
} else if ( typeof module == 'object' && typeof require == 'function' ) {
2017-01-27 18:16:14 +01:00
module . exports = factory ( require ( './kute.js' ) ) ;
2016-10-03 18:26:17 +02:00
} else if ( typeof root . KUTE !== 'undefined' ) {
2016-11-24 21:57:33 +01:00
factory ( root . KUTE ) ;
2016-03-16 14:44:23 +01:00
} else {
throw new Error ( "SVG Plugin require KUTE.js." ) ;
}
2016-10-07 21:59:35 +02:00
} ( this , function ( KUTE ) {
2017-01-27 18:16:14 +01:00
'use strict' ;
2016-10-09 04:45:21 +02:00
2016-11-25 21:54:27 +01:00
var g = typeof global !== 'undefined' ? global : window , K = KUTE , // connect plugin to KUTE object and global
2016-11-30 17:12:11 +01:00
DOM = K . dom , parseProperty = K . parseProperty , prepareStart = K . prepareStart , getCurrentStyle = K . getCurrentStyle ,
2017-01-27 18:16:14 +01:00
trueColor = K . truC , trueDimension = K . truD , crossCheck = K . crossCheck ,
2016-11-30 17:12:11 +01:00
number = g . Interpolate . number , unit = g . Interpolate . unit , color = g . Interpolate . color , // interpolate functions
2017-01-02 23:48:28 +01:00
defaultOptions = K . defaultOptions , // default tween options since 1.6.1
// browser detection
2017-09-28 23:56:16 +02:00
isIE = new RegExp ( "MSIE ([0-9]{1,}[\.0-9]{0,})" ) . exec ( navigator . userAgent ) !== null ? parseFloat ( RegExp . $1 ) : false ;
2017-01-27 18:16:14 +01:00
2016-12-11 02:48:37 +01:00
if ( isIE && isIE < 9 ) { return ; } // return if SVG API is not supported
2017-01-27 18:16:14 +01:00
2016-11-25 21:54:27 +01:00
// here we go with the plugin
var pathReg = /(m[^(h|v|l)]*|[vhl][^(v|h|l|z)]*)/gmi , ns = 'http://www.w3.org/2000/svg' ,
2017-02-02 00:23:19 +01:00
// function(array1, array2, length, progress) for SVG morph
2017-09-28 23:56:16 +02:00
coords = g . Interpolate . coords = function ( a , b , l , v ) {
2017-02-02 00:23:19 +01:00
var points = [ ] ;
2016-11-23 19:42:01 +01:00
for ( var i = 0 ; i < l ; i ++ ) { // for each point
points [ i ] = [ ] ;
for ( var j = 0 ; j < 2 ; j ++ ) { // each point coordinate
2017-09-28 23:56:16 +02:00
points [ i ] . push ( ( ( a [ i ] [ j ] + ( b [ i ] [ j ] - a [ i ] [ j ] ) * v ) * 1000 >> 0 ) / 1000 ) ;
2016-11-23 19:42:01 +01:00
}
}
return points ;
2016-09-03 17:35:49 +02:00
} ;
2016-03-16 14:44:23 +01:00
2017-01-02 23:48:28 +01:00
2016-09-22 04:26:43 +02:00
// SVG MORPH
var getSegments = function ( s , e , r ) { // getSegments returns an array of points based on a sample size morphPrecision
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-16 14:44:23 +01:00
2016-09-22 04:26:43 +02:00
while ( ( j += r ) < sl ) { // populate the points arrays based on morphPrecision as sample size
s1 . push ( [ s . getPointAtLength ( j ) . x , s . getPointAtLength ( j ) . y ] ) ;
e1 . push ( [ e . getPointAtLength ( j ) . x , e . getPointAtLength ( j ) . y ] ) ;
}
return [ s1 , e1 ] ;
} ,
2017-01-02 23:48:28 +01:00
getClosestPoint = function ( p , t , s ) { // utility for polygon paths, returns a close point from the original path (length,pointAtLength,smallest); // intervalLength
2016-09-22 04:26:43 +02:00
var x , y , a = [ ] , l = s . length , dx , nx , pr ;
2017-01-02 23:48:28 +01:00
for ( var i = 0 ; i < l ; i ++ ) {
2016-09-22 04:26:43 +02:00
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 ]
2017-01-27 18:16:14 +01:00
: 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 ]
2016-09-22 04:26:43 +02:00
: [ t . x , t . y ] ;
} ,
2017-01-02 23:48:28 +01:00
pathToAbsolute = function ( p ) { // simple utility for polygons | this is still BETA / a work in progress
2016-09-22 04:26:43 +02:00
var np = p . match ( pathReg ) , wp = [ ] , l = np . length , s , c , r , x = 0 , y = 0 ;
for ( var i = 0 ; i < l ; i ++ ) {
2017-01-27 18:16:14 +01:00
np [ i ] = np [ i ] ; c = np [ i ] [ 0 ] ; r = new RegExp ( c + '[^\\d|\\-]*' , 'i' ) ;
2016-09-22 04:26:43 +02:00
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 ] ) ;
if ( i === 0 ) { x += np [ i ] [ 0 ] ; y += np [ i ] [ 1 ] ; }
else {
2017-01-27 18:16:14 +01:00
x = np [ i - 1 ] [ 0 ] ;
y = np [ i - 1 ] [ 1 ] ;
2016-09-22 04:26:43 +02:00
if ( /l/i . test ( c ) ) {
np [ i ] [ 0 ] = c === 'l' ? np [ i ] [ 0 ] + x : np [ i ] [ 0 ] ;
2017-01-27 18:16:14 +01:00
np [ i ] [ 1 ] = c === 'l' ? np [ i ] [ 1 ] + y : np [ i ] [ 1 ] ;
2016-09-22 04:26:43 +02:00
} else if ( /h/i . test ( c ) ) {
np [ i ] [ 0 ] = c === 'h' ? np [ i ] [ 0 ] + x : np [ i ] [ 0 ] ;
2017-01-27 18:16:14 +01:00
np [ i ] [ 1 ] = y ;
2016-09-22 04:26:43 +02:00
} else if ( /v/i . test ( c ) ) {
np [ i ] [ 0 ] = x ;
np [ i ] [ 1 ] = c === 'v' ? np [ i ] [ 0 ] + y : np [ i ] [ 0 ] ;
2016-03-24 13:27:27 +01:00
}
}
2016-03-16 14:44:23 +01:00
}
2016-09-22 04:26:43 +02:00
return np ;
} ,
2017-01-02 23:48:28 +01:00
getOnePath = function ( p ) { return p . split ( /z/i ) . shift ( ) + 'z' ; } , // we only tween first path only
createPath = function ( p ) { // create a <path> when glyph
2017-01-27 18:16:14 +01:00
var createdPath = document . createElementNS ( ns , 'path' ) , d = typeof p === 'object' ? p . getAttribute ( 'd' ) : p ;
2017-01-02 23:48:28 +01:00
createdPath . setAttribute ( 'd' , d ) ; return createdPath ;
2016-09-22 04:26:43 +02:00
} ,
forcePath = function ( p ) { // forcePath for glyph elements
2017-01-27 18:16:14 +01:00
if ( p . tagName === 'glyph' ) { // perhaps we can also change other SVG tags in the future
2016-09-22 04:26:43 +02:00
var c = createPath ( p ) ; p . parentNode . appendChild ( c ) ; return c ;
2017-01-27 18:16:14 +01:00
}
2016-09-22 04:26:43 +02:00
return p ;
} ,
clone = function ( a ) {
var copy ;
if ( a instanceof Array ) {
copy = [ ] ;
for ( var i = 0 , len = a . length ; i < len ; i ++ ) {
copy [ i ] = clone ( a [ i ] ) ;
2016-03-16 14:44:23 +01:00
}
2016-09-22 04:26:43 +02:00
return copy ;
2016-03-16 14:44:23 +01:00
}
2016-09-22 04:26:43 +02:00
return a ;
} ,
getPath = function ( e ) { // get path d attribute or create a path from string value
var p = { } , el = typeof e === 'object' ? e : /^\.|^\#/ . test ( e ) ? document . querySelector ( e ) : null ;
if ( el && /path|glyph/ . test ( el . tagName ) ) {
p . e = forcePath ( el ) ;
p . o = el . getAttribute ( 'd' ) ;
} else if ( ! el && /[a-z][^a-z]*/ig . test ( e ) ) { // maybe it's a string path already
p . e = createPath ( e . trim ( ) ) ;
p . o = e ;
}
return p ;
} ,
2016-10-09 05:10:41 +02:00
computePathCross = function ( s , e ) { // pathCross
2016-12-11 02:48:37 +01:00
var s1 , e1 , pointsArray , largerPathLength , smallerPath , largerPath , simulatedSmallerPath , nsm = [ ] , sml , cl = [ ] , len , tl , cs ,
index = this . options . morphIndex ;
2016-10-09 05:10:41 +02:00
2016-11-24 21:57:33 +01:00
if ( ! this . _isPolygon ) {
2017-01-27 18:16:14 +01:00
s = createPath ( s ) ; e = createPath ( e ) ;
pointsArray = getSegments ( s , e , this . options . morphPrecision ) ;
2016-10-09 05:10:41 +02:00
s1 = pointsArray [ 0 ] ; e1 = pointsArray [ 1 ] ; largerPathLength = e1 . length ;
} else {
s = pathToAbsolute ( s ) ; e = pathToAbsolute ( e ) ;
if ( s . length !== e . length ) {
largerPathLength = Math . max ( s . length , e . length ) ;
if ( largerPathLength === e . length ) { smallerPath = s ; largerPath = e ; } else { smallerPath = e ; largerPath = s ; }
sml = smallerPath . length ;
2016-11-25 21:54:27 +01:00
simulatedSmallerPath = createPath ( 'M' + smallerPath . join ( 'L' ) + 'z' ) ; len = simulatedSmallerPath . getTotalLength ( ) / largerPathLength ;
2016-10-09 05:10:41 +02:00
for ( var i = 0 ; i < largerPathLength ; i ++ ) {
2016-11-25 21:54:27 +01:00
tl = simulatedSmallerPath . getPointAtLength ( len * i ) ;
2016-10-09 05:10:41 +02:00
cs = getClosestPoint ( len , tl , smallerPath ) ;
nsm . push ( [ cs [ 0 ] , cs [ 1 ] ] ) ;
}
if ( largerPathLength === e . length ) { e1 = largerPath ; s1 = nsm ; } else { s1 = largerPath ; e1 = nsm ; }
} else {
s1 = s ; e1 = e ;
}
}
// reverse arrays
2016-11-24 21:57:33 +01:00
if ( this . options . reverseFirstPath ) { s1 . reverse ( ) ; }
if ( this . options . reverseSecondPath ) { e1 . reverse ( ) ; }
2017-01-27 18:16:14 +01:00
2016-10-09 05:10:41 +02:00
// shift second array to for smallest tween distance
2016-12-11 02:48:37 +01:00
if ( index ) {
var e11 = e1 . splice ( index , largerPathLength - index ) ;
2016-10-09 05:10:41 +02:00
e1 = e11 . concat ( e1 ) ;
}
s = e = null ;
return [ s1 , e1 ]
2016-09-22 04:26:43 +02:00
} ;
2017-01-27 18:16:14 +01:00
2017-01-02 23:48:28 +01:00
// set default morphPrecision since 1.6.1
defaultOptions . morphPrecision = 15 ;
2016-03-16 14:44:23 +01:00
// process path object and also register the render function
2016-11-30 17:12:11 +01:00
parseProperty . path = function ( o , v ) {
2016-09-03 17:35:49 +02:00
if ( ! ( 'path' in DOM ) ) {
2016-11-30 17:12:11 +01:00
DOM . path = function ( l , p , a , b , v ) {
2017-01-27 18:16:14 +01:00
l . setAttribute ( "d" , v === 1 ? b . o : 'M' + coords ( a [ 'd' ] , b [ 'd' ] , b [ 'd' ] . length , v ) + 'Z' ) ;
2016-03-16 14:44:23 +01:00
}
}
2016-11-30 17:12:11 +01:00
return getPath ( v ) ;
2016-03-16 14:44:23 +01:00
} ;
2017-01-27 18:16:14 +01:00
2016-11-30 17:12:11 +01:00
prepareStart . path = function ( p ) {
return this . element . getAttribute ( 'd' ) ;
2016-03-16 14:44:23 +01:00
} ;
2017-01-02 23:48:28 +01:00
crossCheck . path = function ( ) { // unlike other cases, the crossCheck apply to both to() and fromTo() methods
2016-11-30 17:12:11 +01:00
var p1 = getOnePath ( this . valuesStart . path . o ) , p2 = getOnePath ( this . valuesEnd . path . o ) , paths ;
2016-11-24 21:57:33 +01:00
// path tween options
2017-01-02 23:48:28 +01:00
this . options . morphPrecision = this . options && 'morphPrecision' in this . options ? parseInt ( this . options . morphPrecision ) : defaultOptions . morphPrecision ;
2016-11-24 21:57:33 +01:00
this . _isPolygon = ! /[CSQTA]/i . test ( p1 ) && ! /[CSQTA]/i . test ( p2 ) ; // check if both shapes are polygons
// begin processing paths
paths = computePathCross . apply ( this , [ p1 , p2 ] ) ;
2016-11-30 17:12:11 +01:00
this . valuesStart . path . d = paths [ 0 ] ;
this . valuesEnd . path . d = paths [ 1 ] ;
2016-11-24 21:57:33 +01:00
} ;
2016-09-22 04:26:43 +02:00
2016-03-16 14:44:23 +01:00
// SVG DRAW
2016-12-11 02:48:37 +01:00
var percent = function ( v , l ) { return parseFloat ( v ) / 100 * l ; } ,
2016-09-22 04:26:43 +02:00
// SVG DRAW UTILITITES
// http://stackoverflow.com/a/30376660
2016-11-30 17:12:11 +01:00
getRectLength = function ( el ) { // returns the length of a Rect
2016-09-22 04:26:43 +02:00
var w = el . getAttribute ( 'width' ) ;
var h = el . getAttribute ( 'height' ) ;
return ( w * 2 ) + ( h * 2 ) ;
} ,
getPolyLength = function ( el ) { // getPolygonLength / getPolylineLength - return the length of the Polygon / Polyline
var points = el . getAttribute ( 'points' ) . split ( ' ' ) , len = 0 ;
if ( points . length > 1 ) {
var coord = function ( p ) {
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 ] ) ] ;
} ;
var dist = function ( c1 , c2 ) {
if ( c1 != undefined && c2 != undefined ) {
return Math . sqrt ( Math . pow ( ( c2 [ 0 ] - c1 [ 0 ] ) , 2 ) + Math . pow ( ( c2 [ 1 ] - c1 [ 1 ] ) , 2 ) ) ;
}
return 0 ;
} ;
if ( points . length > 2 ) {
for ( var i = 0 ; i < points . length - 1 ; i ++ ) {
len += dist ( coord ( points [ i ] ) , coord ( points [ i + 1 ] ) ) ;
}
}
2018-10-19 07:47:48 +02:00
len += el . tagName === 'polygon' ? dist ( coord ( points [ 0 ] ) , coord ( points [ points . length - 1 ] ) ) : 0 ;
2016-09-22 04:26:43 +02:00
}
return len ;
} ,
2016-11-30 17:12:11 +01:00
getLineLength = function ( el ) { // return the length of the line
2016-09-22 04:26:43 +02:00
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 ) ) ;
} ,
2016-11-30 17:12:11 +01:00
getCircleLength = function ( el ) { // return the length of the circle
2016-09-22 04:26:43 +02:00
var r = el . getAttribute ( 'r' ) ;
2017-01-27 18:16:14 +01:00
return 2 * Math . PI * r ;
2016-09-22 04:26:43 +02:00
} ,
2016-11-30 17:12:11 +01:00
getEllipseLength = function ( el ) { // returns the length of an ellipse
2016-09-22 04:26:43 +02:00
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-11-30 17:12:11 +01:00
getTotalLength = function ( el ) { // returns the result of any of the below functions
2016-09-22 04:26:43 +02:00
if ( /rect/ . test ( el . tagName ) ) {
return getRectLength ( el ) ;
} else if ( /circle/ . test ( el . tagName ) ) {
return getCircleLength ( el ) ;
} else if ( /ellipse/ . test ( el . tagName ) ) {
return getEllipseLength ( el ) ;
} else if ( /polygon|polyline/ . test ( el . tagName ) ) {
return getPolyLength ( el ) ;
} else if ( /line/ . test ( el . tagName ) ) {
return getLineLength ( el ) ;
}
} ,
getDraw = function ( e , v ) {
var l = /path|glyph/ . test ( e . tagName ) ? e . getTotalLength ( ) : getTotalLength ( e ) , start , end , d , o ;
if ( v instanceof Object ) {
return v ;
2017-01-27 18:16:14 +01:00
} else if ( typeof v === 'string' ) {
2016-09-22 04:26:43 +02:00
v = v . split ( /\,|\s/ ) ;
start = /%/ . test ( v [ 0 ] ) ? percent ( v [ 0 ] . trim ( ) , l ) : parseFloat ( v [ 0 ] ) ;
end = /%/ . test ( v [ 1 ] ) ? percent ( v [ 1 ] . trim ( ) , l ) : parseFloat ( v [ 1 ] ) ;
} else if ( typeof v === 'undefined' ) {
2016-10-12 05:44:42 +02:00
o = parseFloat ( getCurrentStyle ( e , 'stroke-dashoffset' ) ) ;
d = getCurrentStyle ( e , 'stroke-dasharray' ) . split ( /\,/ ) ;
2017-01-27 18:16:14 +01:00
2016-09-22 04:26:43 +02:00
start = 0 - o ;
end = parseFloat ( d [ 0 ] ) + start || l ;
}
2017-01-27 18:16:14 +01:00
return { s : start , e : end , l : l }
2016-09-22 04:26:43 +02:00
} ;
2017-01-27 18:16:14 +01:00
2016-11-30 17:12:11 +01:00
parseProperty . draw = function ( a , o ) { // register the draw property
2016-09-03 17:35:49 +02:00
if ( ! ( 'draw' in DOM ) ) {
2016-11-30 17:12:11 +01:00
DOM . draw = function ( l , p , a , b , v ) {
2017-01-27 18:16:14 +01:00
var pathLength = ( a . l * 100 >> 0 ) / 100 , start = ( number ( a . s , b . s , v ) * 100 >> 0 ) / 100 , end = ( number ( a . e , b . e , v ) * 100 >> 0 ) / 100 ,
2016-12-14 13:41:50 +01:00
offset = 0 - start , dashOne = end + offset ;
l . style . strokeDashoffset = offset + 'px' ;
l . style . strokeDasharray = ( ( ( dashOne < 1 ? 0 : dashOne ) * 100 >> 0 ) / 100 ) + 'px, ' + pathLength + 'px' ;
2016-03-16 14:44:23 +01:00
}
}
2016-11-30 17:12:11 +01:00
return getDraw ( this . element , o ) ;
2016-03-16 14:44:23 +01:00
}
2017-01-27 18:16:14 +01:00
2016-11-30 17:12:11 +01:00
prepareStart . draw = function ( ) {
return getDraw ( this . element ) ;
2016-08-20 14:58:30 +02:00
}
2016-08-22 00:45:23 +02:00
// SVG Transform
2016-12-11 02:48:37 +01:00
var parseStringOrigin = function ( origin , box ) {
2017-01-27 18:16:14 +01:00
return /[a-zA-Z]/ . test ( origin ) && ! /px/ . test ( origin ) ? origin . replace ( /top|left/ , 0 ) . replace ( /right|bottom/ , 100 ) . replace ( /center|middle/ , 50 )
2016-12-11 02:48:37 +01:00
: /%/ . test ( origin ) ? ( box . x + parseFloat ( origin ) * box . width / 100 ) : parseFloat ( origin ) ;
} ,
parseTransformString = function ( a ) { // helper function that turns transform value from string to object
var d = a && /\)/ . test ( a ) ? a . substring ( 0 , a . length - 1 ) . split ( /\)\s|\)/ ) : 'none' , c = { } ;
2016-09-22 04:26:43 +02:00
if ( d instanceof Array ) {
2016-12-11 02:48:37 +01:00
for ( var j = 0 , jl = d . length ; j < jl ; j ++ ) {
var p = d [ j ] . trim ( ) . split ( '(' ) ; c [ p [ 0 ] ] = p [ 1 ] ;
2016-09-22 04:26:43 +02:00
}
}
return c ;
} ,
2016-12-11 02:48:37 +01:00
parseTransformObject = function ( v ) {
var svgTransformObject = { } , bb = this . element . getBBox ( ) ,
cx = bb . x + bb . width / 2 , cy = bb . y + bb . height / 2 , // by default the transformOrigin is "50% 50%" of the shape box
origin = this . options . transformOrigin , translation ;
2016-12-16 21:23:23 +01:00
2016-12-11 02:48:37 +01:00
origin = ! ! origin ? ( origin instanceof Array ? origin : origin . split ( /\s/ ) ) : [ cx , cy ] ;
origin [ 0 ] = typeof origin [ 0 ] === 'number' ? origin [ 0 ] : parseStringOrigin ( origin [ 0 ] , bb ) ;
origin [ 1 ] = typeof origin [ 1 ] === 'number' ? origin [ 1 ] : parseStringOrigin ( origin [ 1 ] , bb ) ;
svgTransformObject . origin = origin ;
for ( var i in v ) { // populate the valuesStart and / or valuesEnd
if ( i === 'rotate' ) {
svgTransformObject [ i ] = typeof v [ i ] === 'number' ? v [ i ] : v [ i ] instanceof Array ? v [ i ] [ 0 ] : v [ i ] . split ( /\s/ ) [ 0 ] * 1 ;
} else if ( i === 'translate' ) {
translation = v [ i ] instanceof Array ? v [ i ] : /\,|\s/ . test ( v [ i ] ) ? v [ i ] . split ( ',' ) : [ v [ i ] , 0 ] ;
svgTransformObject [ i ] = [ translation [ 0 ] * 1 || 0 , translation [ 1 ] * 1 || 0 ] ;
} else if ( /skew/ . test ( i ) ) {
svgTransformObject [ i ] = v [ i ] * 1 || 0 ;
} else if ( i === 'scale' ) {
svgTransformObject [ i ] = parseFloat ( v [ i ] ) || 1 ;
}
}
return svgTransformObject ;
2016-09-22 04:26:43 +02:00
} ;
2016-11-30 17:12:11 +01:00
parseProperty . svgTransform = function ( p , v ) {
2016-08-22 00:45:23 +02:00
// register the render function
2016-09-03 17:35:49 +02:00
if ( ! ( 'svgTransform' in DOM ) ) {
2016-11-30 17:12:11 +01:00
DOM . svgTransform = function ( l , p , a , b , v ) {
2017-01-27 18:16:14 +01:00
var x = 0 , y = 0 , tmp , deg = Math . PI / 180 ,
2016-12-11 02:48:37 +01:00
scale = 'scale' in b ? number ( a . scale , b . scale , v ) : 1 ,
rotate = 'rotate' in b ? number ( a . rotate , b . rotate , v ) : 0 ,
sin = Math . sin ( rotate * deg ) , cos = Math . cos ( rotate * deg ) ,
skewX = 'skewX' in b ? number ( a . skewX , b . skewX , v ) : 0 ,
skewY = 'skewY' in b ? number ( a . skewY , b . skewY , v ) : 0 ,
complex = rotate || skewX || skewY || scale !== 1 || 0 ;
2017-01-27 18:16:14 +01:00
2016-12-11 02:48:37 +01:00
// start normalizing the translation, we start from last to first (from last chained translation)
// the normalized translation will handle the transformOrigin tween option and makes sure to have a consistent transformation
x -= complex ? b . origin [ 0 ] : 0 ; y -= complex ? b . origin [ 1 ] : 0 ; // we start with removing transformOrigin from translation
x *= scale ; y *= scale ; // we now apply the scale
y += skewY ? x * Math . tan ( skewY * deg ) : 0 ; x += skewX ? y * Math . tan ( skewX * deg ) : 0 ; // now we apply skews
tmp = cos * x - sin * y ; // apply rotation as well
y = rotate ? sin * x + cos * y : y ; x = rotate ? tmp : x ;
x += 'translate' in b ? number ( a . translate [ 0 ] , b . translate [ 0 ] , v ) : 0 ; // now we apply the actual translation
y += 'translate' in b ? number ( a . translate [ 1 ] , b . translate [ 1 ] , v ) : 0 ;
x += complex ? b . origin [ 0 ] : 0 ; y += complex ? b . origin [ 1 ] : 0 ; // normalizing ends with the addition of the transformOrigin to the translation
2017-01-27 18:16:14 +01:00
// finally we apply the transform attribute value
2017-09-28 23:56:16 +02:00
l . setAttribute ( 'transform' , ( x || y ? ( 'translate(' + ( x * 1000 >> 0 ) / 1000 + ( y ? ( ',' + ( ( y * 1000 >> 0 ) / 1000 ) ) : '' ) + ')' ) : '' )
+ ( rotate ? 'rotate(' + ( rotate * 1000 >> 0 ) / 1000 + ')' : '' )
+ ( skewX ? 'skewX(' + ( skewX * 1000 >> 0 ) / 1000 + ')' : '' )
+ ( skewY ? 'skewY(' + ( skewY * 1000 >> 0 ) / 1000 + ')' : '' )
2016-12-16 21:23:23 +01:00
+ ( scale !== 1 ? 'scale(' + ( scale * 1000 >> 0 ) / 1000 + ')' : '' ) ) ;
2016-08-22 00:45:23 +02:00
}
}
2016-09-03 17:35:49 +02:00
// now prepare transform
2016-12-11 02:48:37 +01:00
return parseTransformObject . call ( this , v ) ;
2016-08-22 00:45:23 +02:00
}
// returns an obect with current transform attribute value
2016-11-30 17:12:11 +01:00
prepareStart . svgTransform = function ( p , t ) {
2016-12-11 02:48:37 +01:00
var transformObject = { } , currentTransform = parseTransformString ( this . element . getAttribute ( 'transform' ) ) ;
for ( var i in t ) { transformObject [ i ] = i in currentTransform ? currentTransform [ i ] : ( i === 'scale' ? 1 : 0 ) ; } // find a value in current attribute value or add a default value
return transformObject ;
2016-09-03 17:35:49 +02:00
}
2016-11-30 17:12:11 +01:00
crossCheck . svgTransform = function ( ) { // helper function that helps preserve current transform properties into the objects
2017-01-02 23:48:28 +01:00
if ( ! this . options . rpr ) return ; // fix since 1.6.1 for fromTo() method
2016-12-11 02:48:37 +01:00
var valuesStart = this . valuesStart . svgTransform , valuesEnd = this . valuesEnd . svgTransform ,
currentTransform = parseTransformObject . call ( this , parseTransformString ( this . element . getAttribute ( 'transform' ) ) ) ;
2016-12-16 21:23:23 +01:00
for ( var i in currentTransform ) { valuesStart [ i ] = currentTransform [ i ] ; } // populate the valuesStart first
2016-11-24 21:57:33 +01:00
2016-12-11 02:48:37 +01:00
// now try to determine the REAL translation
2016-12-14 15:37:50 +01:00
var parentSVG = this . element . ownerSVGElement ,
newTransform = parentSVG . createSVGTransformFromMatrix (
parentSVG . createSVGMatrix ( )
. translate ( - valuesStart . origin [ 0 ] , - valuesStart . origin [ 1 ] ) // - origin
. translate ( 'translate' in valuesStart ? valuesStart . translate [ 0 ] : 0 , 'translate' in valuesStart ? valuesStart . translate [ 1 ] : 0 ) // the current translate
. rotate ( valuesStart . rotate || 0 ) . skewX ( valuesStart . skewX || 0 ) . skewY ( valuesStart . skewY || 0 ) . scale ( valuesStart . scale || 1 ) // the other functions
. translate ( + valuesStart . origin [ 0 ] , + valuesStart . origin [ 1 ] ) // + origin
) ;
2016-12-11 02:48:37 +01:00
valuesStart . translate = [ newTransform . matrix . e , newTransform . matrix . f ] ; // finally the translate we're looking for
// copy existing and unused properties to the valuesEnd
2016-12-16 21:23:23 +01:00
for ( var i in valuesStart ) { if ( ! ( i in valuesEnd ) ) { valuesEnd [ i ] = valuesStart [ i ] ; } }
2016-11-24 21:57:33 +01:00
}
return this ;
2016-10-09 04:45:21 +02:00
} ) ) ;