React

by XeniaSiskaki

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<div id="app"></div>

CSS

svg {
  font: 10px sans-serif;
}

button {
  width: 120px;
  margin: 0 15px;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000000;
  shape-rendering: crispEdges;
}

.axis path {
  display: none;
}

.tick line {
  stroke: rgba(220, 220, 220, 0.6);
}

React

const zoom = ['x', 'y'];
const range = [-10, 10];
const sampleDate = new Date("2020-01-01T09:00:00.000Z").getTime();
const innerWidth = 390;
const innerHeight = 278;

const chartData = {
	a: [{v:0, t:sampleDate-10000},{v:-1, t: sampleDate-9000},{v:0.6, t: sampleDate-8000},{v:1, t: sampleDate-7000},{v:-2, t: sampleDate-6000},{v:10, t: sampleDate-5000},{v:0.8, t: sampleDate-4000},{v:5.3, t: sampleDate-3000},{v:0, t: sampleDate-2000},{v:1.2, t: sampleDate-1000},{v:3, t: sampleDate},{v:4.5, t: sampleDate+1000},{v:10, t: sampleDate+2000}],
  b: [{v:0.8, t:sampleDate-10000},{v:11, t: sampleDate-9000},{v:-7.9, t: sampleDate-8000},{v:22.1, t: sampleDate-7000},{v:-0.3, t: sampleDate-6000},{v:10, t: sampleDate-5000},{v:1.3, t: sampleDate-4000},{v:4.3, t: sampleDate-3000},{v:0, t: sampleDate-2000},{v:-14.4, t: sampleDate-1000},{v:3, t: sampleDate},{v:-0.1, t: sampleDate+1000},{v:10, t: sampleDate+2000}]
};
const allPoints = chartData.a.concat(chartData.b);

class Chart extends React.Component {
  constructor(props) {
    super(props);
    
    const [x0, x1] = d3.extent(allPoints.map(({t}) => t));
    
    this.state = {
    	zoom: 1,
      viewport: { x0, x1 }
    };
    
    this.zoomPane = React.createRef();
    this.svg = React.createRef();
    
    this. line = d3.svg
			.line()
  		.interpolate('linear')
 			.x(({t}) => this.xScale(t))
  		.y(({v}) => this.configScale(this.yScale(v)));
  }
  
  render() {
    return (
      <div>
        <div>
          <button onClick={this.handleZoomChange.bind(this)}>Set zoom to {zoom[this.getNextZoomIndex()]}</button>
          <button onClick={this.resetViewport.bind(this)}>Reset viewport</button>
        </div>
        <div style={{ height: 300 }}>
          <svg width={450} height={300} ref={this.svg}>
            <rect ref={this.zoomPane} style={{cursor: 'move'}} width={innerWidth} height={innerHeight} fill='transparent' transform='translate(55,0)'></rect>
            <defs>
              <clipPath id={'clipId'}>
             ...