Skip to content

Instantly share code, notes, and snippets.

@thednp
Last active August 30, 2016 20:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thednp/0b93068e20adb84658b5840ead0a07f8 to your computer and use it in GitHub Desktop.
Save thednp/0b93068e20adb84658b5840ead0a07f8 to your computer and use it in GitHub Desktop.
process SVG transforms
// function to process a **string value of a SVG transform attribute** to have same aspect as a CSS3 transform
// with the transform origin of 50% 50%
// http://stackoverflow.com/questions/39191054/how-to-compensate-translate-when-skewx-and-skewy-are-used-on-svg/39210937
function combineTranslates(transform) {
var ts = [], // will contain list of elementary transformations
r = /\s*([A-Za-z0-9]+\s*\([\-0-9.,\s]*\))/g,
match,
pos = 0, // used during tokenization
deg = Math.PI/180.0,
x = 0, y = 0, // translation gets accumulated here
tmp;
// Tokenize transform into individual elementary transformations
while (match = r.exec(transform)) {
if (match.index !== pos) throw Error('Invalid transform: ' + transform);
pos += match[0].length;
ts.push(match[1]);
}
// TODO: check that only whitespace remains after matches
//console.log(ts);
// Iterate over transformations from inside to outside
for (var i = ts.length - 1; i >= 0; --i) {
match = /([A-Za-z0-9]+)\s*\(([\-0-9.,\s]*)\)/.exec(ts[i]);
var op = match[1],
args = match[2].replace(/\s+/g, '').split(',').map(Number);
//console.log(op, args);
switch (op) {
// Apply given transformation to (x,y) vector
case 'translate':
x += args[0];
y += args[1];
ts.splice(i, 1); // Drop translate from ts array
break;
case 'rotate':
var angle = args[0]*deg,
cos = Math.cos(angle),
sin = Math.sin(angle);
tmp = cos*x - sin*y;
y = sin*x + cos*y;
x = tmp;
break;
case 'scale':
x *= args[0];
y *= (args.length === 1 ? args[0] : args[1]);
break;
case 'skewX':
x += y*Math.tan(args[0]*deg);
break;
case 'skewY':
y += x*Math.tan(args[0]*deg);
break;
default:
throw Error('Unknown transform ' + op)
}
}
ts.unshift('translate('+x+','+y+')'); // add as first element
//console.log('From '+transform+'\n to '+ts.join(' '));
return ts.join(' ');
};
// my clean take on the problem, with almost working code
var tf = { translate: [0,0], rotate: [0,0,0], skewX: 0, skewY: 0, scale: 1 }
if ('scale' in tf) {
!('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;
} else if (!('scale' in tf) && !('rotate' in tf) ) {
!('translate' in tf) && ( tf['translate'] = [bb.x,bb.y] ); // if no translate is found in current value or next value, we default to 0
tf['translate'][0] += 'skewX' in tf ? (Math.tan(tf['skewX']) * bb.height/Math.PI)/2 : 0;
tf['translate'][1] += 'skewY' in tf ? (Math.tan(tf['skewY']) * bb.width/Math.PI)/2 : 0;
}
// function to process an **object** with SVG transform attribute values
// to have same aspect as a CSS3 transform with the transform origin of 50% 50%
// http://stackoverflow.com/questions/39191054/how-to-compensate-translate-when-skewx-and-skewy-are-used-on-svg/39210937
function combineTranslates(t) {
var deg = Math.PI/180.0,
x = 0, y = 0, tmp; // translation gets accumulated here
// Iterate over transformations from inside to outside
for (var op in t) {
var args = t[op];
switch (op) {
// Apply given transformation to (x,y) vector
case 'translate': x += args[0]; y += args.length === 2 ? args[1] : 0; break; // Drop translate from ts array
case 'rotate':
var angle = args[0]*deg, cos = Math.cos(angle), sin = Math.sin(angle);
tmp = cos*x - sin*y; y = sin*x + cos*y; x = tmp;
break;
case 'scale': x *= args; y *= args; break; // x *= args[0]; y *= (args.length === 1 ? args[0] : args[1]);
case 'skewX': x += y*Math.tan(args*deg); break;
case 'skewY': y += x*Math.tan(args*deg); break;
default: throw Error('Unknown transform function ' + op)
}
}
return [x,y];
};
// console.log(combineTranslates(tf) );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment