Implement zoom functionality on a line chart using d3js

I am currently working on integrating a d3js V4 chart into an Angular 4 application. The chart is designed to show multiple sets of data as individual lines.

One issue I am facing is getting the zoom feature to function correctly, specifically on the X-axis only.

Within the zoomed() function, I have attempted various approaches with comments outlining why they are not successful.

createChart() {
    const element = this.chartContainer.nativeElement;
    this.width = element.offsetWidth - this.margin.left - this.margin.right;
    this.height = element.offsetHeight - this.margin.top - this.margin.bottom;

    // canvas
    const svg = d3.select(element).append('svg')
                .attr('width', element.offsetWidth)
                .attr('height', element.offsetHeight);

    // chart plot area
    this.chart = svg.append('g')
                .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');

    // domains
    const xDomain = [this.from, this.to];
    const yDomain = [0, 100];

    // scales
    this.xScale = d3.scaleTime().domain(xDomain).range([0, this.width]);
    this.yScale = d3.scaleLinear().domain(yDomain).range([this.height, 0]);

    // "data" function to draw lines
    this.valueline = d3.line<TimeValueObject>()
        .curve(d3.curveStepBefore)
        .x((d) => this.xScale(d.time))
        .y((d) => this.yScale(d.value));

    // X Axis
    this.xAxis = this.chart.append('g')
        .attr('class', 'xaxis')
        .attr('transform', 'translate(0,' + this.height + ')')
        .call(d3.axisBottom(this.xScale)
            .ticks(this.xTicks)
            .tickFormat(function (d) { return moment.utc(d).format(this.timeFormat); }));

    // Y Axis
    this.yAxis = this.chart.append('g')
        .attr('class', 'yaxis')
        .call(d3.axisLeft(this.yScale)
            .ticks(this.yTicks));

    // zoom
    const zoom = d3.zoom()
                .on('zoom', this.zoomed.bind(this));

    this.chart.call(zoom);
}

addData(myDataObject) {
    // Add a data valueline path
    this.chart.append('path')
        .attr('class', 'line')
        .attr('stroke', myDataObject.strokeStyle)
        .attr('id', myDataObject.name)
        .attr('d', this.valueline(myDataObject.data));
}

zoomed() {
   
    // Multiple attempts at implementing zoom functionality.
    
    // Based on examples from: 
    // <a href="https://github.com/d3/d3-zoom" rel="nofollow noreferrer">https://github.com/d3/d3-zoom</a>
}

The above code snippets reflect my efforts in aligning the zoom behavior with my specific requirements within the charting implementation.

Answer №1

After much troubleshooting, I have successfully found the solution.

I discovered that the d3 zoom event values are always relative to the initial zoom level, not the previous one.

To address this, it is necessary to adjust the scale by transforming the original scale:

const t = d3.event.transform;
this.xScale.domain(t.rescaleX(this.xScaleOriginal).domain());

→ I had to make a duplicate of the original scale


Next, the xAxis needs to be re-rendered:

this.xAxisElement.call(this.xAxis);

→ I needed to store the xAxis and xAxisElement in separate variables for clarity: xAxis for the result of d3.axisBottom() function and xAxisElement for the appended axis element


Finally, the data lines can now be redrawn:

for (let [key, value] of this.dataObjects) {
    this.chart.select('#' + key + '')
        .attr('d', this.valueline(value.data));
}

→ No changes were made to this section

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ensure that the variable is not 'undefined' and that it is a single, clean value

In my node backend project, I've encountered a situation with my TypeScript code where a variable occasionally prints as the string 'undefined' instead of just being undefined. Is there a more elegant way to check that the variable is not eq ...

Jest is having difficulty locating a module while working with Next.js, resulting in

I am encountering some difficulties trying to make jest work in my nextjs application. Whenever I use the script "jest", the execution fails and I get the following result: FAIL __tests__/index.test.tsx ● Test suite failed to run ...

Determine the closest whole number value lower than a floating point exponent

When given two floating point values zoomAmount and zoomFactor, the goal is to calculate a newZoomAmount that satisfies the condition: (newZoomAmount <= zoomAmount) && (newZoomAmount == pow( zoomFactor, i )) I could iterate through the values ...

Setting the current date in Angular using TypeScript and storing it in a MySQL database

As I delve into learning Angular, I am focused on practicing with a form. Specifically, I am attempting to include the current date when inputting client records along with their RFC, branch, and cost. Unfortunately, my attempts have been unsuccessful in i ...

Extending Mongoose's capabilities with header files for the "plugin" feature, utilizing the .methods and .statics methods

My task is to develop Typescript header files for a script that enhances my Mongoose model using the .plugin method. The current signature in the Mongoose header files looks like this: export class Schema { // ... plugin(plugin: (schema: Schema, opt ...

Struggling to effectively organize data routing within Angular? Let's tackle the challenges of

As a newcomer to Angular, I initially had success with CRUD operations without using routing. However, after implementing routing, I encountered an issue where the added values were not displaying in the content table on another page. It seems like there ...

Learn how to utilize React lazy effectively in components that utilize Redux compose without any similarities to type 'IntrinsicAttributes'

Here is the structure of a component that is exported with compose from redux. This component is called TestInspector.tsx export interface TestInspectorProps { closeInspector: () => void; onExpand: () => void; isFullScreen: boolean; selected ...

What steps can I take to ensure my CSS selector for the element is functioning properly?

Here is an example of some code: <html> <head> <style> .test{ background-color: red; p { background-color: yellow; } } </style> </head> <body> <div class="test"> ...

Upgrade from Next.js version 12

Greetings to all! I have recently been assigned the task of migrating a project from next.js version 12 to the latest version. The changes in page routing and app routing are posing some challenges for me as I attempt to migrate the entire website. Is ther ...

Angular loop is unable to detect changes in the updated list

My Angular application is facing a peculiar issue that I can't seem to figure out. // Here are the class attributes causing trouble tenants: any[] = []; @Input() onTenantListChange: EventEmitter<Tenant[]>; ngOnInit(): void { this. ...

Apply a border to the div that has been selected

I have a tool for storing information and I am using *ngFor to display each instance in a line. Is there a way to add a border when clicking on a line? The border should only appear on the clicked line, disappearing from the previous one if another line i ...

Creating interactive buttons within a card in Ionic 2

I created a card in Ionic 2 with the following structure. Upon clicking the card, it navigates to another page. <ion-content padding class="item item-text-wrap"> <ion-list> <ion-item *ngFor='let topic of topicList' text-wr ...

Issues encountered while transitioning from Angular 2 to 4 using npm

I am facing a challenge in upgrading Angular 2 to Angular 4 using npm. I have tried running "npm update --save" in the terminal, but unfortunately, my package.json does not reflect the latest Angular 4 version and remains unchanged. Can someone please ad ...

What is the correct way to set up Typescript for usage with Angular 2 in Visual Studio 2015?

I recently created an Angular 2 app using Visual Studio 2015 and TypeScript. I made sure to install TypeScript globally using the npm command "npm install -g [email protected]." However, when I try to build the project, I encounter several errors re ...

Testing Angular 16 Component with Jasmine Spy and callFake Strategy

I've encountered an issue while trying to test my component unit. The problem arises when I call the product-list.component.ts from my product.service.ts. While my product.service.spec.ts is successful, the product-list.component.spec.ts fails as the ...

Is there a way to invoke an Angular2 function from within a Google Map infowindow?

I am currently working on integrating Google Maps using javascript in a project, and I'm facing a challenge. I want to call an Angular2 function inside an infowindow as shown in the code snippet below. Pay attention to the infoContent variable that co ...

Lack of MaterialUI Table props causing issues in Storybook

Currently, I am utilizing MaterialUI with some modifications to create a personalized library. My tool of choice for documentation is Storybook, using Typescript. An issue I have encountered is that the storybook table props are not consistently auto-gene ...

Issue with building Webpack React Router DOM configuration

I recently developed a React+Typescript app with Webpack 5 configuration completely from scratch. Everything was running smoothly in development mode, and I utilized React Router DOM version 6.23.1 for routing. However, once I built the app, some component ...

Capture a snapshot of a webpage that includes an embedded iframe

Currently, we have a nodeJS/angular 4 website that contains an iframe from a third party (powerBI Emebdded). Our goal is to develop a feature that allows the end user to capture a screenshot of the entire page, including the content within the iframe. We ...

Preventing the dropdown options from appearing in Angular when clearing the input field

In a reusable component, I implemented some simple logic to clear the value of a select input when the 'x' button is clicked. select.component.ts @Input() selectFormControlName: string; @Input() selectAbstractControl: AbstractControl; ... ...