Skip to content
eloxi
v0.12.0

Introduction to Veloxi

Veloxi is a UI engine for creating smooth interactions in the browser. It’s called an engine because:

  • It abstracts away the low-level details of how the browser reads and updates the DOM.
  • It works directly with the DOM, so it’s framework-agnostic.
  • It is not opinionated on how you use it to implement interactions.

🌟 Check out the showcase to see what you can build with it.

Main Components

A Veloxi app consists of three main parts: the app runner, plugins, and views.

On each page, you will have a single app object running all the interactions on that page. Each interaction code will be written in a plugin, and a plugin will have access to the views assigned to it.

So the relationship between these components is one-to-many: one app can contain multiple plugins, and one plugin can contain multiple views. This leads us to an important note: each view can be owned only by a single plugin.

This design decision is critical to ensure that there are no conflicts between plugins when manipulating views.

Does It Work on Server-Side Rendered Applications (SSR)?

Yes, but you should create and run your Veloxi app instance only after your DOM is created and loaded.

For example, if you are using SvelteKit, you need to wrap your app instance in onMount().

onMount(() => {
const app = createApp()
app.addPlugin(MyPlugin)
app.run()
})

The same applies to other frameworks.

Your First Plugin

This example will provide a brief overview of how to use Veloxi. In this example, we will build a ‘follow-the-mouse’ interaction.

This example has a single element for the circle.

<div
class="circle"
data-vel-view="circle"
data-vel-plugin="FollowMousePlugin"
></div>

This element will be accessed as a view within our plugin. The view’s name is defined using the data-vel-view attribute, which will be circle. This view will be assigned to our plugin, which we’ll call FollowMousePlugin; we’ll use the data-vel-plugin data attribute for this purpose.

To make the element look like a circle, we need to add this CSS:

.circle {
width: 50px;
height: 50px;
background: whitesmoke;
border-radius: 50%;
will-change: transform;
}

Now, we come to our plugin definition.

API Style:
Functions
Classes
import { createApp, Events } from 'veloxi'
const FollowMousePlugin = (context) => {
let circle
context.setup(() => {
// Fetch the circle view
circle = context.getView('circle')
// Use the spring animator on the position property of the view
circle.position.animator.set('spring')
})
// Subscribe to global events here
context.subscribeToEvents((eventBus) => {
// Subscribe to the PointerMoveEvent global event
eventBus.subscribeToEvent(Events.PointerMoveEvent, (event) => {
// When the mouse moves, update the position of the circle
circle.position.set({
x: event.x + circle.getScroll().x - circle.size.width / 2,
y: event.y + circle.getScroll().y - circle.size.height / 2
})
})
})
}
// Specify the name of the plugin
FollowMousePlugin.pluginName = 'FollowMousePlugin'
// Create a new Veloxi app
const app = createApp()
// Add the FollowMousePlugin to it
app.addPlugin(FollowMousePlugin)
// And finally run the app
app.run()
import { createApp, Events, Plugin } from 'veloxi'
class FollowMousePlugin extends Plugin {
// Specify the name of the plugin
static pluginName = 'FollowMousePlugin'
circle
setup() {
// Fetch the circle view
circle = context.getView('circle')
// Use the spring animator on the position property of the view
circle.position.animator.set('spring')
}
// Subscribe to global events here
subscribeToEvents(eventBus) {
// Subscribe to the PointerMoveEvent global event
eventBus.subscribeToEvent(Events.PointerMoveEvent, (event) => {
// When the mouse moves, update the position of the circle
circle.position.set({
x: event.x + circle.getScroll().x - circle.size.width / 2,
y: event.y + circle.getScroll().y - circle.size.height / 2
})
})
}
}
// Create a new Veloxi app
const app = createApp()
// Add the FollowMousePlugin to it
app.addPlugin(FollowMousePlugin)
// And finally run the app
app.run()

Though this example may seem simple, it contains important parts we should look at one by one.

In the upcoming sections of this guide, we’ll cover:

  • How to work with single and multiple views.
  • Applying a plugin to a specific view (using scoped plugins).
  • Changing view properties like position, size, and rotation.
  • Adding animations to view properties.
  • Sending data to plugins.
  • Understanding global events vs. plugin events.
  • Using EventPlugins.