Skip to content
On this page

Transform contexts

At its core, Mafs is just SVG with two contextual transforms. Those transforms correspond to two things:

  • The view transform, which maps from world space to pixel space.
  • The user transform, which is imposed by the Transform component.

The general approach is that, to render a point (x, y), you must first apply the user transform (because, well, the user is trying to move your component in some way), and then the view transform (so that it gets rendered by the SVG renderer in the right spot).

Mafs provides these transforms through two means:

  • The --mafs-view-transform and --mafs-user-transform CSS custom properties, which can be applied to an SVG element's style attribute.
  • The useInjectUserTransform hook return the userTransform matrix.
  • The useInjectViewTransform hook return the viewTransform matrix.

Components can mix and match these two approaches depending on needs. For example, the Text component transforms its anchor point in JavaScript, and doesn't apply any CSS transforms, because that would distort the text itself. On the other hand, the Ellipse component almost entirely relies on CSS transforms internally.

Accessing transforms in CSS

Here's an example of a custom component that uses the CSS transforms approach to render a delicious little PizzaSlice. The slice is wrapped in TransformWidget component so that you can try applying some user transforms it.

Code Slice
vue
<script setup lang="ts">
import PizzaSlice from './PizzaSlice.vue'
import { Mafs, Cartesian, TransformWidget } from 'mafs-vue'

</script>
<template>
    <Mafs :height="300" :viewBox="{ y: [-1, 1], x: [-3, 3] }">
        <Cartesian />
        <TransformWidget>
            <PizzaSlice />
        </TransformWidget>
    </Mafs>
</template>

This is an example of a component that gets entirely transformed by the user and view transforms. The pizza slice can end up totally distorted. For cases where you want to preserve the aspect ratio or pixel size of your component, you likely need to use the hooks approach.

Accessing transforms in JavaScript

Here's an example of a custom component that uses the hooks approach to render a grid of points. Because we want the grid's points to have a radius of 3 pixels (regardless of the viewport or any transforms), we use the useInjectUserTransform and useInjectViewTransform hook to get the user and view transforms and apply them to the circles' x and y coordinates, but not to their radius (which is in pixels). We also cannot use the CSS transforms approach here, because that would distort each circle.

Code
vue
<script setup lang="ts">
import PointCloud from "./PointCloud.vue"
import { Mafs, Cartesian, TransformWidget } from "mafs-vue"
</script>

<template>
    <Mafs :height="300" :viewBox="{ y: [-1, 5], x: [-1, 6] }">
        <Cartesian />
        <TransformWidget>
            <PointCloud />
        </TransformWidget>
    </Mafs>
</template>