Skip to content

Commit

Permalink
[zoom] start @vx/zoom
Browse files Browse the repository at this point in the history
  • Loading branch information
hshoff committed Aug 22, 2017
1 parent a3156cc commit ff8dd86
Show file tree
Hide file tree
Showing 14 changed files with 344 additions and 50 deletions.
11 changes: 9 additions & 2 deletions packages/vx-demo/package.json
Expand Up @@ -9,7 +9,11 @@
"start": "next start",
"test": "jest"
},
"keywords": ["vx", "demo", "examples"],
"keywords": [
"vx",
"demo",
"examples"
],
"author": "@hshoff",
"license": "MIT",
"dependencies": {
Expand Down Expand Up @@ -39,6 +43,7 @@
"@vx/text": "0.0.136",
"@vx/tooltip": "0.0.137",
"@vx/voronoi": "0.0.136",
"@vx/zoom": "0.0.136",
"classnames": "^2.2.5",
"d3-array": "^1.1.1",
"d3-format": "^1.2.0",
Expand All @@ -59,7 +64,9 @@
"topojson-client": "^3.0.0"
},
"browserify": {
"transform": ["babelify"]
"transform": [
"babelify"
]
},
"publishConfig": {
"access": "public"
Expand Down
158 changes: 118 additions & 40 deletions packages/vx-demo/pages/zoom.js
@@ -1,43 +1,121 @@
import React from 'react';
// import { genPhyllotaxis } from '@vx/mock-data';

function genPhyllotaxis({ radius, width, height }) {
const theta = Math.PI * (3 - Math.sqrt(5));
return function(i) {
const r = radius * Math.sqrt(i);
const a = theta * i;
return [width / 2 + r * Math.cos(a), height / 2 + r * Math.sin(a)];
};
}
import { genPhyllotaxis } from '@vx/mock-data';
import { localPoint } from '@vx/event';
import { ZoomTransform, zoomIdentity } from '@vx/zoom';
import { Point } from '@vx/point';

const width = 960;
const height = 500;
const gen = genPhyllotaxis({ radius: 10, width, height });
const data = [...Array(2000)].map((d, i) => gen(i));
const center = new Point({ x: width / 2, y: height / 2 });
const focus = data[0];

export default class Zoom extends React.Component {
constructor(props) {
super(props);
this.reset = this.reset.bind(this);
this.increase = this.increase.bind(this);
this.decrease = this.decrease.bind(this);
this.handleWheel = this.handleWheel.bind(this);

this.start = zoomIdentity
.translate(center.value())
.scale(0.5)
.translate({ x: -focus.x, y: -focus.y });

this.state = { transform: this.start };
}

reset() {
this.setState({ transform: this.start });
}

increase() {
const { transform } = this.state;
this.setState({
transform: transform
.translate(center.value())
.scale(1.5)
.translate({ x: -focus.x, y: -focus.y }),
});
}

decrease() {
const { transform } = this.state;
this.setState({
transform: transform
.translate(center.value())
.scale(0.5)
.translate({ x: -focus.x, y: -focus.y }),
});
}

handleWheel(event) {
const { transform } = this.state;
const increase = event.deltaY > 0;
const scaleBy = increase ? 1.1 : 0.9;

this.setState({
transform: transform
.translate(center.value())
.scale(scaleBy)
.translate({ x: -focus.x, y: -focus.y }),
});
}

render() {
return (
<div className="Zoom">
<div>
<button onClick={this.reset}>Reset</button>
<button onClick={this.decrease}>-</button>
<button onClick={this.increase}>+</button>
</div>
<svg width={width} height={height} ref={s => (this.svg = s)}>
<rect width={width} height={height} fill="#fdf6e6" />
<g transform={`${this.state.transform.toString()}`}>
{data.map((d, i) => {
const { x, y } = d;
return (
<circle
key={`dot-${i}`}
cx={x}
cy={y}
r={4}
fill={i % 2 === 0 ? '#ffcc59' : '#f6b6b6'}
/>
);
})}
</g>
<rect
width={width}
height={height}
fill="transparent"
onWheel={this.handleWheel}
onDoubleClick={this.increase}
/>
</svg>
<div>
scale: {`${this.state.transform.k}`}
</div>

<style jsx>{`
.Zoom {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 98vh;
font-family: sans-serif;
user-select: none;
}
export default function Zoom() {
const width = 960;
const height = 500;
const gen = genPhyllotaxis({ radius: 10, width, height });
const data = [...Array(2000)].map((d, i) => gen(i));

return (
<div className="Zoom">
<svg width={width} height={height}>
<rect
width={width}
height={height}
stroke="black"
strokeWidth={1}
fill="transparent"
/>
{data.map((d, i) => {
return <circle key={`dot-${i}`} cx={d[0]} cy={d[1]} r={2.5} />;
})}
</svg>
<style jsx>{`
.Zoom {
display: flex;
justify-content: center;
align-items: center;
height: 98vh;
}
`}</style>
</div>
);
svg {
margin: 1rem 0;
}
`}</style>
</div>
);
}
}
1 change: 1 addition & 0 deletions packages/vx-mock-data/package.json
Expand Up @@ -39,6 +39,7 @@
"access": "public"
},
"dependencies": {
"@vx/point": "0.0.136",
"d3-random": "^1.0.3"
}
}
7 changes: 6 additions & 1 deletion packages/vx-mock-data/src/generators/genPhyllotaxis.js
@@ -1,8 +1,13 @@
import Point from '@vx/point/build/Point';

export default function genPhyllotaxis({ radius, width, height }) {
const theta = Math.PI * (3 - Math.sqrt(5));
return function(i) {
const r = radius * Math.sqrt(i);
const a = theta * i;
return [width / 2 + r * Math.cos(a), height / 2 + r * Math.sin(a)];
return new Point({
x: width / 2 + r * Math.cos(a),
y: height / 2 + r * Math.sin(a),
});
};
}
3 changes: 3 additions & 0 deletions packages/vx-zoom/package.json
Expand Up @@ -39,5 +39,8 @@
"react-fatigue-dev": "github:tj/react-fatigue-dev",
"react-tools": "^0.10.0",
"regenerator-runtime": "^0.10.5"
},
"dependencies": {
"@vx/point": "0.0.136"
}
}
1 change: 0 additions & 1 deletion packages/vx-zoom/src/Zoom.js

This file was deleted.

Empty file.
4 changes: 3 additions & 1 deletion packages/vx-zoom/src/index.js
@@ -1 +1,3 @@
export { default as Zoom } from './Zoom';
export { default as ZoomTransform } from './zoom/ZoomTransform';
export { default as withZoom } from './enhancers/withZoom';
export { default as zoomIdentity } from './util/zoomIdentity';
6 changes: 6 additions & 0 deletions packages/vx-zoom/src/util/zoomIdentity.js
@@ -0,0 +1,6 @@
import ZoomTransform from '../zoom/ZoomTransform';
export default new ZoomTransform({
k: 1,
x: 0,
y: 0,
});
20 changes: 20 additions & 0 deletions packages/vx-zoom/src/zoom/Zoom.js
@@ -0,0 +1,20 @@
import zoomIdentity from '../util/zoomIdenity';

class Zoom {
constructor({ transform, extent }) {
this.transform = transform || zoomIdenity;
this.extent = extent || [0, Infinity];
}

constrain(transform, extent) {
const e = extent || this.extent;
}

scaleBy(k) {
this.transform.scale(k);
}

toString() {
return `${this.transform}`;
}
}
53 changes: 53 additions & 0 deletions packages/vx-zoom/src/zoom/ZoomTransform.js
@@ -0,0 +1,53 @@
import Point from '@vx/point/build/Point';

export default class ZoomTransform {
constructor({ k = 1, x = 0, y = 0 }) {
this.k = k;
this.x = x;
this.y = y;
}

scale(k) {
return new ZoomTransform({
k: this.k * k,
x: this.x,
y: this.y,
});
}

translate({ x, y }) {
return new ZoomTransform({
k: this.k,
x: this.x + this.k * x,
y: this.y + this.k * y,
});
}

apply({ x, y }) {
return new Point({ x: this.applyX(x), y: this.applyY(y) });
}

applyX(x) {
return x * this.k + this.x;
}

applyY(y) {
return y * this.k + this.y;
}

invert({ x, y }) {
return new Point({ x: this.invertX(x), y: this.invertY(y) });
}

invertX(x) {
return (x - this.x) / this.k;
}

invertY(y) {
return (y - this.y) / this.k;
}

toString() {
return `translate(${this.x}, ${this.y}) scale(${this.k})`;
}
}
24 changes: 19 additions & 5 deletions packages/vx-zoom/test/Zoom.test.js
@@ -1,7 +1,21 @@
import { Zoom } from '../src';
import { withZoom, ZoomTransform, zoomIdentity } from '../src';

describe('Zoom', () => {
test('it should be defined', () => {
expect(Zoom).toBeDefined()
})
})
describe('withZoom()', () => {
test('it should be defined', () => {
expect(withZoom).toBeDefined();
});
});

describe('ZoomTransform', () => {
test('it should be defined', () => {
expect(ZoomTransform).toBeDefined();
});
});

describe('zoomIdentity', () => {
test('it should be defined', () => {
expect(zoomIdentity).toBeDefined();
});
});
});

0 comments on commit ff8dd86

Please sign in to comment.