/* KUTE.js - The Light Tweening Engine * package - SVG Plugin * desc - draw SVG strokes, morph SVG and SVG transforms * by dnp_theme * Licensed under MIT-License */ (function (root,factory) { if (typeof define === 'function' && define.amd) { define(['./kute.js'], factory); } else if(typeof module == 'object' && typeof require == 'function') { module.exports = factory(require('./kute.js')); } else if ( typeof root.KUTE !== 'undefined' ) { factory(root.KUTE); } else { throw new Error("SVG Plugin require KUTE.js."); } }(this, function(KUTE) { 'use strict'; var g = typeof global !== 'undefined' ? global : window, K = KUTE, // connect plugin to KUTE object and global DOM = K.dom, parseProperty = K.parseProperty, prepareStart = K.prepareStart, getCurrentStyle = K.getCurrentStyle, trueColor = K.truC, trueDimension = K.truD, crossCheck = K.crossCheck, number = g.Interpolate.number, unit = g.Interpolate.unit, color = g.Interpolate.color, // interpolate functions defaultOptions = K.defaultOptions, // default tween options since 1.6.1 // browser detection isIE = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})").exec(navigator.userAgent) !== null ? parseFloat( RegExp.$1 ) : false; if (isIE&&isIE<9) {return;} // return if SVG API is not supported // 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', // function(array1, array2, length, progress) for SVG morph coords = g.Interpolate.coords = function(a,b,l,v) { var points = []; for(var i=0;i> 0)/1000 ); } } return points; }; // 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 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]; }, getClosestPoint = function(p,t,s){ // utility for polygon paths, returns a close point from the original path (length,pointAtLength,smallest); // intervalLength var x, y, a = [], l = s.length, dx, nx, pr; for (var i=0; i when glyph var createdPath = document.createElementNS(ns,'path'), d = typeof p === 'object' ? p.getAttribute('d') : p; createdPath.setAttribute('d',d); return createdPath; }, forcePath = function(p){ // forcePath for glyph elements if (p.tagName === 'glyph') { // perhaps we can also change other SVG tags in the future var c = createPath(p); p.parentNode.appendChild(c); return c; } 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]); } return copy; } 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; }, computePathCross = function(s,e){ // pathCross var s1, e1, pointsArray, largerPathLength, smallerPath, largerPath, simulatedSmallerPath, nsm = [], sml, cl = [], len, tl, cs, index = this.options.morphIndex; if (!this._isPolygon) { s = createPath(s); e = createPath(e); pointsArray = getSegments(s,e,this.options.morphPrecision); 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; simulatedSmallerPath = createPath('M'+smallerPath.join('L')+'z'); len = simulatedSmallerPath.getTotalLength() / largerPathLength; for (var i=0; i 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>0)/100, start = (number(a.s,b.s,v)*100>>0)/100, end = (number(a.e,b.e,v)*100>>0)/100, offset = 0 - start, dashOne = end+offset; l.style.strokeDashoffset = offset +'px'; l.style.strokeDasharray = (((dashOne <1 ? 0 : dashOne)*100>>0)/100) + 'px, ' + pathLength + 'px'; } } return getDraw(this.element,o); } prepareStart.draw = function(){ return getDraw(this.element); } // SVG Transform var parseStringOrigin = function(origin,box){ return /[a-zA-Z]/.test(origin) && !/px/.test(origin) ? origin.replace(/top|left/,0).replace(/right|bottom/,100).replace(/center|middle/,50) : /%/.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 = {}; if (d instanceof Array) { for (var j=0, jl = d.length; j>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 + ')' : '' ) +( scale !== 1 ? 'scale(' + (scale*1000>>0)/1000 +')' : '' ) ); } } // now prepare transform return parseTransformObject.call(this,v); } // returns an obect with current transform attribute value prepareStart.svgTransform = function(p,t) { 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; } crossCheck.svgTransform = function() { // helper function that helps preserve current transform properties into the objects if (!this.options.rpr) return; // fix since 1.6.1 for fromTo() method var valuesStart = this.valuesStart.svgTransform, valuesEnd = this.valuesEnd.svgTransform, currentTransform = parseTransformObject.call(this, parseTransformString(this.element.getAttribute('transform')) ); for ( var i in currentTransform ) { valuesStart[i] = currentTransform[i]; } // populate the valuesStart first // now try to determine the REAL translation 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 ); valuesStart.translate = [newTransform.matrix.e,newTransform.matrix.f]; // finally the translate we're looking for // copy existing and unused properties to the valuesEnd for ( var i in valuesStart) { if ( !(i in valuesEnd)) { valuesEnd[i] = valuesStart[i]; } } } return this; }));