Jump to content
Search Community

FabricJS Plugin?

Praney Behl
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

How would you like Fabric.js Plugin for GSAP?  

23 members have voted

  1. 1. Fabric.js is a powerful and simple Javascript canvas library. Have you tried it?

  2. 2. Would you be interested in seeing a GSAP Fabric.js plugin to handle it even easily?



Recommended Posts

Posted

Hi,

 

I have been playing with the Fabric.js library for a little while. What like about it is the ease of transforming objects and a couple more.

 

I am wondering if there is a plugin for Fabric.js available with GSAP? If not then how to use Fabric.js like transformations(at http://fabricjs.com/customization/)  in Kinetic.js?

 

Thanks in advance,

Praney

Posted

We don't have any plans to create a fabricjs plugin at this point, but it very well could happen in the future if enough people are interested.

Posted

Thanks Jack,

 

Maybe we can start a poll to find out how many others are interested.

Posted

All right just added a poll to this thread for anyone to vote if they are interested in Fabris.js plugin.

 

Cheers!

Posted

I think TransformManager JS would be better than FabricJS plugin ;) 

  • Like 2
Posted

Well if we can get TransformManager JS then there is nothing better than that.

  • Like 1
  • 1 year later...
Posted

Actually I work with TransformManager (Flash) and since 2013 I was wondering a TransformManager JS. If it's connected with GSAP it will be the best HTML5 tool

Posted

Maybe we'll create that someday, but there are tons of other things on our to-do list with the animation platform that are a higher priority for now :)

  • 2 years later...
Posted

Forget it !

I also asked for it  3 years ago  and got the same answer...

 

So sad !

 

:(

 

 

Posted

Hi @EricMtl

 

Are you asking about a transform manager? I don't know what's on GSAP's roadmap, but I seriously doubt that would be on there. What's it supposed to work with e.g. HTML, SVG, canvas, a canvas library? That's a pretty tall order making it work cross-platform with different types of content.

 

Fabric.js already does that out of the box, but for canvas. If you're looking for something with HTML, maybe this demo will give you some ideas. It uses GSAP's Draggable, and some simple vector math to rotate and scale an object.

 

See the Pen gGLLOa by osublake (@osublake) on CodePen.

 

  • Like 2
Posted

Thx OSUBlake !

Pretty interesting  proof of concept.

But as Chrysto and surely many others users, we miss TM for a while...

 

 

 

 

 

  • Like 1
Posted

I feel your pain. I've searched all over for something similar, but haven't found anything outside of Fabric.js. It was definitely much easier when GreenSock only had to worry about one platform, Flash. Now it's much more complicated when you throw HTML, SVG, and canvas into the mix.

 

  • 5 years later...
Posted

5 years after - having similar issues.

So far, the problems I seen emerging are when creating custom objects in fabricjs with custom properties.

Here is my attempt at a plugin.

 

All properties should be updated in fabric using `set(key, value)` - some of them work directly through `fobj.prop = value` - but if one subclasses `fabric.Object` and decorates it with a custom property, gsap won't interpolate it. I actually don't know why.

Here is my attempt at solving this - using a Proxy - intercept calls and then use the fabric way of setting / getting properties.

But it is ugly as then props must be placed in the plugin registered key like this

 

      anim.to(pi, {
        fabric: {
          progress: 75,
          top: 20,
        },
        duration: 3,
        ease: "none",
        onComplete: () => {
          pi.setCoords();
        },
      });

 

And here is the ugly plugin :) - hopefully another human in 5 years from now will find it useful.

 

/* eslint-disable */

let gsap,
  _coreInitted,
  _win,
  _fabricjs,
  _windowExists = () => typeof window !== "undefined",
  _getGSAP = () => gsap || (_windowExists() && (gsap = window.gsap) && gsap.registerPlugin && gsap),
  _warn = (message) => console.warn(message),
  _initCore = (core) => {
    gsap = core || _getGSAP();
    if (_windowExists()) {
      _win = window;
    }
    if (gsap) {
      _coreInitted = 1;
    }
  };

export const FabricJSPlugin = {
  version: "3.11.3",
  name: "fabric",
  init(target, value, tween, index, targets) {
    if (!_coreInitted) {
      _initCore();
      if (!gsap) {
        _warn("Please gsap.registerPlugin(FabricJSPlugin)");
      }
    }
    this.target = new Proxy(target, {
      get(obj, key) {
        return obj.get(key);
      },
      set(obj, key, val) {
        obj.set(key, val);
        return obj;
      },
    });
    let p, end;
    for (p in value) {
      end = value[p];
      if (this.target[p] != null) {
        this.add(this.target, p, "get", end);
      }
    }
  },
  register: _initCore,
};

FabricJSPlugin.registerFabricJS = (fabricjs) => {
  _fabricjs = fabricjs;
};

_getGSAP() && gsap.registerPlugin(FabricJSPlugin);

export { FabricJSPlugin as default };

 

Thank you guys, for such awesome tools and libraries.

  • Like 1
  • Thanks 1
  • 2 years later...
Ralph Fueley
Posted

- hopefully another human in 5 years from now will find it useful.

 

Thank you, @iongion, it was useful a scant 3 years later! I used it as inspiration for a TypeScript implementation.

 

 

Plugin Source

/*
 * @fileoverview GSAP Fabric.js Plugin inspired by iongion's code.
 * @see https://gsap.com/community/forums/topic/8295-fabricjs-plugin/
 */

import * as fabric from 'fabric';
import { gsap } from 'gsap';

// --- Type Definitions ---

/**
 * Interface defining the structure of the FabricJSPlugin object.
 * It extends the base GSAP Plugin interface.
 */
interface FabricJSPluginInterface extends gsap.Plugin {
  version: string;
  name: string;
  // `init` signature matches gsap.Plugin, using 'any' for broader compatibility.
  // Specific types are used within the implementation.
  init(
    this: gsap.PluginScope,
    /* tslint:disable-next-line:no-any */
    target: any,
    value: gsap.TweenVars,
    tween: gsap.core.Tween,
    index: number,
    /* tslint:disable-next-line:no-any */
    targets: any[]
  ): void;
  // Method to explicitly register the GSAP instance.
  register(core: typeof gsap): void;
  // Method to explicitly register the Fabric.js instance.
  registerFabricJS(fabricjs: typeof fabric): void;
}

// --- Module State ---

let registeredGSAP: typeof gsap | undefined;
let registeredFabric: typeof fabric | undefined;
let isCoreInitialized = false;

// --- Helper Functions ---

/** Checks if the code is running in a browser environment. */
function hasWindow(): boolean {
  return typeof window !== 'undefined';
}

/** Logs a warning message specific to the FabricJSPlugin. */
function logWarning(message: string): void {
  console.warn(`FabricJSPlugin: ${message}`);
}

/**
 * Attempts to retrieve the GSAP instance, either from a registered one or from
 * the global window object.
 */
function getGSAPInstance(): typeof gsap | undefined {
  if (registeredGSAP) {
    return registeredGSAP;
  }
  if (hasWindow()) {
    const globalGSAP = (window as unknown as { gsap: typeof gsap }).gsap;

    // Check if global GSAP exists and has the registerPlugin method.
    if (globalGSAP && typeof globalGSAP.registerPlugin === 'function') {
      registeredGSAP = globalGSAP;
      return registeredGSAP;
    }
  }
  return undefined;
}
/**
 * Initializes the plugin's core dependencies, primarily GSAP, and ensures it
 * only runs once.
 * @param core An optional GSAP instance to register directly.
 */
function initializeCore(core?: typeof gsap): void {
  if (isCoreInitialized) {
    return;
  }

  registeredGSAP = core || getGSAPInstance();

  if (registeredGSAP) {
    isCoreInitialized = true;
  } else if (!core) {
    // Only warn if initialization was attempted without providing a core and
    // the global lookup failed.
    logWarning(
      'GSAP core not found. Please ensure GSAP is loaded or pass it to FabricJSPlugin.register().'
    );
  }
}
/**
 * Checks if a property value on a Fabric object is suitable for tweening.
 * @param target The Fabric object (to check for presence of 'set' method).
 * @param value The current value of the property.
 * @return True if the property can likely be tweened, false otherwise.
 */
/* tslint:disable-next-line:no-any */
function isTweenableFabricProperty(
  target: fabric.FabricObject,
  /* tslint:disable-next-line:no-any */
  value: any
): boolean {
  return (
    value !== undefined &&
    value !== null &&
    typeof value !== 'function' && // Don't tween methods.
    typeof target.set === 'function' // Ensure the object has a 'set' method.
  );
}

// --- FabricJSPlugin Implementation ---

/**
 * FabricJSPlugin implementation.
 *
 * This object is exported as the default, so users can import it directly.
 */
export const FabricJSPlugin: FabricJSPluginInterface = {
  version: '0.9.0',
  name: 'fabric',

  /**
   * GSAP Plugin Initialization Method.
   * Called by GSAP for each target being tweened.
   */
  init(
    this: gsap.PluginScope, // Provides 'this.add' etc.
    target: fabric.FabricObject, // Use specific type within the implementation.
    value: gsap.TweenVars, // The properties being tweened e.g. `{left: 100, opacity: 0.5}`.
    tween: gsap.core.Tween,
    index: number,
    targets: fabric.FabricObject[] // All targets in the tween.
  ) {
    // --- 1. Safety Checks & Initialization ---

    if (!(target instanceof fabric.FabricObject)) {
      logWarning(
        `Target at index ${index} is not a valid fabric.FabricObject instance.`
      );
      return;
    }

    if (!isCoreInitialized) {
      initializeCore();
      if (!isCoreInitialized) {
        logWarning('Cannot initialize plugin - GSAP core missing.');
        return;
      }
    }

    if (!registeredFabric && target.canvas && (target.canvas as any).fabric) {
      registeredFabric = (target.canvas as any).fabric;
    }

    // --- 2. Proxy Setup for Property Interception ---

    const proxiedTarget = new Proxy(target, {
      /* tslint:disable-next-line:no-any */
      get(obj: fabric.FabricObject, key: string | symbol): any {
        if (typeof key === 'string' && typeof obj.get === 'function') {
          return obj.get(key);
        }
        return Reflect.get(obj, key);
      },
      /* tslint:disable-next-line:no-any */
      set(obj: fabric.FabricObject, key: string | symbol, val: any): boolean {
        if (typeof key === 'string' && typeof obj.set === 'function') {
          // Ensure key is string for `obj.set`.
          obj.set(key as string, val);
          if (obj.canvas) {
            obj.canvas.requestRenderAll();
          }
          return true;
        }
        return Reflect.set(obj, key, val);
      },
    });

    // --- 3. Register Properties with GSAP ---

    for (const propertyName in value) {
      if (Object.prototype.hasOwnProperty.call(value, propertyName)) {
        const endValue = value[propertyName];

        try {
          const startValue = target.get(propertyName);

          // Check if this property is suitable for tweening.
          if (isTweenableFabricProperty(target, startValue)) {
            this.add(proxiedTarget, propertyName, startValue, endValue);
          } else {
            // Log warnings for properties that will be skipped.
            if (typeof startValue === 'function') {
              logWarning(
                `Skipping property '${propertyName}' because it is a function.`
              );
            } else if (startValue === undefined || startValue === null) {
              logWarning(
                `Property '${propertyName}' not found or is null/undefined on the target object.`
              );
            } else if (typeof target.set !== 'function') {
              logWarning(
                `Target object does not have a 'set' method, cannot tween property '${propertyName}'.`
              );
            }
          }
          /* tslint:disable-next-line:no-any */
        } catch (error: any) {
          logWarning(
            `Error accessing property '${propertyName}' on target: ${
              error?.message || error
            }`
          );
        }
      }
    }
  },

  /**
   * Public method to explicitly register the GSAP instance.
   */
  register(core: typeof gsap): void {
    if (!core) {
      logWarning('Attempted to register an invalid GSAP instance.');
      return;
    }
    initializeCore(core);
  },

  /**
   * Public method to explicitly register the Fabric.js instance.
   */
  registerFabricJS(fabricjs: typeof fabric): void {
    if (!fabricjs) {
      logWarning('Attempted to register an invalid Fabric.js instance.');
      return;
    }
    registeredFabric = fabricjs;
  },
};

// --- Auto-registration with Global GSAP ---

const globalGSAPInstance = getGSAPInstance();
if (globalGSAPInstance) {
  if (!isCoreInitialized) {
    initializeCore(globalGSAPInstance);
  }
  // Ensure core is initialized before registering.
  if (registeredGSAP) {
    registeredGSAP.registerPlugin(FabricJSPlugin);
  }
}

// --- Export ---
export { FabricJSPlugin as default };

 

Usage

import { gsap } from 'gsap';
import FabricJSPlugin from '../gsap_fabric_plugin'; // Update to reflect your path to plugin.

gsap.registerPlugin(FabricJSPlugin);

const blueRect = new fabric.Rect({
    fill: 'blue',
    height: 100,
    left: 100,
    objectCaching: false,
    top: 100,
    width: 100,
  });


 gsap.to(this.blueRect, { left: 200 });

 

  • Thanks 1
  • 2 weeks later...
EricMtl
Posted

Surprised to see this thread still alive after 12 years !!😮
Thx Raph for you collab. I was dying to see the demo, but the page doesn't seem anymore active on codesandbox.io. 🙁
 

PS : GSAP now belonging to a web oriented platform, chances to get some interactive plugins like TM are quite null

 

EricMtl

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...