DEV Community

Simon László
Simon László

Posted on

Customizing Vizzu Charts - Mouse-wheel zooming

Adding mouse-wheel zooming to a Vizzu chart in JavaScript can be useful for users who want to zoom in and out of a dense chart. In this how-to, we will create a Zoomer class that will handle the zooming functionality and then add event listeners to the Vizzu chart to connect the events to our Zoomer instance. And then, we will improve its performance by adding throttle to the zoom event calls.

Here are the steps to add mouse-wheel zooming over a Vizzu chart in JavaScript:

Step 1: Import Vizzu and data

First, we need to import the Vizzu library and an example data set as well, which we will visualize:

import Vizzu from 'https://cdn.jsdelivr.net/npm/vizzu@latest/dist/vizzu.min.js';
import { data } from 'https://lib.vizzuhq.com/0.7/assets/data/chart_types_eu.js'  
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the chart

We can create the chart using the following code:

let chart = await (new Vizzu('vizzu-container', { data })).initializing;
Enter fullscreen mode Exit fullscreen mode

This creates a new Vizzu chart instance with the provided data.

Step 3: Initialize the Chart with Zoom Range

Next, we create a line chart with an initial x-axis range.

chart.animate({
  config: {
    x: {
      set: 'Year',
      range: {
        min: 0.5,
        max: 20.5
      }
    },
    y: 'Value 5 (+/-)',
    geometry: 'line'
  }
}, 0);
Enter fullscreen mode Exit fullscreen mode

Now that we have a static chart, we can add the zoom functionality.

Step 4: Create the Zoomer class and instance

The Zoomer class will be used to track the zoom level of the chart:

class Zoomer {
  constructor(min, max) {
    this.min = min;
    this.max = max;
    this.finalMin = min;
    this.finalMax = max;
    this.pos = null;
  }

  trackPos(pos) {
    this.pos = pos;
  }

  zoom(factor) {
    let ref = this.min + this.pos * (this.max - this.min);
    this.min = ref - (1 + factor) * (ref - this.min);
    this.max = ref + (1 + factor) * (this.max - ref);
    if (this.min < this.finalMin) this.min = this.finalMin;
    if (this.max > this.finalMax) this.max = this.finalMax;
  }
};
Enter fullscreen mode Exit fullscreen mode

The constructor takes the initial minimum and maximum value of the x-axis range. The trackPos method is used to track the mouse position, and the zoom method is used to zoom in or out based on the mouse wheel delta.

Now, we can create a new Zoomer instance with an initial zoom range of 0.5 to 20.5:

let zoomer = new Zoomer(0.5, 20.5);
Enter fullscreen mode Exit fullscreen mode

Step 5: Add event listeners to the chart

We need to add event listeners to the chart to detect mouse-wheel and mouse-move events and prevent the default scroll event on the container element:

let container = document.getElementById('vizzu-container');

container.addEventListener('wheel', event => {
  event.preventDefault();
})

chart.on('wheel', event => {
  // Zoom event handling
});

chart.on('mousemove', event => {
  // Mouse move event handling
});
Enter fullscreen mode Exit fullscreen mode

Step 6: Handle the Zoom Event

When a zoom event is triggered, we update the zoom level using the Zoomer object and trigger an animation to update the chart with the new zoom range.

chart.on('wheel', event => {
  zoomer.zoom(- event.data.delta / 200);
  chart.animate(
    { x: { range: {
      min: zoomer.min,
      max: zoomer.max
    } } },
    { duration: '50ms', easing: 'linear' });
});
Enter fullscreen mode Exit fullscreen mode

Step 7: Handle the Mouse Move Event

When a mouse move event is triggered, we update the position of the Zoomer object to reflect the current mouse position.

chart.on('mousemove', event => {
  zoomer.trackPos(event.data.coords.x);
});
Enter fullscreen mode Exit fullscreen mode

With these steps in place, your Vizzu chart should now be able to handle mouse-wheel zooming events. However, you will notice that zooming events can be called before the chart animation can finish, delaying the zoom effect. To prevent this behavior, we will need to throttle the zoom call.

Step 8: Create the Throttle class

The Throttle class will be used to limit the number of zoom events that are processed at any given time.

class Throttle {
  constructor() {
    this.finished = true;
    this.next = null;
  }
  call(func) {
    if (!this.finished) {
      this.next = func;
      return;
    }
    else {
      this.finished = false;
      func().then(() => {
        this.finished = true;
        if (this.next !== null) {
          let f = this.next;
          this.next = null;
          this.call(f);
        }
      })
    }
  }
}

let throttle = new Throttle();
Enter fullscreen mode Exit fullscreen mode

The call method takes a promise-returning function as an argument and calls it if the promise returned by the previous function has resolved. Otherwise, it stores the function and waits for the current function's promise to finish.

Step 9: Adding the throttle to the Zoom Event

We can now modify the zoom event to call the zoom method via our new throttle instance:

chart.on('wheel', event => {
  zoomer.zoom(- event.data.delta / 200);
  throttle.call(() =>
    chart.animate(
      { x: { range: {
        min: zoomer.min,
        max: zoomer.max
      } } },
      { duration: '50ms', easing: 'linear' })
  );
});
Enter fullscreen mode Exit fullscreen mode

With these additional steps, your Vizzu chart can handle mouse-wheel zooming events flawlessly.

Working example:

Sentry blog image

Identify what makes your TTFB high so you can fix it

In the past few years in the web dev world, we’ve seen a significant push towards rendering our websites on the server. Doing so is better for SEO and performs better on low-powered devices, but one thing we had to sacrifice is TTFB.

Read more

Top comments (0)

Cloudinary image

Optimize, customize, deliver, manage and analyze your images.

Remove background in all your web images at the same time, use outpainting to expand images with matching content, remove objects via open-set object detection and fill, recolor, crop, resize... Discover these and hundreds more ways to manage your web images and videos on a scale.

Learn more

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay