Skip to content

Repository files navigation

OpenSeadragon Fabric Overlay

codecov npm version License: MIT Build Status Downloads

A powerful plugin that seamlessly integrates FabricJS with OpenSeadragon, enabling interactive annotations, drawings, and shape manipulation on top of deep-zoom images.

⚠️ Version 2.0 Breaking Changes: This version uses Fabric.js 7.x. Import syntax changed from fabric.Rect to named imports like import { Rect } from 'fabric'.

Note: This library automatically sets default object origins to left/top for intuitive positioning. In Fabric.js 7.x, the default changed to center/center. You can override this by passing defaultObjectOrigin in the config:

const overlay = initOSDFabricOverlay(viewer, {
  defaultObjectOrigin: { originX: 'center', originY: 'center' }
});

Live Demo | Report Bug | Request Feature

Features

  • Interactive Annotations - Add shapes, text, and drawings that scale with image zoom
  • 🎨 Full FabricJS Support - Access all FabricJS features and objects
  • 🖱️ Freehand Drawing - Built-in support for pencil, spray, and custom brushes
  • 📐 Shape Tools - Rectangles, circles, polygons, lines, and more
  • 🔄 Automatic Synchronization - Overlay stays perfectly aligned during pan and zoom
  • 📦 TypeScript Support - Full type definitions included
  • 🪶 Lightweight - Minimal overhead, maximum performance
  • 🔧 Highly Configurable - Customize every aspect of the overlay

Installation

npm install openseadragon-fabric-overlay

Peer Dependencies

Ensure you have the required peer dependencies installed:

npm install openseadragon fabric@^7.1.0

Note: Version 2.x requires Fabric.js 7.x. For Fabric.js 6.x support, use version 1.x.

Quick Start

import OpenSeadragon from "openseadragon";
import { Rect } from "fabric";
import { initOSDFabricOverlay } from "openseadragon-fabric-overlay";

// Initialize OpenSeadragon viewer
const viewer = OpenSeadragon({
  id: "viewer",
  prefixUrl: "//openseadragon.github.io/openseadragon/images/",
  tileSources: "//openseadragon.github.io/example-images/highsmith/highsmith.dzi",
});

// Create fabric overlay
const overlay = initOSDFabricOverlay(viewer, {
  fabricCanvasOptions: {
    selection: true,
  },
});

// Add a rectangle
const rect = new Rect({
  left: 100,
  top: 100,
  width: 200,
  height: 150,
  fill: "rgba(255, 0, 0, 0.3)",
  stroke: "#ff0000",
  strokeWidth: 2,
});

overlay.fabricCanvas().add(rect);

API Reference

initOSDFabricOverlay

Initialize the fabric overlay on an OpenSeadragon viewer.

function initOSDFabricOverlay(
  viewer: OpenSeadragon.Viewer,
  options?: FabricOverlayOptions,
  overlayId?: string
): FabricOverlay

Parameters:

  • viewer - OpenSeadragon viewer instance
  • options - Configuration options (optional)
    • fabricCanvasOptions - Options passed to FabricJS canvas constructor
  • overlayId - Unique identifier for the overlay (optional)

Returns: FabricOverlay instance

FabricOverlay Methods

fabricCanvas()

Get the underlying FabricJS canvas instance.

overlay.fabricCanvas(): fabric.Canvas

resize()

Manually trigger a resize of the overlay canvas.

overlay.resize(): void

clear()

Remove all objects from the canvas.

overlay.fabricCanvas().clear();

Usage Examples

Adding Shapes

Rectangle

const rect = new Rect({
  left: 200,
  top: 200,
  width: 300,
  height: 200,
  fill: "rgba(0, 123, 255, 0.2)",
  stroke: "#007bff",
  strokeWidth: 3,
});

overlay.fabricCanvas().add(rect);

Circle

const circle = new Circle({
  left: 500,
  top: 300,
  radius: 100,
  fill: "rgba(40, 167, 69, 0.2)",
  stroke: "#28a745",
  strokeWidth: 3,
});

overlay.fabricCanvas().add(circle);

Text

const text = new Text("Hello World", {
  left: 300,
  top: 400,
  fontSize: 40,
  fill: "#333",
  fontFamily: "Arial",
});

overlay.fabricCanvas().add(text);

Polygon

const polygon = new Polygon(
  [
    { x: 100, y: 100 },
    { x: 200, y: 50 },
    { x: 300, y: 100 },
    { x: 250, y: 200 },
    { x: 150, y: 200 },
  ],
  {
    fill: "rgba(255, 193, 7, 0.2)",
    stroke: "#ffc107",
    strokeWidth: 2,
  }
);

overlay.fabricCanvas().add(polygon);

Freehand Drawing

Enable drawing mode with a pencil brush:

// Disable OpenSeadragon mouse navigation
viewer.setMouseNavEnabled(false);

const canvas = overlay.fabricCanvas();

// Configure pencil brush
canvas.freeDrawingBrush = new PencilBrush(canvas);
canvas.freeDrawingBrush.width = 5;
canvas.freeDrawingBrush.color = "#000000";
canvas.isDrawingMode = true;

// To disable drawing mode
canvas.isDrawingMode = false;
viewer.setMouseNavEnabled(true);

Spray Brush

canvas.freeDrawingBrush = new SprayBrush(canvas);
canvas.freeDrawingBrush.width = 10;
canvas.freeDrawingBrush.color = "#ff0000";
canvas.isDrawingMode = true;

Object Selection and Manipulation

// Enable selection
overlay.fabricCanvas().selection = true;

// Listen to selection events
overlay.fabricCanvas().on("selection:created", (e) => {
  console.log("Object selected:", e.selected);
});

overlay.fabricCanvas().on("object:modified", (e) => {
  console.log("Object modified:", e.target);
});

// Get active object
const activeObject = overlay.fabricCanvas().getActiveObject();

// Remove active object
if (activeObject) {
  overlay.fabricCanvas().remove(activeObject);
}

Serialization and Deserialization

Save and restore canvas state:

// Save canvas to JSON
const json = overlay.fabricCanvas().toJSON();
localStorage.setItem("canvas", JSON.stringify(json));

// Load canvas from JSON
const savedJson = JSON.parse(localStorage.getItem("canvas"));
overlay.fabricCanvas().loadFromJSON(savedJson, () => {
  overlay.fabricCanvas().renderAll();
});

Custom Object Properties

const rect = new Rect({
  left: 100,
  top: 100,
  width: 200,
  height: 100,
  fill: "blue",
  // Custom properties
  id: "my-rect-1",
  metadata: {
    type: "annotation",
    author: "John Doe",
    timestamp: Date.now(),
  },
});

overlay.fabricCanvas().add(rect);

// Access custom properties
rect.on("selected", () => {
  console.log(rect.id, rect.metadata);
});

Event Handling

const canvas = overlay.fabricCanvas();

// Mouse events
canvas.on("mouse:down", (e) => console.log("Mouse down", e.pointer));
canvas.on("mouse:move", (e) => console.log("Mouse move", e.pointer));
canvas.on("mouse:up", (e) => console.log("Mouse up", e.pointer));

// Object events
canvas.on("object:added", (e) => console.log("Object added", e.target));
canvas.on("object:removed", (e) => console.log("Object removed", e.target));
canvas.on("object:modified", (e) => console.log("Object modified", e.target));

// Selection events
canvas.on("selection:created", (e) => console.log("Selection created"));
canvas.on("selection:cleared", (e) => console.log("Selection cleared"));

Advanced Configuration

Custom Canvas Options

const overlay = initOSDFabricOverlay(viewer, {
  fabricCanvasOptions: {
    selection: true,
    backgroundColor: "rgba(255, 255, 255, 0.1)",
    renderOnAddRemove: true,
    enableRetinaScaling: true,
    allowTouchScrolling: false,
    preserveObjectStacking: true,
  },
  scale: 1000,
});

Coordinate Conversion

Convert between viewport and canvas coordinates:

// OpenSeadragon viewport point to canvas point
const viewportPoint = viewer.viewport.pointFromPixel(
  new OpenSeadragon.Point(100, 100)
);
const canvasPoint = {
  x: viewportPoint.x * overlay.fabricCanvas().getWidth(),
  y: viewportPoint.y * overlay.fabricCanvas().getHeight(),
};

Best Practices

  1. Disable Mouse Navigation During Drawing

    viewer.setMouseNavEnabled(false); // When drawing
    viewer.setMouseNavEnabled(true); // When done
  2. Optimize Performance

    // Disable rendering during bulk operations
    overlay.fabricCanvas().renderOnAddRemove = false;
    // Add multiple objects
    overlay.fabricCanvas().add(obj1, obj2, obj3);
    // Re-enable and render
    overlay.fabricCanvas().renderOnAddRemove = true;
    overlay.fabricCanvas().renderAll();
  3. Clean Up Event Listeners

    // Remove specific listener
    canvas.off("mouse:down", handler);
    
    // Remove all listeners for an event
    canvas.off("mouse:down");

Limitations

  • Static FabricJS canvases are not supported
  • For non-interactive overlays, disable interactions via fabricCanvasOptions

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Update version in package.json
  5. Build the project (npm run build)
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

Development Setup

# Install dependencies
npm install

# Build the library
npm run build

# Run demo site locally
npm run dev

# Run tests
npm test

Testing Locally

# Build the package
npm run build

# Link locally
npm link

# In your test project
npm link openseadragon-fabric-overlay

Publishing

# Update version in package.json
npm version patch|minor|major

# Build and publish
npm run build
npm publish

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

This project is inspired by @altert's OpenseadragonFabricjsOverlay. The original package used JavaScript with Prototype-based functions, which caused type safety issues in strictly-typed codebases and was built for older OpenSeadragon versions.

@brunoocastro rewrote it in TypeScript as openseadragon-fabric. This package builds upon that work with optimizations and extended functionality.

Support


Made with ❤️ by Ravi Shankar Saini

About

A lightweight plugin to integrate Fabric.js as an interactive drawing overlay for OpenSeadragon - enabling shapes, freehand drawing, and annotations on zoomable images.

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages