I'm trying to zoom my timeline by x axis, but it doesn't show any reaction on zooming, I've looked through many tutorials, tried separate zoom functions, eventually here what I've got (with this https://coderwall.com/p/psogia/simplest-way-to-add-zoom-pan-on-d3-js manual):
const { dataset } = this.props;
var minDt = 0;
var maxDt = 300;
var intervalMinWidth = 1;
var elementWidth = 1200; // TODO: make adoptable
var elementHeight = 200;
var width = 500;
var height = 600;
var groupHeight = 20;
var xTickSize = 5;
var x = d3.scaleLinear()
.domain([minDt, maxDt])
.range([0, width]);
var xAxis = d3.axisBottom(x)
.tickSize(xTickSize);
var svg = d3.select(svgRoot)
.append('svg')
.attr('id', 'chart')
.attr('class', 'timeline-chart')
.attr("width", "100%")
.attr("height", "100%")
.call(d3.zoom().on("zoom", function () {
svg.attr("transform", d3.event.transform)
}))
.append('g');
var gX = svg.append("g")
.attr("class", "axis axis--x")
.call(xAxis);
var groupExonItems = svg
.selectAll('.group-interval-item')
.data(dataset)
.enter()
.append('g')
.attr('clip-path', 'url(#chart-content)')
.attr('class', 'item')
.attr('transform', function (d, i) {
return 'translate(0, ' + groupHeight * i + ')';
})
.selectAll('.rect').data(function (d) {
return d.data.filter(function (_) {
return _.type === 'exon';
});
}).enter();
var exonBarHeight = 0.8 * groupHeight;
var exonBarMargin = 30;
var exons = groupExonItems
.append('rect')
.attr('class', 'interval')
.attr('width', function (d) {
return Math.max(intervalMinWidth, x(d.to) - x(d.from));
})
.attr('height', exonBarHeight)
.attr('y', exonBarMargin)
.attr('x', function (d) {
return x(d.from);
});
I have tried separate function, not any progress as well :
function zoomFunction(){
var new_xScale = d3.event.transform.rescaleX(x)
gX.call(xAxis.scale(new_xScale));
svg.selectAll('rect.interval').attr('x', function (d) {
return x(d.from);
}).attr('width', function (d) {
return Math.max(intervalMinWidth, x(d.to) - x(d.from));
});
};
If I log zoom function - I can see it's reachable, and scale changes, but not for my rectangles. May be the reason is I'm using react + faux to draw svg, I have tried without faux - by putting D3 code into the componentDidMount function - still no any progress. What am I missing? Appreciate any help.
Even react-faux-dom is the simplest way to render D3, it breaks dynamic changes of the object, such as zoom. And without faux here is a very simple example to implement zoom: https://swizec.com/blog/two-ways-build-zoomable-dataviz-component-d3-zoom-react/swizec/7753
As far as I need only x-axis zoom, here is solution:
import { Component} from 'react'
import { PropTypes} from 'prop-types'
import React from 'react'
import ReactDOM from 'react-dom';
import ReactFauxDOM from 'react-faux-dom'
import * as d3 from 'd3'
import styles from './chart.css';
const random = d3.randomNormal(2, 1);
class Scatterplot extends React.Component {
constructor(props) {
super(props);
this.updateD3(props);
}
componentWillUpdate(nextProps) {
this.updateD3(nextProps);
}
updateD3(props) {
const { data, width, height, zoomTransform } = props;
this.xScale = d3.scaleLinear()
.domain([0, d3.max(data, ([x, y]) => x)])
.range([0, width]);
}
get transform() {
const { x, y, zoomTransform } = this.props;
let transform = "";
if (zoomTransform) {
transform = `translate(${x + zoomTransform.x}, ${y}) scale(${zoomTransform.k} 1)`;
}
return transform;
}
render() {
const { data } = this.props;
return (
<g transform={this.transform} ref="scatterplot">
{data.map(([x, y]) => <rect x={this.xScale(x)} y={y} width={this.xScale(4)} height = {5} />)}
</g>
)
}
}
class Chart extends React.Component {
constructor(props) {
super(props);
this.state = {
data: d3.range(5).map(_ => [random(), random()]),
zoomTransform: null
}
this.zoom = d3.zoom()
.scaleExtent([-5, 5])
.translateExtent([[-100, -100], [props.width+100, props.height]])
.extent([[-100, -100], [props.width+100, props.height]])
.on("zoom", this.zoomed.bind(this))
}
componentDidMount() {
d3.select(this.refs.svg)
.call(this.zoom)
}
componentDidUpdate() {
d3.select(this.refs.svg)
.call(this.zoom)
}
zoomed() {
this.setState({
zoomTransform: d3.event.transform
});
}
render() {
const { zoomTransform } = this.state,
{ width, height } = this.props;
return (
<svg width={width} height={height} ref="svg">
<Scatterplot data={this.state.data}
x={0} y={0}
width={width/2}
height={height}
zoomTransform={zoomTransform}/>
</svg>
)
}
}
export default Chart