diff --git a/demo/assets/css/kute.css b/demo/assets/css/kute.css index a9ced15..3bc23ca 100644 --- a/demo/assets/css/kute.css +++ b/demo/assets/css/kute.css @@ -96,17 +96,18 @@ footer p {margin: 0 0 10px} /* example box */ .example-box { font-size: 40px; line-height: 150px; text-align:center; font-weight: bold; - width: 150px; height: 150px; float: left; position:relative; + float: left; position:relative; + width: 150px; height: 150px; border-radius: 5px; margin: 0 20px 20px 0; -/* easy hack to improve box model properties performance on modern browsers only ofc */ - transform: translate3d(0px,0px,0px); -webkit-transform: translate3d(0px,0px,0px); } -.example-box-model { - font-size: 40px; text-align:center; font-weight: bold; +/*.example-box-model { + font-size: 40px; text-align:center; font-weight: bold; float: left; position:relative; border-radius: 5px; margin: 0 20px 20px 0; } +svg.example-box { width: auto; height: auto; }*/ + .easing-example {float: none; font-size: 24px; width: 320px} .example-buttons {position: absolute; top: 18px; right:0} @@ -114,7 +115,7 @@ footer p {margin: 0 0 10px} h1.example-item { font-size: 50px; line-height:50px; - color: #fff; + color: #333; } h1.example-item span { diff --git a/demo/assets/js/css.js b/demo/assets/js/css.js index 7249401..97f1455 100644 --- a/demo/assets/js/css.js +++ b/demo/assets/js/css.js @@ -25,7 +25,7 @@ var boxModel = document.getElementById('boxModel'), // built the tween objects var bm1 = KUTE.to(box,{marginTop:50},{ yoyo: true, repeat: 1, duration: 1500, update: onMarginTop}); var bm2 = KUTE.to(box,{marginBottom:50},{ yoyo: true, repeat: 1, duration: 1500, update: onMarginBottom}); -var bm3 = KUTE.to(box,{padding:30},{ yoyo: true, repeat: 1, duration: 1500, update: onPadding}); +var bm3 = KUTE.to(box,{paddingTop:15},{ yoyo: true, repeat: 1, duration: 1500, update: onPadding}); var bm4 = KUTE.to(box,{marginTop:50,marginLeft:50,marginBottom:70},{ yoyo: true, repeat: 1, duration: 1500, update: onMargin, complete: onComplete}); // chain the bms @@ -37,7 +37,7 @@ bm3.chain(bm4); //callback functions function onMarginTop() { var css = KUTE.gCS(box,'marginTop'); box.innerHTML = parseInt(css)+'px'+'
MARGIN'; } function onMarginBottom() { var css = KUTE.gCS(box,'marginBottom'); box.innerHTML = 'MARGIN
'+parseInt(css)+'px'; } -function onPadding() { var css = KUTE.gCS(box,'paddingTop'); box.innerHTML = 'PADDING
'+parseInt(css)+'px'; } +function onPadding() { var css = KUTE.gCS(box,'paddingTop'); box.innerHTML = parseInt(css)+'px
PADDING'; } function onMargin() { var css = KUTE.gCS(box,'marginTop'); box.innerHTML = 'MARGIN
'+parseInt(css)+'px'; } function onComplete() { box.innerHTML = 'BOX
 MODEL '; } @@ -115,16 +115,18 @@ var colTween2 = KUTE.to(colBoxElement, {borderTopColor: '#9C27B0'}, {duration: 1 var colTween3 = KUTE.to(colBoxElement, {borderRightColor: '#9C27B0'}, {duration: 1000}); var colTween4 = KUTE.to(colBoxElement, {borderBottomColor: '#9C27B0'}, {duration: 1000}); var colTween5 = KUTE.to(colBoxElement, {borderLeftColor: '#9C27B0'}, {duration: 1000}); +var colTween6 = KUTE.to(colBoxElement, {outlineColor: '#9C27B0'}, {duration: 1000, repeat: 1, yoyo: true}); colTween1.chain(colTween2); colTween2.chain(colTween3); colTween3.chain(colTween4); colTween4.chain(colTween5); +colTween5.chain(colTween6); colbtn.addEventListener('click', function(e){ e.preventDefault(); - !colTween1.playing && !colTween2.playing && !colTween3.playing && !colTween4.playing && !colTween5.playing && colTween1.start(); + !colTween1.playing && !colTween2.playing && !colTween3.playing && !colTween4.playing && !colTween5.playing && !colTween6.playing && colTween1.start(); },false); /* COLORS EXAMPLE */ diff --git a/demo/assets/js/easing.js b/demo/assets/js/easing.js index 370c12c..25e4c8b 100644 --- a/demo/assets/js/easing.js +++ b/demo/assets/js/easing.js @@ -39,7 +39,7 @@ for (var j=0; jsuper simple write text demo."}, {duration: 3000, easing: 'easingBounceOut'}); + headTween = KUTE.to(headText, {text: "This is a super simple write text demo."}, {repeat: 1, yoyo: true, duration: 5000, easing: 'easingBounceOut'}); headBtn.addEventListener('click', function(){ !headTween.playing && headTween.start(); }, false); diff --git a/demo/css.html b/demo/css.html index 915032c..7460857 100644 --- a/demo/css.html +++ b/demo/css.html @@ -118,7 +118,7 @@ var tween4 = KUTE.to('selector1',{margin:'5%'});

We're gonna chain these tweens and start the animation. You can download this example here.

-
BOX
 MODEL 
+
BOX
 MODEL 
Start @@ -159,7 +159,7 @@ KUTE.to('selector1',{outlineColor:'#069'}).start();

Let's get some animation going. Download the example here.

-
Colors
+
Colors
Start diff --git a/demo/extend.html b/demo/extend.html index 6d5b914..5569e23 100644 --- a/demo/extend.html +++ b/demo/extend.html @@ -213,23 +213,29 @@ K.pp['boxShadow'] = function(property,value,element){ // the DOM update function for boxShadow registers here // we only enqueue it if the boxShadow property is used to tween if ( !('boxShadow' in K.dom) ) { - K.dom['boxShadow'] = function(w,p,v) { + K.dom['boxShadow'] = function(l,p,a,b,v) { // element, propertyName, valuesStart.boxShadow, valuesEnd.boxShadow, progress + // let's start with the numbers | set unit | also determine inset - var numbers = [], unit = 'px', // the unit is always px - inset = w._vS[p][5] !== 'none' || w._vE[p][5] !== 'none' ? ' inset' : false; - for (var i=0; i<4; i++){ - numbers.push( (w._vS[p][i] + (w._vE[p][i] - w._vS[p][i]) * v ) + unit); + var numbers = [], px = 'px', // the unit is always px + inset = a[5] !== 'none' || w._vE[p][5] !== 'none' ? ' inset' : false; + + for (var i=0; i<4; i++){ // for boxShadow coordinates we do the math for an array of numbers + numbers.push( (a[i] + (b[i] - a[i]) * v ) + px); + // we can also write this like this now, starting 1.5.3 + // numbers.push( K.Interpolate.unit(a[i], b[i], px, v) ); } // now we handle the color - var color, _color = {}; + var colorValue, _color = {}; for (var c in w._vE[p][4]) { _color[c] = parseInt(w._vS[p][4][c] + (w._vE[p][4][c] - w._vS[p][4][c]) * v )||0; } - color = 'rgb(' + _color.r + ',' + _color.g + ',' + _color.b + ') '; + colorValue = 'rgb(' + _color.r + ',' + _color.g + ',' + _color.b + ') '; + // for color interpolation, starting with v1.5.3 we can cut it short to this + // var colorValue = K.Interpolate.color(a[5],b[5],v); // last piece of the puzzle, the DOM update - w._el.style[_boxShadow] = inset ? color + numbers.join(' ') + inset : color + numbers.join(' '); + l.style[_boxShadow] = inset ? colorValue + numbers.join(' ') + inset : colorValue + numbers.join(' '); }; } @@ -305,12 +311,15 @@ var myBSTween3 = KUTE.fromTo('selector', {boxShadow: [5, 5, 0, '#069', 'inset']}
  • KUTE.property(propertyName) is the autoPrefix function that returns the property with the right vendor prefix, but only if required; on legacy browsers that don't support the property, the function returns undefinedPropertyName and that would be an easy way to detect support for that property on most legacy browsers:
    if (/undefined/.test(KUTE.property('propertyName')) ) { /* legacy browsers */ } else { /* modern browsers */ }
  • KUTE.getPrefix() returns a vendor preffix even if the browser supports a specific preffixed property or not.
  • KUTE.gCS(element,property) a hybrid getComputedStyle function to get the current value of the property required for the .to() method; it actually checks in element.style, element.currentStyle and window.getComputedStyle(element,null) to make sure it won't miss the property value;
  • -
  • KUTE.gIS() a getInlineStyle function to read the current inline style, very useful for transform, because decomposing a computed matrix would require a ton lot more code;
  • -
  • KUTE.truD(value) a function that accepts String and Number and returns a {v: 150, u: 'px'} object for any box model or a single numeric value based property and make it ready to tween.
  • +
  • KUTE.truD(value) a function that accepts String and Number and returns a {v: 150, u: 'px'} object for any box model or a single numeric value based property and make it ready to tween. When a second parameter is set to true it will return an object with value and unit specific for rotation angles and skews.
  • KUTE.truC(color) a function that returns an {r: 150, g: 150, b: 0} color object ready to tween; if the color value is a web safe color, the IE9+ browsers will be able to return the rgb object we need.
  • KUTE.htr(hex) a function that accepts HEX formatted colors and returns an {r: 150, g: 150, b: 0} color object;
  • KUTE.rth(r,g,b) a function that accepts numeric values for red, blue and green and returns a HEX format #006699 color string.
  • - +
  • KUTE.Interpolate.number is most essential interpolation tool when developing plugins for various properties not supported in the core.
  • +
  • KUTE.Interpolate.unit is used mainly for box model properties, text properties, and generally anything that's a string based valued. Like width: 250px
  • +
  • KUTE.Interpolate.color is a very fast interpolation function for colors, as used in the above example.
  • +
  • KUTE.Interpolate.array and KUTE.Interpolate.coords are SVG Plugin only, but you can have a look anytime when you're out of ideas.
  • + +

    SVG Properties

    +

    The SVG Plugin can animate the d attribute of a given <path> or <glyph> element with the tween property called path. The animation effect is widelly known as morph SVG and implemented in various scripts, but the KUTE.js implementation is similar to the D3.js examples for wider usability.

    + +

    Further more, the SVG Plugin can animate the stroke in a way that you probably know as drawSVG. KUTE.js implements it as draw tween property that deals with the well known CSS properties: strokeDasharray and strokeDashoffset. +

    The SVG Plugin also manages animation for most useful CSS properties that are specific to SVG elements, since SMIL animations tend to go extinct, this plugin can get quite useful.

    +
      +
    • strokeWidth allows you to animate the stroke-width for a given SVG element.
    • +
    • strokeOpacity allows you to animate the stroke-opacity for a given SVG element.
    • +
    • fillOpacity allows you to animate the fill-opacity for a given SVG element.
    • +
    • stopOpacity allows you to animate the stop-opacity for a given gradient SVG element.
    • +
    • fill allows you to animate the fill color property for a given SVG element.
    • +
    • stroke allows you to animate the stroke color for a given SVG element.
    • +
    • stopColor allows you to animate the stop-color for a given gradient SVG element.
    • +
    +

    Box Model Properties

    The core engine supports width, height, left and top while the CSS Plugin adds support for all other box-model properties.

      @@ -152,21 +167,6 @@
    • borderTopColor, borderRightColor, borderBottomColor and borderLeftColor properties allow you to animate the color of the border on each side of a given element.

    Remember: shorthands for borderColor property are not supported.

    - -

    SVG Properties

    -

    The SVG Plugin can animate the d attribute of a given <path> or <glyph> element with the tween property called path. The animation effect is widelly known as morph SVG and implemented in various scripts, but the KUTE.js implementation is similar to the D3.js examples for wider usability.

    - -

    Further more, the SVG Plugin can animate the stroke in a way that you probably know as drawSVG. KUTE.js implements it as draw tween property that deals with the well known CSS properties: strokeDasharray and strokeDashoffset. -

    The SVG Plugin also manages animation for most useful CSS properties that are specific to SVG elements, since SMIL animations tend to go extinct, this plugin can get quite useful.

    -
      -
    • strokeWidth allows you to animate the stroke-width for a given SVG element.
    • -
    • strokeOpacity allows you to animate the stroke-opacity for a given SVG element.
    • -
    • fillOpacity allows you to animate the fill-opacity for a given SVG element.
    • -
    • stopOpacity allows you to animate the stop-opacity for a given gradient SVG element.
    • -
    • fill allows you to animate the fill color property for a given SVG element.
    • -
    • stroke allows you to animate the stroke color for a given SVG element.
    • -
    • stopColor allows you to animate the stop-color for a given gradient SVG element.
    • -

    Presentation Attributes

    The Attributes Plugin can animate any numerical presentation attribute such as width, cx or stop-opacity, but the values can be also suffixed: 150px or 50%, and for that you must always provide a string value that include the measurement unit, and that, of course, depends on the attribute. This plugin can be a great addition to the above SVG Plugin for specific gradient attributes or specific geometric shapes' attributes.

    diff --git a/demo/src/kute-attr.js b/demo/src/kute-attr.js index 323f2f6..ed0bfb9 100644 --- a/demo/src/kute-attr.js +++ b/demo/src/kute-attr.js @@ -21,7 +21,8 @@ } }( function (KUTE) { 'use strict'; - var K = window.KUTE; + + var K = window.KUTE, DOM = K.dom, PP = K.pp, unit = K.Interpolate.unit, number = K.Interpolate.number; // get current attribute value K.gCA = function(e,a){ @@ -35,35 +36,35 @@ } return f; }; - // register the render attributes object - if (!('attr' in K.dom)) { - K.dom.attr = function(w,p,v){ - for ( var o in w._vE[p] ){ - K.dom.attr.prototype[o](w,o,v); + if (!('attr' in DOM)) { + DOM.attr = function(l,p,a,b,v) { + for ( var o in b ){ + DOM.attr.prototype[o](l,o,a[o],b[o],v); } }; } - var ra = K.dom.attr.prototype; - + var ra = DOM.attr.prototype; + // process attributes object K.pp.attr(t[x]) // and also register their render functions - K.pp['attr'] = function(a,o){ + PP['attr'] = function(a,o){ var ats = {}, p; for ( p in o ) { if ( /%|px/.test(o[p]) ) { var u = K.truD(o[p]).u, s = /%/.test(u) ? '_percent' : '_'+u; if (!(p+s in ra)) { - ra[p+s] = function(w,p,v){ - w._el.setAttribute(p.replace(s,''), (w._vS.attr[p].v + (w._vE.attr[p].v - w._vS.attr[p].v) * v) + w._vE.attr[p].u); + ra[p+s] = function(l,p,a,b,v) { + var _p = p.replace(s,''); + l.setAttribute(_p, unit(a.v,b.v,b.u,v) ); } } ats[p+s] = K.truD(o[p]); } else { if (!(p in ra)) { - ra[p] = function(w,p,v){ - w._el.setAttribute(p, w._vS.attr[p] + (w._vE.attr[p]- w._vS.attr[p]) * v); + ra[p] = function(l,o,a,b,v) { + l.setAttribute(o, number(a,b,v)); } } ats[p] = o[p] * 1; diff --git a/demo/src/kute-bezier.js b/demo/src/kute-bezier.js index f402215..0f631d7 100644 --- a/demo/src/kute-bezier.js +++ b/demo/src/kute-bezier.js @@ -5,26 +5,6 @@ * optimized by dnp_theme 2015 – MIT License * Licensed under MIT-License */ - -// /* THIS IS THE OLD CODE */ -// (function(kute_ea){ -// // Obtain a reference to the base KUTE. -// // Since KUTE supports a variety of module systems, -// // we need to pick up which one to use. -// if(define == "function") { -// define(["./kute.js"], function(KUTE){ kute_ea(KUTE); return KUTE; }); -// } else if(typeof module == "object" && typeof require == "function") { -// // We assume, that require() is sync. -// var KUTE = require("./kute.js"); -// kute_ea(KUTE); -// // Export the modified one. Not really required, but convenient. -// module.exports = KUTE; -// } else if(typeof window.KUTE != "undefined") { -// kute_ea(window.KUTE); -// } else { -// throw new Error("KUTE.js Bezier/Easing depends on KUTE.js. Read the docs for more info.") -// } -// })(function(KUTE){ (function (factory) { if (typeof define === 'function' && define.amd) { diff --git a/demo/src/kute-css.js b/demo/src/kute-css.js index 12d6261..8dc94db 100644 --- a/demo/src/kute-css.js +++ b/demo/src/kute-css.js @@ -16,7 +16,7 @@ throw new Error("CSS Plugin require KUTE.js.") } })(function(KUTE){ - var K = window.KUTE, p, + var K = window.KUTE, p, DOM = K.dom, PP = K.pp, _br = K.property('borderRadius'), _brtl = K.property('borderTopLeftRadius'), _brtr = K.property('borderTopRightRadius'), // all radius props prefixed _brbl = K.property('borderBottomLeftRadius'), _brbr = K.property('borderBottomRightRadius'), _cls = ['borderColor', 'borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor', 'outlineColor'], // colors 'hex', 'rgb', 'rgba' -- #fff / rgb(0,0,0) / rgba(0,0,0,0) @@ -28,7 +28,8 @@ _clp = ['clip'], _bg = ['backgroundPosition'], // clip | background position _mg = _rd.concat(_bm,_tp), // a merge of all properties with px|%|em|rem|etc unit - _all = _cls.concat(_clp, _rd, _bm, _tp, _bg), al = _all.length, + _all = _cls.concat(_clp, _rd, _bm, _tp, _bg), al = _all.length, + number = K.Interpolate.number, unit = K.Interpolate.unit, color = K.Interpolate.color, _d = _d || {}; //all properties default values //populate default values object @@ -48,26 +49,13 @@ // create prepare/process/render functions for additional colors properties for (var i = 0, l = _cls.length; i 2) { - return a[0].trim() + 'z'; - } else { return p.trim(); } + return p.split(/z/i).shift() + 'z'; } S.cP = function (p){ // createPath @@ -216,7 +223,7 @@ 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]; } + if (i === 0) { x+=np[i][0]; y +=np[i][1]; } else { x = np[i-1][0]; y = np[i-1][1]; @@ -234,23 +241,16 @@ } return np; } - - // a shortHand pathCross - K.svq = function(w){ if ( w._vE.path ) S.pCr(w); } + + // a shortHand pathCross && SVG transform stack + K.svq = function(w){ if ( w._vE.path ) S.pCr(w); if ( w._vE.svgTransform ) S.sT(w); } // register the render SVG path object // process path object and also register the render function K.pp['path'] = function(a,o,l) { // K.pp['path']('path',value,element); - if (!('path' in K.dom)) { - K.dom['path'] = function(w,p,v){ - var points =[], i, l; - for(i=0,l=w._vE.path.d.length;i 1) { var coord = function (p) { @@ -373,30 +369,16 @@ 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; - } - + } // SVG CSS Color Properties for ( var i = 0, l = _cls.length; i< l; i++) { p = _cls[i]; - K.pp[p] = function(p,v){ - if (!(p in K.dom)) { - K.dom[p] = function(w,p,v){ - var _c = {}; - for (var c in w._vE[p]) { - if ( c !== 'a' ){ - _c[c] = parseInt(w._vS[p][c] + (w._vE[p][c] - w._vS[p][c]) * v )||0; - } else { - _c[c] = (w._vS[p][c] && w._vE[p][c]) ? parseFloat(w._vS[p][c] + (w._vE[p][c] - w._vS[p][c]) * v) : null; - } - } - - if ( w._hex ) { - w._el.style[p] = K.rth( _c.r, _c.g, _c.b ); - } else { - w._el.style[p] = !_c.a ? 'rgb(' + _c.r + ',' + _c.g + ',' + _c.b + ')' : 'rgba(' + _c.r + ',' + _c.g + ',' + _c.b + ',' + _c.a + ')'; - } - } + 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); + }; } return K.truC(v); } @@ -404,23 +386,25 @@ return K.gCS(el,p) || 'rgba(0,0,0,0)'; } } + for ( var i = 0, l = _nm.length; i< l; i++) { // for numeric CSS props from any type of SVG shape p = _nm[i]; - if (p === 'strokeWidth'){ - K.pp[p] = function(p,v){ - if (!(p in K.dom)) { - K.dom[p] = function(w,p,v) { - w._el.style[p] = (w._vS[p].value + (w._vE[p].value - w._vS[p].value) * v) + w._vS[p].unit; + 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); } } - return K.pp.box(p,v); + return /px|%|em|vh|vw/.test(v) ? PP.box(p,v) : parseFloat(v); } } else { - K.pp[p] = function(p,v){ - if (!(p in K.dom)) { - K.dom[p] = function(w,p,v) { - w._el.style[p] = w._vS[p] + (w._vE[p] - w._vS[p]) * v; + PP[p] = function(p,v){ + if (!(p in DOM)) { + DOM[p] = function(l,p,a,b,v) { + l.style[p] = number(a,b,v); } } return parseFloat(v); @@ -432,72 +416,115 @@ } // SVG Transform - K.pp['svgTransform'] = function(p,v,l){ + PP['svgTransform'] = function(p,v,l){ // register the render function - if (!('svgTransform' in K.dom)) { - K.dom['svgTransform'] = function(w,p,v) { + if (!('svgTransform' in DOM)) { + DOM['svgTransform'] = function(l,p,a,b,v){ var tr = '', i; - for (i in w._vE[p]){ + for (i in b){ tr += i + '('; // start string if ( i === 'translate'){ // translate - tr += (w._vS[p][i][1] === w._vE[p][i][1] && w._vE[p][i][1] === 0 ) - ? (w._vS[p][i][0] + (w._vE[p][i][0] - w._vS[p][i][0]) * v ) - : (w._vS[p][i][0] + (w._vE[p][i][0] - w._vS[p][i][0]) * v ) + ' ' + (w._vS[p][i][1] + (w._vE[p][i][1] - w._vS[p][i][1]) * v ); + 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); } else if ( i === 'rotate'){ // rotate - tr += w._vS[p][i][0] + (w._vE[p][i][0] - w._vS[p][i][0]) * v + ' '; - tr += !w.reversed ? w._vE[p][i][1] + ',' + w._vE[p][i][2] : w._vS[p][i][1] + ',' + w._vS[p][i][2]; // make sure to always use the right transform-origin + tr += number(a[i][0],b[i][0],v) + ' '; + tr += b[i][1] + ',' + b[i][2]; } else { // scale, skewX or skewY - tr += w._vS[p][i] + (w._vE[p][i] - w._vS[p][i]) * v; + tr += number(a[i],b[i],v); } tr += ') '; // end string } - w._el.setAttributeNS(null,'transform', tr.replace(/\)\s$/,')') ); + l.setAttribute('transform', tr.trim() ); } } - // return transform object - var tf = {}, bb = l.getBBox(), cx = bb.x + bb.width/2, cy = bb.y + bb.height/2, i, r, t; - for ( i in v ) { + // 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 if (i === 'rotate'){ - r = v[i] instanceof Array ? v[i] + r = v[i] instanceof Array ? v[i] : /\s/.test(v[i]) ? [v[i].split(' ')[0]*1, v[i].split(' ')[1].split(',')[0]*1, v[i].split(' ')[1].split(',')[1]*1] - : [v[i],cx,cy]; + : [v[i]*1,cx,cy]; tf[i] = r; } else if (i === 'translate'){ - t = v[i] instanceof Array ? v[i] : /\s/.test(v[i]) ? v[i].split(' ') : [v[i],0]; - tf[i] = [t[0] * 1||0, t[1] * 1]; + 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]; } else if (i === 'scale'){ tf[i] = v[i] * 1||1; - } else { + } else if (/skew/.test(i)) { tf[i] = v[i] * 1||0; } } + // 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 // http://www.petercollingridge.co.uk/interactive-svg-components/pan-and-zoom-control - // try to adjust translation when scale is used if ('scale' in tf) { - tf['translate'] = !( 'translate' in tf ) ? [0,0] : tf['translate']; - tf['translate'][0] += (1-tf['scale']) * bb.width/2; + !('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; tf['translate'][1] += (1-tf['scale']) * bb.height/2; - } + // 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 + 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) { - var tr = {}, cta = l.getAttributeNS(null,'transform'), - ct = cta && /\)/.test(cta) ? cta.split(')') : 'none', i, j, ctr ={}, pr; - if (ct instanceof Array) { - for (j=0; j,./?\=-").split(""), // symbols _n = String("0123456789").split(""), // numeric _a = _s.concat(_S,_n), // alpha numeric _all = _a.concat(_sb), // all caracters - _r = Math.random, _f = Math.floor, _m = Math.min; + random = Math.random, floor = Math.floor, min = Math.min; K.prS['text'] = K.prS['number'] = function(l,p,v){ return l.innerHTML; } - K.pp['text'] = function(p,v,l) { - if ( !( 'text' in K.dom ) ) { - K.dom['text'] = function(w,p,v) { - var tp = tp || w.textChars === 'alpha' ? _s // textChars is alpha - : w.textChars === 'upper' ? _S // textChars is numeric - : w.textChars === 'numeric' ? _n // textChars is numeric - : w.textChars === 'alphanumeric' ? _a // textChars is alphanumeric - : w.textChars === 'symbols' ? _sb // textChars is symbols - : w.textChars ? w.textChars.split('') // textChars is a custom text - : _s, l = tp.length, s = w._vE[p], - t = tp[_f((_r() * l))], tx = '', f = s.substring(0); + PP['text'] = function(p,v,l) { + if ( !( 'text' in DOM ) ) { + DOM['text'] = function(l,p,a,b,v,o) { + var tp = tp || o.textChars === 'alpha' ? _s // textChars is alpha + : o.textChars === 'upper' ? _S // textChars is numeric + : o.textChars === 'numeric' ? _n // textChars is numeric + : o.textChars === 'alphanumeric' ? _a // textChars is alphanumeric + : o.textChars === 'symbols' ? _sb // textChars is symbols + : o.textChars ? o.textChars.split('') // textChars is a custom text + : _s, ll = tp.length, + t = tp[floor((random() * ll))], ix = '', tx = '', fi = a.substring(0), f = b.substring(0); - tx = f.substring(0,_f(_m(v * f.length, f.length))); - w._el.innerHTML = v < 1 ? tx+t : tx; + // use string.replace(/<\/?[^>]+(>|$)/g, "") to strip HTML tags while animating ? + ix = a !== '' ? fi.substring(fi.length,floor(min(v * fi.length, fi.length))) : ''; // initial text, A value + tx = f.substring(0,floor(min(v * f.length, f.length))); // end text, B value + l.innerHTML = v < 1 ? tx + t + ix : b; } } return v; } - K.pp['number'] = function(p,v,l) { - if ( !( 'number' in K.dom ) ) { - K.dom['number'] = function(w,p,v) { - w._el.innerHTML = parseInt(w._vS[p] + (w._vE[p] - w._vS[p]) * v); + PP['number'] = function(p,v,l) { + if ( !( 'number' in DOM ) ) { + DOM['number'] = function(l,p,a,b,v) { + l.innerHTML = parseInt( number(a, b, v)); } } return parseInt(v) || 0; diff --git a/demo/src/kute.js b/demo/src/kute.js index c82329b..5ed87f7 100644 --- a/demo/src/kute.js +++ b/demo/src/kute.js @@ -13,37 +13,137 @@ } }( function () { "use strict"; - var K = K || {}, _tws = [], _t, _min = Math.min; + var K = K || {}, _tws = [], _t = null, - K.getPrefix = function() { //returns browser prefix - var div = document.createElement('div'), i = 0, pf = ['Moz', 'moz', 'Webkit', 'webkit', 'O', 'o', 'Ms', 'ms'], pl = pf.length, - s = ['MozTransform', 'mozTransform', 'WebkitTransform', 'webkitTransform', 'OTransform', 'oTransform', 'MsTransform', 'msTransform']; - for (i; i < pl; i++) { if (s[i] in div.style) { return pf[i]; } } - div = null; - }; - - K.property = function(p){ // returns prefixed property | property - var r = (!(p in document.body.style)) ? true : false, f = K.getPrefix(); // is prefix required for property | prefix - return r ? f + (p.charAt(0).toUpperCase() + p.slice(1)) : p; - }; - - K.selector = function(el,multi){ // a selector utility - var nl; - if (multi){ - nl = el instanceof NodeList ? el : document.querySelectorAll(el); - } else { - nl = typeof el === 'object' ? el : /^#/.test(el) ? document.getElementById(el.replace('#','')) : document.querySelector(el); - } - if (nl === null && el !== 'window') throw new TypeError('Element not found or incorrect selector: '+el); - return nl; - }; - - var _tch = ('ontouchstart' in window || navigator.msMaxTouchPoints) || false, // support Touch? + getPrefix = function() { //returns browser prefix + var div = document.createElement('div'), i = 0, pf = ['Moz', 'moz', 'Webkit', 'webkit', 'O', 'o', 'Ms', 'ms'], pl = pf.length, + s = ['MozTransform', 'mozTransform', 'WebkitTransform', 'webkitTransform', 'OTransform', 'oTransform', 'MsTransform', 'msTransform']; + for (i; i < pl; i++) { if (s[i] in div.style) { return pf[i]; } } + div = null; + }, + property = function(p){ // returns prefixed property | property + var r = (!(p in document.body.style)) ? true : false, f = getPrefix(); // is prefix required for property | prefix + return r ? f + (p.charAt(0).toUpperCase() + p.slice(1)) : p; + }, + selector = function(el,multi){ // a selector utility + var nl; + if (multi){ + nl = el instanceof NodeList ? el : document.querySelectorAll(el); + } else { + nl = typeof el === 'object' ? el + : /^#/.test(el) ? document.getElementById(el.replace('#','')) : document.querySelector(el); + } + if (nl === null && el !== 'window') throw new TypeError('Element not found or incorrect selector: '+el); + return nl; + }, + trueDimendion = function (d,p) { //true dimension returns { v = value, u = unit } + var x = parseInt(d) || 0, mu = ['px','%','deg','rad','em','rem','vh','vw'], l = mu.length, + y = getU(); + function getU() { + var u,i=0; + for (i;i 0) { - if ( w._r < 9999 ) { w._r--; } + if ( w._r < 9999 ) { w._r--; } // we have to make it stop somewhere, infinity is too damn much if (w._y) { w.reversed = !w.reversed; w.rvs(); } // handle yoyo @@ -130,8 +253,7 @@ if (w._cC) { w._cC.call(); } - //stop preventing scroll when scroll tween finished - w.scrollOut(); + w.scrollOut(); // unbind preventing scroll when scroll tween finished // start animating chained tweens var i = 0, ctl = w._cT.length; @@ -145,41 +267,25 @@ } } return true; - }; - - // internal ticker - K._tk = function (t) { - var i = 0, tl; - _t = _raf(K._tk); - while ( i < (tl = _tws.length) ) { - if ( K._u(_tws[i],t) ) { - i++; - } else { - _tws.splice(i, 1); - } - } - }; - - // aplies the transform origin and perspective - K.perspective = function (l,w) { + }, + + // applies the transform origin and perspective + perspective = function (l,w) { if ( w._to !== undefined ) { l.style[_pfto] = w._to; } // element transform origin if ( w._ppo !== undefined ) { l.style[_pfo] = w._ppo; } // element perspective origin if ( w._ppp !== undefined ) { l.parentNode.style[_pfp] = w._ppp + 'px'; } // parent perspective if ( w._pppo !== undefined ) { l.parentNode.style[_pfo] = w._pppo; } // parent perspective origin - }; + }, //more internals - K.getAll = function () { return _tws; }; - K.removeAll = function () { _tws = []; }; - K.add = function (tw) { _tws.push(tw); }; - K.remove = function (tw) { - var i = _tws.indexOf(tw); - if (i !== -1) { _tws.splice(i, 1); } - }; - K.s = function () { _caf(_t); _t = null; }; + getAll = function () { return _tws; }, + removeAll = function () { _tws = []; }, + add = function (tw) { _tws.push(tw); }, + remove = function (tw) { var i = _tws.indexOf(tw); if (i !== -1) { _tws.splice(i, 1); }}, + stop = function () { _t && _caf(_t); _t = null; }, // process properties for _vE and _vS or one of them - K.prP = function (e, s, l) { + preparePropertiesObject = function (e, s, l) { var i, pl = arguments.length, _st = []; pl = pl > 2 ? 2 : pl; for (i=0; i 0) { self._r = self.repeat; } - if (self._y && self.reversed===true) { self.rvs(); self.reversed = false; } - self.playing = false; - },64) - }; + + this.close = function () { + var self = this; + setTimeout(function(){ + var i = _tws.indexOf(self); + if (i === _tws.length-1) { stop(); } + if (self.repeat > 0) { self._r = self.repeat; } + if (self._y && self.reversed===true) { self.rvs(); self.reversed = false; } + self.playing = false; + },64) + } + }, // the multi elements Tween constructs - K.TweensTO = function (els, vE, o) { // .to + TweensTO = function (els, vE, o) { // .to this.tweens = []; var i, tl = els.length, _o = []; for ( i = 0; i < tl; i++ ) { _o[i] = o || {}; o.delay = o.delay || 0; _o[i].delay = i>0 ? o.delay + (o.offset||0) : o.delay; - this.tweens.push( K.to(els[i], vE, _o[i]) ); + this.tweens.push( to(els[i], vE, _o[i]) ); } - }; - K.TweensFT = function (els, vS, vE, o) { // .fromTo + }, + TweensFT = function (els, vS, vE, o) { // .fromTo this.tweens = []; var i, tl = els.length, _o = []; for ( i = 0; i < tl; i++ ) { _o[i] = o || {}; o.delay = o.delay || 0; _o[i].delay = i>0 ? o.delay + (o.offset||0) : o.delay; - this.tweens.push( K.fromTo(els[i], vS, vE, _o[i]) ); + this.tweens.push( fromTo(els[i], vS, vE, _o[i]) ); } }; - var ws = K.TweensTO.prototype = K.TweensFT.prototype; + + var ws = TweensTO.prototype = TweensFT.prototype; ws.start = function(t){ t = t || window.performance.now(); var i, tl = this.tweens.length; @@ -799,5 +733,53 @@ ws.chain = function(){ this.tweens[this.tweens.length-1]._cT = arguments; return this; } ws.play = ws.resume = function(){ for ( var i = 0; i < this.tweens.length; i++ ) { this.tweens[i].play(); } return this; } + var start = function (t) { // move functions that use the ticker outside the prototype to be in the same scope with it + this.scrollIn(); + + perspective(this._el,this); // apply the perspective and transform origin + if ( this._rpr ) { this.stack(); } // on start we reprocess the valuesStart for TO() method + + for ( var e in this._vE ) { + this._vSR[e] = this._vS[e]; + } + + // now it's a good time to start + add(this); + this.playing = true; + this.paused = false; + this._sCF = false; + this._sT = t || window.performance.now(); + this._sT += this._dl; + + if (!this._sCF) { + if (this._sC) { this._sC.call(); } + this._sCF = true; + } + if (!_t) _tk(); + return this; + }, + play = function () { + if (this.paused && this.playing) { + this.paused = false; + if (this._rC !== null) { this._rC.call(); } + this._sT += window.performance.now() - this._pST; + add(this); + if (!_t) _tk(); // restart ticking if stopped + } + return this; + }; + Tween.prototype.start = start; + Tween.prototype.play = Tween.prototype.resume = play; + + K = { // export core methods to public for plugins + property: property, getPrefix: getPrefix, selector: selector, pe : pe, // utils + to: to, fromTo: fromTo, allTo: allTo, allFromTo: allFromTo, // main methods + Interpolate: {number: number, unit: unit, color: color }, // interpolators ?? move array & coords to svg and leave color + dom: DOM, // DOM manipulation + pp: parseProperty, prS: prepareStart, // init + truD: trueDimendion, truC: trueColor, rth: rgbToHex, htr: hexToRGB, gCS: getComputedStyle, // property parsing + Easing: easing, + Tween: Tween, TweensTO: TweensTO, TweensFT: TweensFT // constructors + }; return K; })); \ No newline at end of file diff --git a/demo/svg.html b/demo/svg.html index b20396f..148f5ed 100644 --- a/demo/svg.html +++ b/demo/svg.html @@ -23,7 +23,7 @@ - + @@ -282,7 +282,7 @@ var morph4 = KUTE.fromTo('#startpath2', { path: '#startpath2' }, {
  • When morphing subpaths/multipaths instead of cloning shapes to have same number of shapes in both starting and ending shapes, you should also consider a fade and/or scale animation to improve the overal animation performance, don't forget about mobile devices.
  • Large displays would need best resolution possible so a small morphPrecision value (1-10) would be required, assuming performant hardware are powering the displays. For small displays you can get quite comfortable with almost any value, including the default value.
  • Polygons with only lineto path commands are best for performance.
  • -
  • Faster animation speed could be a great trick to hide any polygonal "artefacts".
  • +
  • Faster animation speed could be a great trick to hide any polygonal "artefacts". Strokes are also very useful for hiding the polygons' edges.
  • Always use the showMorphInfo:true tween option to check how the values required for the morph change with every new option value, but never forget to disable it after you have optimized the morph to your liking, this option enables a function that detects the best index for points rotation that is very expensive and delays the animation for quite some time.
  • The SVG morph performance is the same for both .to() and .fromTo() methods, but the ones that use the second method will start faster, because the values have been prepared already and for the first method the processing of the two paths happens on tween start delaying the animation, so keep that in mind when working with syncing multiple tweens, the .to() based morph will always start later. Of course this assumes the you cache the tween objects first and start the animation later, if not (you start the animation on object creation), both methods will be delayed.
  • @@ -314,13 +314,21 @@ var tween3 = KUTE.fromTo('selector1',{draw:'0% 100%'}, {draw:'50% 50%'});

    Remember: the draw property also accepts absolute values, eg. draw: '0 150'; the .to() method takes 0% 100% as start value for your tweens when stroke-dasharray and stroke-dashoffset are not set.

    SVG Transforms

    -

    Starting with KUTE.js 1.5.2, the SVG Plugin features a new tween property for cross browser transforms for SVG, but I decided to code a separate set of methods for SVG only, to keep performance tight. A very simple roadmap was described here; in brief we needed to find a way to enable transforms for SVG in a reliable and cross-browser supported fashion. In that sense the plugin will allow you to animate SVG shapes via the transform presentation attribute and the svgTransform tween property. To make sure we don't mess up with the current methods, we changed the syntax a little bit:

    -
    // using all possible values
    -var tween1 = KUTE.to('selector', {svgTransform: { translate: [150,100], rotate: [45,0,0], skewX: 15, skewY: 20, scale: [1.2,1.3] }});
    -// using shorthand notations
    -var tween2 = KUTE.to('selector', {svgTransform: { translate: 120, rotate: 45, scale: 1.2 }});
    +		

    Starting with KUTE.js 1.5.2, the SVG Plugin features a new tween property for cross browser SVG transforms, but was coded as a separate set of methods for SVG only, to keep performance tight. A very simple roadmap was described here; in brief we needed to find a way to enable transforms for SVG in a reliable and cross-browser supported fashion.

    +

    While you can still use regular CSS3 transforms for SVGs on browsers like Google Chrome and Firefox, to animate SVG shapes on all browsers, we use the transform presentation attribute and the svgTransform tween property with a special notation:

    +
    // regular CSS3 transforms apply to SVG elements, but not working in IE and some older Opera versions
    +var tween1 = KUTE.to('shape', { translate: [150,100], rotate: 45, skewX: 15, skewY: 20, scale: 1.5 }, {transformOrigin: "50% 50%"});
    +
    +// using the svgTransform property, working in all browsers
    +// the Y translation as well as rotate transform-origin are provided
    +var tween2 = KUTE.to('shape', {svgTransform: { translate: [120,100], rotate: [45,0,0], skewX: 15, skewY: 20, scale: 1.2 }}); 
    +
    +// OR using shorthand notations
    +// we understand the Y translation is 0 and the rotation transform-origin is 50% 50%, 
    +// the center of the shape and not the parent SVG's center
    +var tween2 = KUTE.to('shape', {svgTransform: { translate: 120, rotate: 45, scale: 1.2 }}); 
     
    -

    As you can see we have some familiar notation as well as new notation. Let's break it down.

    +

    As you can see we have some familiar notation as well as new notation. One thing to know is that this feature may not support and wasn't tested for SVG specific chained/nested transformations. Perhaps the most important thing to remember is the fact that SVG tranformations always use SVG coordinates system, and the transform attribute requires no degree or pixel as measurement units. Let's break it down.

    SVG Rotation

    In our first example we'll have a look at rotations. For instance setting rotate: [45,0,0], the first value is the angle to which the shape will rotate to and the other two values are coordinates for the transform-origin. When rotate: 45 notation is used, the script will calculate the coordinates of the shape's central point, as if your transform-origin default value is 50% 50%, something you would expect from regular HTML elements. However keep in mind that the transform-origin coordinates are relative to the parent <svg> element. Let's have a look at a quick demo:

    @@ -334,19 +342,39 @@ var tween2 = KUTE.to('selector', {svgTransform: { translate: 120, rotate: 45, sc Start
    -

    The first tween uses the rotate: [-360,0,0] notation and the animation clearly shows the shape rotating around it's top left coordinate. The second tween uses the rotate: 360 notation and the animation shows the shape rotating around it's own central point.

    -

    When for CSS3 transforms we could have used values such as 50% 50% or center bottom as transform-origin and the entire processing was basically none, when it comes to SVG we need to make use of the SVG API functions to determine the proper value. But stay cool, here comes .getBBox() to the rescue! And here's how to:

    -
    // get the bounding box
    -var bb = element.getBBox(); // returns an object like {x:0, y:20, width:200, height: 200} for any type of shape
    +        

    The first tween uses the CSS3 transform notation and the animation clearly shows the shape rotating around it's center coordinate, as we've set transformOrigin option to 50% 50%, but this animation doesn't work in all browsers. The second tween uses the rotate: 360 notation and the animation shows the shape rotating around it's own central point, an animation that DO WORK in all SVG enabled browsers.

    +

    When for CSS3 transforms we could have used values such as center bottom as transform-origin (also not supported in all modern browsers), the entire processing was basically in/by the browser, however when it comes to SVG we need to make use of the SVG API functions to determine the proper value. But stay cool, here comes .getBBox() to the rescue! Here's how to rotate an element around it's 25% 75% coordinate:

    + +
    // rotate around element's "25% 75%"" coordinate as transform-origin
    +// get the bounding box
    +// returns an object like {x:0, y:20, width:200, height: 200} for any type of SVG shape
    +// x is the distance from top, y is the distance from left, width and height are just that
    +var bb = element.getBBox(); 
         
     // determine the X point of transform-origin for 25%
    -var transformOriginX = bb.x + (25 * bb.width / 100);
    +var shapeOriginX = bb.x + (25 * bb.width / 100);
     
     // determine the Y point of transform-origin for 75%
    -var transformOriginY = bb.y + (75 * bb.height / 100);
    +var shapeOriginY = bb.y + (75 * bb.height / 100);
     
    -// set your rotation tween with "25% 75%" transform-origin
    -var rotationTween = KUTE.to(element, {svgTransform: {rotate: [45, transformOriginX, transformOriginY]}});
    +// set your rotation tween with "25% 75%" transform-origin 
    +var rotationTween = KUTE.to(element, {svgTransform: {rotate: [45, shapeOriginX, shapeOriginY]}});
    +
    + +

    Or you can rotate an element around the parent's <svg> 50% 50% coordinate:

    + +
    // rotate around parent svg's "50% 50%" coordinate as transform-origin
    +// get the bounding box
    +var svgBB = element.ownerSVGElement.getBBox(); // returns same object but it's for the parent <svg> element
    +    
    +// determine the X point of transform-origin for 50%
    +var svgOriginX = svgBB.x + (50 * svgBB.width / 100);
    +
    +// determine the Y point of transform-origin for 50%
    +var svgOriginY = svgBB.y + (50 * svgBB.height / 100);
    +
    +// set your rotation tween with "50% 50%" transform-origin of the parent <svg> element
    +var rotationTween = KUTE.to(element, {svgTransform: {rotate: [150, svgOriginX, svgOriginY]}});
     

    SVG Translation

    @@ -361,7 +389,7 @@ var rotationTween = KUTE.to(element, {svgTransform: {rotate: [45, transformOrigi Start
    -

    The first tween uses the translate: [580,0] notation and the second tween uses the translate: 0 notation. Keep in mind that the values are unitless and are relative to the viewBox attribute.

    +

    The first tween uses the CSS3 translate: 580 notation and the second tween uses the translate: [0,0] as svgTransform value. For the second example the values are unitless and are relative to the viewBox attribute.

    SVG Skew

    For skews for SVGs we have a very simple notation: skewX: 25 or skewY: -25 as SVGs don't support the skew: [X,Y] function. Here's a quick demo:

    @@ -375,10 +403,11 @@ var rotationTween = KUTE.to(element, {svgTransform: {rotate: [45, transformOrigi Start -

    The first tween skews the shape on X (horizontal) axis and the second tween skews the shape on Y (vertical) axis.

    +

    The first tween skews the shape on both X and Y axis in a chain via regular CSS3 transforms and the second tween skews the shape on X and Y axis via the svgTransform tween property. The example also showcases the fact that chain transformations for SVGs via transform attribute works just as for the CSS3 transformations and you really have to check the example code here to learn how to master this technique.

    +

    For this particular example we've set transformOrigin option to "0px 0px" for the first shape (the default value for SVGs' transform-origin on most browsers) because we cannot set anything similar for the skews of the transform attribute for the second shape. Generally skews are hard to handle for SVGs because they are independent of any transform-origin. We can only compensate with translation, a very complicated story.

    SVG Scaling

    -

    Our last transform example for SVGs is the scale. Unlike translations, for scale animation the plugin only accepts single value like scale: 1.5, for both X (horizontal) axis and Y (vertical) axis, to keep it simple and even if SVGs do support scale(X,Y). But because the scaling on SVGs depends very much on the shape's position, the script will always try to adjust the translation to make the animation look as we would expect. A quick demo:

    +

    Another transform example for SVGs is the scale. Unlike translations, for scale animation the plugin only accepts single value like scale: 1.5, for both X (horizontal) axis and Y (vertical) axis, to keep it simple and even if SVGs do support scale(X,Y). But because the scaling on SVGs depends very much on the shape's position, the script will always try to adjust the translation to make the animation look as we would expect. A quick demo:

    @@ -389,15 +418,29 @@ var rotationTween = KUTE.to(element, {svgTransform: {rotate: [45, transformOrigi Start
    -

    The first tween scales the shape at scale: 1.5 and the second tween scales down the shape at scale: 0.5 value. If you inspect the elements, you will notice for both shapes translation is involved, and this is to keep transform-origin at an expected 50% 50% value, well, approximately.

    +

    The first tween scales the shape at scale: 1.5 via regular CSS3 transforms, and the second tween scales down the shape at scale: 0.5 value via svgTransform. If you inspect the elements, you will notice for the second shape translation is involved, and this is to keep transform-origin at an expected 50% 50% value. A similar case as with the skews.

    + +

    SVG Mixed Transforms

    +

    Our last transform example for SVGs is the mixed transformation. Similar with scale animation the plugin will try to adjust the rotation transform-origin to make it look as you would expect it from regular HTML elements. Let's combine 3 functions at the same time and see what happens:

    +
    + + + + + +
    + Start +
    +
    +

    Both shapes are scaled at scale: 1.5, translated to translate: 250 and skewed at skewX: -15. If you inspect the elements, you will notice the second shape's translation is different from what we've set in the tween object, and this is to keep transform-origin at an expected 50% 50% value. This means that the plugin will also compensate rotation transform origin when skews are used, so that both CSS3 transform property and SVG transform attribute have an identical animation.

    Recommendations for SVG Transforms