How to Handle Unit Conversions in Anime.js Animations

Anime.js automatically normalizes CSS units through a three-stage pipeline—parsing, conversion, and application—using the convertValueUnit function in src/core/units.js to handle angles via mathematical lookup tables and lengths via temporary DOM measurements, with built-in caching to avoid redundant calculations.

Anime.js (juliangarnier/anime) eliminates the friction of mixing CSS units in animations by converting values internally before interpolation begins. Whether you are tweening from em to px, deg to turn, or querying computed styles in specific units, the library ensures numeric values share a common baseline. This article explains the technical implementation of unit handling based on the actual source code.

The Unit Conversion Pipeline

The conversion process follows a strict three-step architecture that separates concerns between value parsing, unit mathematics, and animation application.

Parsing Values with decomposeRawValue

Located in src/core/values.js, the decomposeRawValue function inspects raw CSS values and breaks them into structured objects. It detects numbers, units, relative operators (+=, -=), colors, and complex values, producing a decomposed object with the shape {t, n, u, …} where t represents the value type, n the numeric component, and u the unit string.

Converting Units via convertValueUnit

The core algorithm resides in src/core/units.js. The convertValueUnit function accepts an element, a decomposed value, a target unit, and an optional force flag. It returns a new decomposed value with the numeric part converted to the requested unit.

// src/core/units.js
export const convertValueUnit = (el, decomposedValue, unit, force = false) => {
  const currentUnit = decomposedValue.u;
  const currentNumber = decomposedValue.n;

  // Early exit if already in target unit
  if (decomposedValue.t === valueTypes.UNIT && currentUnit === unit) {
    return decomposedValue;
  }

  const cachedKey = currentNumber + currentUnit + unit;
  const cached = convertedValuesCache[cachedKey];
  if (!isUnd(cached) && !force) {
    decomposedValue.n = cached;
  } else {
    let convertedValue;
    // Angle units: deg, rad, turn
    if (currentUnit in angleUnitsMap) {
      convertedValue = currentNumber * angleUnitsMap[currentUnit] / angleUnitsMap[unit];
    } else {
      // Length units: px, em, rem, cm, etc.
      const baseline = 100;
      const tempEl = el.cloneNode();
      const parent = el.parentNode && (el.parentNode !== doc) ? el.parentNode : doc.body;
      parent.appendChild(tempEl);
      const style = tempEl.style;
      style.width = baseline + currentUnit;
      const curWidth = tempEl.offsetWidth || baseline;
      style.width = baseline + unit;
      const newWidth = tempEl.offsetWidth || baseline;
      const factor = curWidth / newWidth;
      parent.removeChild(tempEl);
      convertedValue = factor * currentNumber;
    }
    decomposedValue.n = convertedValue;
    convertedValuesCache[cachedKey] = convertedValue;
  }

  decomposedValue.t = valueTypes.UNIT;
  decomposedValue.u = unit;
  return decomposedValue;
}

Applying Conversions During Tween Creation

In src/animation/animation.js, the tween preparation logic ensures that from and to values share the same unit before interpolation begins. If the source value carries a unit different from the target, the engine invokes convertValueUnit to align them under a single _unit property.

How convertValueUnit Handles Different Unit Types

The function implements two distinct strategies based on the unit category.

Angle Conversion via Lookup Table

For angular units (deg, rad, turn), Anime.js uses a constant map: angleUnitsMap = { deg: 1, rad: 180/π, turn: 360 }. Converting between any two angle units requires only multiplication and division using these ratios, avoiding DOM manipulation entirely.

Length Conversion via DOM Measurement

For CSS length units (px, em, rem, cm, in, etc.), the function creates a hidden clone of the target element, sets its width to a baseline value (100) in the source unit, measures the pixel width via offsetWidth, then repeats the process in the target unit. The ratio of these two measurements yields the conversion factor. Results are memoized in convertedValuesCache using a key pattern of number + sourceUnit + targetUnit to prevent expensive reflows on subsequent calls.

Practical Implementation Examples

Querying CSS Properties in Specific Units

Use utils.get from src/utils/target.js to retrieve a computed style converted to any supported unit. The function automatically decomposes the original value and calls convertValueUnit when you supply a unit argument.

import { utils } from 'animejs';

// Element has width: 2em with font-size: 16px
const widthInPx = utils.get('#box', 'width', 'px');
// Returns: "32px"

Animating Between Angle Units

Anime.js normalizes angle values automatically, allowing you to mix units in keyframes.

anime({
  targets: '#rotor',
  rotate: ['360deg', '.5turn'], // 360deg converts to 1turn internally
  duration: 800,
  easing: 'linear'
});

Internally, 360deg converts to 1turn using the angleUnitsMap, ensuring the tween interpolates from 1.0 to 0.5 rather than attempting to animate between incompatible strings.

Manual Length Unit Conversion

Access convertValueUnit directly for programmatic unit math without animation.

import { convertValueUnit } from 'animejs/lib/utils/units.js';

const el = document.createElement('div');
document.body.appendChild(el);

const decomposed = { t: valueTypes.UNIT, n: 5, u: 'cm' };
const result = convertValueUnit(el, decomposed, 'px');

console.log(`${result.n}${result.u}`); // e.g., "188.976px"
document.body.removeChild(el);

Automatic Unit Inheritance

When you omit units in subsequent calls, Anime.js preserves the original unit from the element's inline style.

utils.set('#box', { width: '1em' }); // font-size: 20px
utils.set('#box', { width: 2 });     // Infers "em" unit
utils.get('#box', 'width', 'px');    // Returns "40px"

Summary

  • Parsing: decomposeRawValue in src/core/values.js splits CSS values into typed objects containing numeric and unit components.
  • Conversion: convertValueUnit in src/core/units.js handles angles via angleUnitsMap (mathematical ratios) and lengths via temporary DOM element measurement (baseline 100 technique).
  • Caching: Conversion results are stored in convertedValuesCache keyed by value, source unit, and target unit to minimize reflow costs.
  • Application: Tween creation in src/animation/animation.js automatically aligns units between from/to values, while utils.get in src/utils/target.js exposes unit conversion for property queries.

Frequently Asked Questions

How does Anime.js convert between angle units like deg and rad?

Anime.js uses a lookup table defined as angleUnitsMap = { deg: 1, rad: 180/π, turn: 360 }. When converting between angle units, the convertValueUnit function multiplies the source number by the source unit's map value and divides by the target unit's map value, producing mathematically accurate results without DOM manipulation.

Why does Anime.js create a temporary DOM element for length conversions?

CSS length units (em, rem, cm, in) are context-dependent based on font sizes or viewport settings. To obtain accurate conversion factors, Anime.js clones the target element, appends it to the DOM, sets a baseline width of 100 in both the source and target units, and measures the resulting pixel widths via offsetWidth. The ratio between these measurements provides the precise conversion factor for that specific rendering context.

Can I force a specific unit when querying a CSS property?

Yes. The utils.get function accepts a third argument specifying the desired unit. When provided, the function decomposes the computed style and passes it through convertValueUnit before returning the rounded string value, allowing you to retrieve dimensions in px even if the element is styled in em or rem.

Does Anime.js cache unit conversion results for performance?

Yes. The convertValueUnit function maintains a convertedValuesCache object that stores results using a composite key of number + sourceUnit + targetUnit. Before performing any DOM measurement or calculation, the function checks this cache, significantly reducing reflow costs when the same conversion is requested multiple times during an animation sequence.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →