raphael.draggable icon indicating copy to clipboard operation
raphael.draggable copied to clipboard

Errors with Raphael 2.0

Open adamholmes opened this issue 14 years ago • 3 comments

When using with Raphael 2.0 the following error appears: "paper.draggable is undefined" on line 25.

I'm using it the standard way as shown in the README.

Any ideas?

Thanks.

adamholmes avatar Oct 17 '11 20:10 adamholmes

Same issue here

mohsen1 avatar Nov 15 '11 23:11 mohsen1

https://github.com/DmitryBaranovskiy/raphael/issues/409

jazzunit avatar Jan 07 '12 04:01 jazzunit

Hi everybody, here is my two cents proposed solution just tested with Raphael 2.1.0.

/** 
 * raphael.draggable plugin 1.0.2
 * Copyright (c) 2015 @author Matthew Ephraim
 *
 * licensed under the MIT license 
 */
(function() {
  /** 
   * Raphael paper extensions namespace 
   */
  Raphael.fn.$draggable = {};

  /** 
   * Enable draggable plugin on canvas 
   */
  Raphael.fn.draggable = function() {
    var paper = this;
    paper.$draggable.enable(paper);
    return paper;
  };

  /** 
   * Calling the draggable.enable function on a Raphael 
   * canvas will add the functions current and clearCurrent
   * to the canvas. It will also wrap the functions that 
   * create elements and sets with extra draggable functionality 
   *
   * @return the canvas
   */
  Raphael.fn.$draggable.enable = function(paper) {
    //var paper = this;

    var currentDraggable;

    paper.$draggable.enabled = true;

    /**
     * Set the current draggable element to the element
     * that was passed in. Otherwise, return the current
     * draggable element (if there is one)
     *
     * @param {Raphael element} element (optional) The element to set 
     * as the current draggable
     *
     * @return the canvas, if an element was passed in, otherwise, the
     * current draggable
     */
    paper.$draggable.current = function(element) {
      if (element) {
        currentDraggable = element;
        return paper;
      }
      else {
        return currentDraggable;
      }
    };

    /** 
     * Clear out the current draggable 
     */
    paper.$draggable.clearCurrent = function() {
      currentDraggable = null;  
      return paper;  
    };

    overrideElements(paper);
    overrideSet(paper);
    return paper;
  };

  /**
   * Wrap each element creation function on the canvas with a custom
   * element function that adds draggable functionality to the 
   * newly created element before returning it
   *
   * @param {Raphael Canvas} paper The canvas that will have its functions wrapped 
   */
  function overrideElements(paper) {
    var elementTypes = ['circle', 'rect', 'ellipse', 'image', 'text', 'path'];
    for(var i = 0; i < elementTypes.length; i++) {
      overrideElementFunc(paper, elementTypes[i]);
    }
  }

  /** 
   * Overrides the element creation function that matches up with elemenType
   * 
   * @param {Raphael canvas} paper The canvas that will have its function wrapped
   * @param {String} elementType The name of the element function to be wrapped
   */
  function overrideElementFunc(paper, elementType) {    
    paper[elementType] = function(oldFunc) {
      return function() {
        var element = oldFunc.apply(paper, arguments);
        element.draggable = new DraggableExtension(element);
        return element;
      };
    }(paper[elementType]);
  }

  var lastDragX,
      lastDragY;

  /**
   * Base functionality to be used as a prototype for draggable extentions
   */
  var BaseFunctions = {
    enable: function() {
      this.enabled = true;
      return this.element;
    },

    disable: function() {
      this.enabled = false;
      return this.element;
    }
  };

  /** 
   * Extension for Raphael elements that adds draggable functionality  
   * @constructor
   */
  var DraggableExtension = function (element) {
    var paper = element.paper;

    this.element = element;
    this.enabled = false;

    element.mousedown(function(event) {
      if (paper.$draggable.current() || !element.draggable.enabled)
        return;

      paper.$draggable.current(element.draggable.parent || element);
      lastDragX = event.clientX;
      lastDragY = event.clientY;

      startDragger();
    });

    document.onmouseup = function() {
      document.onmousemove = null;
      paper.$draggable.clearCurrent();
    };

    function startDragger() {
      document.onmousemove = function(event) {
        if (paper.$draggable.current()) {
          var transX = event.clientX - lastDragX;
          var transY = event.clientY - lastDragY;

          paper.$draggable.current().translate(transX, transY);
          lastDragX = event.clientX;
          lastDragY = event.clientY;
        }
      };
    }
  };

  DraggableExtension.prototype = BaseFunctions;

  /** 
   * Wrap the set creation function on the canvas with a custom
   * function that adds draggable functionality to the 
   * newly created set before returning it
   * 
   * @param {Raphael canvas} paper The canvas that will have its function wrapped
   */
  function overrideSet(paper) {
    paper.set = function(oldFunc) { 
      return function() {
        var set = oldFunc.apply(paper, arguments);
        set.paper = paper;
        set.draggable = new DraggableSetExtension(set);

        overrideSetFunctions(set);
        return set;
      };
    }(paper.set);
  }

  /**
   * Takes a Raphael set and wraps some functions so that
   * they take advantage of the extra draggable functionality
   *
   * @param {Raphael set} set The that will have its functions wrapped
   */
  function overrideSetFunctions(set) {
    set.push = function(oldFunc) { 
      return function() {
        oldFunc.apply(set, arguments);

        for (var i = 0, len = arguments.length; i < len; i++) {
          var element = arguments[i];
          if (element.draggable) {
            element.draggable.parent = set;

            if (set.draggable.enabled) {
              element.draggable.enable();
            }
          }
        }
      };
    }(set.push);
  }

  /**
   * Extension for draggable Raphael sets
   * @constructor
   */ 
  DraggableSetExtension = function(set) {
    this.element = set;
  };

  DraggableSetExtension.prototype = BaseFunctions;
})();

Taking inspiration from what the demo does for elements, the plugin usage should be changed as follow.

paper = Raphael(0,0, 800, 600);
paper.draggable(); // enable canvas method is called here
attrs = { 'fill': 'white' };
  paper.rect(0, 0, 100, 100).
        attr(attrs).
        draggable.enable();
  paper.ellipse(150, 150, 50, 100).
        attr(attrs).
        draggable.enable();

Any ideas about potential or real problems caused by the changes described in this comment?

Wating to hear any feedback.

Thanks, Giorgio.

garata avatar Sep 12 '15 12:09 garata