Skip to content

Commit

Permalink
Fix #6 - add interpolateTransformCss.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Feb 26, 2016
1 parent 9e5c767 commit be1eb45
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 111 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ Note that the generic value interpolator detects not only nested objects and arr

## Installing

If you use NPM, `npm install d3-interpolate`. Otherwise, download the [latest release](https://github.com/d3/d3-interpolate/releases/latest). You can also load directly from [d3js.org](https://d3js.org), either as a [standalone library](https://d3js.org/d3-interpolate.v0.5.min.js) or as part of [D3 4.0 alpha](https://github.com/mbostock/d3/tree/4). AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3_interpolate` global is exported:
If you use NPM, `npm install d3-interpolate`. Otherwise, download the [latest release](https://github.com/d3/d3-interpolate/releases/latest). You can also load directly from [d3js.org](https://d3js.org), either as a [standalone library](https://d3js.org/d3-interpolate.v0.6.min.js) or as part of [D3 4.0 alpha](https://github.com/mbostock/d3/tree/4). AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3_interpolate` global is exported:

```html
<script src="https://d3js.org/d3-color.v0.4.min.js"></script>
<script src="https://d3js.org/d3-interpolate.v0.5.min.js"></script>
<script src="https://d3js.org/d3-interpolate.v0.6.min.js"></script>
<script>
var interpolate = d3_interpolate.interpolateRgb("steelblue", "brown");
Expand Down Expand Up @@ -122,9 +122,13 @@ Object interpolation is particularly useful for *dataspace interpolation*, where

Note: **no defensive copy** of the template object is created; modifications of the returned object may adversely affect subsequent evaluation of the interpolator. No copy is made for performance reasons; interpolators are often part of the inner loop of [animated transitions](https://github.com/d3/d3-transition).

<a name="interpolateTransform" href="#interpolateTransform">#</a> d3.<b>interpolateTransform</b>(<i>a</i>, <i>b</i>)
<a name="interpolateTransformCss" href="#interpolateTransformCss">#</a> d3.<b>interpolateTransformCss</b>(<i>a</i>, <i>b</i>)

Returns an interpolator between the two 2D affine transforms represented by *a* and *b*. Each transform is decomposed to a standard representation of translate, rotate, *x*-skew and scale; these component transformations are then interpolated. This behavior is standardized by CSS: see [matrix decomposition for animation](http://www.w3.org/TR/css3-2d-transforms/#matrix-decomposition).
Returns an interpolator between the two 2D CSS transforms represented by *a* and *b*. Each transform is decomposed to a standard representation of translate, rotate, *x*-skew and scale; these component transformations are then interpolated. This behavior is standardized by CSS: see [matrix decomposition for animation](http://www.w3.org/TR/css3-2d-transforms/#matrix-decomposition).

<a name="interpolateTransformSvg" href="#interpolateTransformSvg">#</a> d3.<b>interpolateTransformSvg</b>(<i>a</i>, <i>b</i>)

Returns an interpolator between the two 2D SVG transforms represented by *a* and *b*. Each transform is decomposed to a standard representation of translate, rotate, *x*-skew and scale; these component transformations are then interpolated. This behavior is standardized by CSS: see [matrix decomposition for animation](http://www.w3.org/TR/css3-2d-transforms/#matrix-decomposition).

<a name="interpolateZoom" href="#interpolateZoom">#</a> d3.<b>interpolateZoom</b>(<i>a</i>, <i>b</i>)

Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export {default as interpolateNumber} from "./src/number";
export {default as interpolateObject} from "./src/object";
export {default as interpolateRound} from "./src/round";
export {default as interpolateString} from "./src/string";
export {default as interpolateTransform} from "./src/transform";
export {interpolateTransformCss, interpolateTransformSvg} from "./src/transform/index";
export {default as interpolateZoom} from "./src/zoom";
export {default as interpolateRgb} from "./src/rgb";
export {default as interpolateHsl} from "./src/hsl";
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"pretest": "mkdir -p build && node -e 'process.stdout.write(\"var version = \\\"\" + require(\"./package.json\").version + \"\\\"; export * from \\\"../index\\\"; export {version};\");' > build/bundle.js && rollup -f umd -g d3-color:d3_color -n d3_interpolate -o build/d3-interpolate.js -- build/bundle.js",
"test": "faucet `find test -name '*-test.js'` && eslint index.js src test",
"prepublish": "npm run test && uglifyjs build/d3-interpolate.js -c -m -o build/d3-interpolate.min.js && rm -f build/d3-interpolate.zip && zip -j build/d3-interpolate.zip -- LICENSE README.md build/d3-interpolate.js build/d3-interpolate.min.js",
"postpublish": "VERSION=`node -e 'console.log(require(\"./package.json\").version)'`; git push && git push --tags && cp build/d3-interpolate.js ../d3.github.com/d3-interpolate.v0.5.js && cp build/d3-interpolate.min.js ../d3.github.com/d3-interpolate.v0.5.min.js && cd ../d3.github.com && git add d3-interpolate.v0.5.js d3-interpolate.v0.5.min.js && git commit -m \"d3-interpolate ${VERSION}\" && git push"
"postpublish": "VERSION=`node -e 'console.log(require(\"./package.json\").version)'`; git push && git push --tags && cp build/d3-interpolate.js ../d3.github.com/d3-interpolate.v0.6.js && cp build/d3-interpolate.min.js ../d3.github.com/d3-interpolate.v0.6.min.js && cd ../d3.github.com && git add d3-interpolate.v0.6.js d3-interpolate.v0.6.min.js && git commit -m \"d3-interpolate ${VERSION}\" && git push"
},
"dependencies": {
"d3-color": "~0.4.0"
Expand Down
105 changes: 0 additions & 105 deletions src/transform.js

This file was deleted.

34 changes: 34 additions & 0 deletions src/transform/decompose.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var rad2deg = 180 / Math.PI;

export default function(matrix) {
var r0 = [matrix.a, matrix.b],
r1 = [matrix.c, matrix.d],
kx = normalize(r0),
kz = dot(r0, r1),
ky = normalize(combine(r1, r0, -kz)) || 0;

if (r0[0] * r1[1] < r1[0] * r0[1]) r0[0] *= -1, r0[1] *= -1, kx *= -1, kz *= -1;

return {
rotate: (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * rad2deg,
translate: [matrix.e, matrix.f],
scale: [kx, ky],
skew: ky ? Math.atan2(kz, ky) * rad2deg : 0
};
}

function dot(a, b) {
return a[0] * b[0] + a[1] * b[1];
}

function normalize(a) {
var k = Math.sqrt(dot(a, a));
if (k) a[0] /= k, a[1] /= k;
return k;
}

function combine(a, b, k) {
a[0] += k * b[0];
a[1] += k * b[1];
return a;
}
63 changes: 63 additions & 0 deletions src/transform/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import number from "../number";
import {parseCss, parseSvg} from "./parse";

function interpolateTransform(parse, pxComma, pxParen, degParen) {

function pop(s) {
return s.length ? s.pop() + " " : "";
}

function translate(ta, tb, s, q) {
if (ta[0] !== tb[0] || ta[1] !== tb[1]) {
var i = s.push("translate(", null, pxComma, null, pxParen);
q.push({i: i - 4, x: number(ta[0], tb[0])}, {i: i - 2, x: number(ta[1], tb[1])});
} else if (tb[0] || tb[1]) {
s.push("translate(" + tb[0] + pxComma + tb[1] + pxParen);
}
}

function rotate(ra, rb, s, q) {
if (ra !== rb) {
if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; // shortest path
q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: number(ra, rb)});
} else if (rb) {
s.push(pop(s) + "rotate(" + rb + degParen);
}
}

function skew(wa, wb, s, q) {
if (wa !== wb) {
q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: number(wa, wb)});
} else if (wb) {
s.push(pop(s) + "skewX(" + wb + degParen);
}
}

function scale(ka, kb, s, q) {
if (ka[0] !== kb[0] || ka[1] !== kb[1]) {
var i = s.push(pop(s) + "scale(", null, ",", null, ")");
q.push({i: i - 4, x: number(ka[0], kb[0])}, {i: i - 2, x: number(ka[1], kb[1])});
} else if (kb[0] !== 1 || kb[1] !== 1) {
s.push(pop(s) + "scale(" + kb + ")");
}
}

return function(a, b) {
var s = [], // string constants and placeholders
q = []; // number interpolators
a = parse(a), b = parse(b);
translate(a.translate, b.translate, s, q);
rotate(a.rotate, b.rotate, s, q);
skew(a.skew, b.skew, s, q);
scale(a.scale, b.scale, s, q);
a = b = null; // gc
return function(t) {
var i = -1, n = q.length, o;
while (++i < n) s[(o = q[i]).i] = o.x(t);
return s.join("");
};
};
}

export var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
export var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
24 changes: 24 additions & 0 deletions src/transform/parse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import decompose from "./decompose";

var identity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0},
cssNode,
cssRoot,
cssView,
svgNode;

export function parseCss(value) {
if (value === "none") return decompose(identity);
if (!cssNode) cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView;
cssNode.style.transform = value;
cssRoot.appendChild(cssNode);
value = cssView.getComputedStyle(cssNode, null).getPropertyValue("transform");
cssRoot.removeChild(cssNode);
var m = value.slice(7, -1).split(",");
return decompose({a: +m[0], b: +m[1], c: +m[2], d: +m[3], e: +m[4], f: +m[5]});
};

export function parseSvg(value) {
if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
svgNode.setAttribute("transform", value == null ? "" : value);
return decompose(svgNode.transform.baseVal.consolidate().matrix);
};

0 comments on commit be1eb45

Please sign in to comment.