trackball
Version:
The RAW library to add a virtual Trackball to your DOM
227 lines (165 loc) • 6.34 kB
Markdown
# Trackball.js - 3D Trackballs for your Website
[](https://npmjs.org/package/trackball "View this project on npm")
[](http://opensource.org/licenses/MIT)
Trackball.js is a small JavaScript library on top of [Quaternion.js](https://github.com/rawify/Quaternion.js) that enables natural, free 3D rotation of DOM elements via CSS transforms. The library **does not style** your DOM – it just handles user input and gives you a quaternion that you apply to your object.
<p align="center">
<img src="https://github.com/rawify/Trackball.js/blob/main/res/cube.png?raw=true" width="300">
</p>
If you are interested in the math behind this library or want to see a demo, take a look at the [derivation of the trackball](https://raw.org/article/trackball-rotation-using-quaternions/).
## Example
The usage of Trackball.js is pretty straightforward. Create a scene that should handle the input and put a object with 3D transforms in it:
```html
<div id="scene">
<div id="object">
<div class="side"></div>
<!-- ... -->
</div>
</div>
```
Basic styles:
```html
<style>
#scene {
width: 400px;
height: 400px;
touch-action: none; /* important for touch */
perspective: 500px;
position: relative;
}
#object {
width: 200px;
height: 200px;
margin: 100px;
transform-style: preserve-3d;
box-sizing: border-box;
}
.side {
width: 200px;
height: 200px;
position: absolute;
pointer-events: none;
box-sizing: border-box;
}
</style>
```
Usage:
```html
<script src="/path/to/quaternion.min.js"></script>
<script src="/path/to/trackball.min.js"></script>
<script>
const obj = document.getElementById('object');
const tb = new Trackball({
scene: '#scene',
q: new Quaternion(), // initial rotation
inertia: true, // enable inertia (defaults shown below)
limitAxis: null, // 'x' | 'y' | null
onDraw(q) {
// apply q (Quaternion) to your view matrix / object here
// e.g., shader uniform from q.toMatrix4() if your lib supports it
obj.style.transform = q.toCSSTransform();
}
});
</script>
```
Limit interaction to a **bounding box** inside the scene:
```js
const bbox = document.getElementById('object');
const tb = new Trackball({
scene: '#scene',
activeArea: () => bbox.getBoundingClientRect(),
areaPolicy: 'start-inside', // start inside, keep rotating if pointer leaves
inertia: { damping: 0.93 },
onDraw(q) { obj.style.transform = q.toCSSTransform(); }
});
```
Programmatic rotation (when idle):
```js
const tick = () => {
if (!tb.inertiaID && !tb.drag) {
tb.rotate(Quaternion.fromAxisAngle([0,1,0], Math.PI/180));
}
requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
```
## Installation
You can install `Trackball.js` via npm:
```bash
npm install trackball
```
Or with yarn:
```bash
yarn add trackball
```
Alternatively, download or clone the repository:
```bash
git clone https://github.com/rawify/Trackball.js
```
## Usage
Include the `trackball.min.js` file in your project:
```html
<script src="path/to/trackball.min.js"></script>
<script>
const cm = new Trackball();
...
</script>
```
Or in a Node.js project:
```javascript
const Trackball = require('trackball');
```
or
```javascript
import Trackball from "trackball";
```
## API
### `new Trackball(options)`
**Required**
* `scene: HTMLElement | string` – host element or CSS selector that receives interaction.
**Common**
* `q: Quaternion` *(default: `Quaternion.ONE`)* – initial orientation.
* `onDraw(q)` – called every time orientation changes.
* `onStart(q)` – called when a drag/press starts.
* `onEnd(q)` – called on release, or when inertia finishes.
* `enabled: boolean` *(default: `true`)* – enable/disable interaction.
* `limitAxis: 'x'|'y'|null` *(default: `null`)* – lock one axis if desired.
* `invertX: boolean` *(default: `false`)* – invert X motion.
* `invertY: boolean` *(default: `false`)* – invert Y motion.
* `speed: number` *(default: `1`)* – motion multiplier.
* `ballsize: number` *(default: `0.75`)* – **relative** ball radius (fraction of the active rect’s larger side).
* `border: number` *(default: `0.5`)* – rounded-arcball rim amount (0 = pure sphere, 0.5 = pleasant rim).
**Active area**
* `activeArea: HTMLElement | string | DOMRect | () => DOMRect | {left,top,width,height}` *(default: `null`)* – restricts interaction.
* `areaPolicy: 'inside-only'|'start-inside'|'always'` *(default: `'inside-only'`)* – update rules vs. the active box.
**Pointer options**
* `usePointerCapture: boolean` *(default: `true`)* – improves robustness while dragging.
* `passiveMove: boolean` *(default: `true`)* – passive `pointermove` listener.
**Inertia**
* `inertia: boolean | { damping?: number, maxStep?: number, minVelocity?: number, timeDiv?: number }`
* If `true`, uses defaults below.
* **Defaults:** `{ damping: 0.93, maxStep: 0.2, minVelocity: 1e-3, timeDiv: 50 }`
### Methods
* `rotate(by: Quaternion)` – multiply current orientation by `by` (only when not dragging and no inertia).
* `setQuaternion(q: Quaternion)` – replace orientation immediately.
* `setActiveArea(area)` – change the active area at runtime.
* `setEnabled(boolean)` – enable/disable interaction.
* `dispose()` – remove listeners and stop inertia.
## Notes
* Trackball.js only computes the quaternion. Apply it to your object any way you like, e.g. `q.toCSSTransform()`.
* To restrict to a sub-rect of the scene, provide `activeArea` (element, selector, rect, or function).
## Coding Style
As every library I publish, Trackball.js is also built to be as small as possible after compressing it with Google Closure Compiler in advanced mode. Thus the coding style orientates a little on maxing-out the compression rate. Please make sure you keep this style if you plan to extend the library.
## Building the library
After cloning the Git repository run:
```
npm install
npm run build
```
## Run a test
Testing the source against the shipped test suite is as easy as
```
npm run test
```
## Copyright and Licensing
Copyright (c) 2025, [Robert Eisele](https://raw.org/)
Licensed under the MIT license.