Look at Time Updates: A more efficient zoom mechanism

Valerian
3 min readJul 24, 2022

Simplify the zoom mechanism

While I was chasing an issue with jumping elements when the view port was repositioned, I discovered a more efficient way of calculating the new position of an element when zooming in and out.

Zooming in and out requires calculations for every element

The benefits

I would not consider it a bottleneck of the same magnitude as the infamous application of CSS transformations through Vue props that I discuss in another post, but this change reduces the number of arithmetic operations per adjustment for each element. With dozens of elements on the screen every millisecond spared is welcome! ⏳

But of much greater value are two other benefits:

  1. It simplifies the code.
  2. It prevents elements from drifting away from their date over time.

The key to the solution is the position translator.

Leveraging the position translator

Very early in development, I introduced the position translator. It translates relative positions (dates like the year 1516) into absolute positions and vice versa. I used the position translator to place newly created time events and time markers at the right place on the screen. It has a simple and straight-forward logic:

A: absolute position (screen pixels)

R: relative position (a date like 1516)

ZL: the current zoom level

A0: the absolute position of the date 0 (short: timeline zero)

A = R * ZL + A0

R = (A — A0) / ZL

This same logic can be applied to the zoom mechanism. For each element the zoomer calculates the new absolute position by dropping the elements date into the position translator.

Previously, the zoomer calculated the new position of a time event or marker by taking its old position, the mouse pointer position and the zoom factor. Take a look:

for (let i = 0; i < timeEvents.length; i++) {  const distance =  timeEvents[i].positionCenter - referencePosition) * zoomFactor;  const newPosition = referencePosition + distance;  timeEvents[i].positionCenter = newPosition;}

As we calculate the timeline zero on every zoom anyway, we can leave all those parameters aside and just call the position translator to get a new absolute position.

for (let i = 0; i < timeEvents.length; i++) {  const newPosition =  PositionTranslator.toAbsolutePosition(timeEvents[i].date);}

Removing position drift

Whilst zooming around in our timeline the application performs a lot of calculations with fractional numbers. As these numbers are represented as decimals in JavaScript we lose precision over time. The absolute position of an element will no longer correspond to its actual date. E.g. an element of with the date 1516 might have an absolute position that, if we translated it back, would correspond to the date 1515.999.

The drift is microscopic, but I think it would add up to significant amounts during longer session… Hold on! Don’t we suffer from precision loss with the position translator, too? Yes we are, but now we have the same precision loss every element.

Using the original zoom mechanism, we would have gotten an individual precision loss per element. The new zoom mechanism derives each element’s position from the same point: timeline zero. Timeline zero will drift over time, but at least all elements will have the same drift.

Summary

The calculation got faster, we have better re-usage of our code and we prevent drift.

--

--