пятница, 6 декабря 2013 г.

jQuery draggable с относительным позиционированием

Задача: необходимо иметь диалоговое окно, которое можно перетаскивать и которое при изменении размера окна будет сохранять свое положение относительно центра экрана.
Решение:
Для перетаскивания объектов на javascript уже существует такой замечательный инструмент как jquery.ui.draggable, он очень богат и именно его мне и хотелось бы использовать, но у него перетаскиваемые элементы позиционируются абсолютно. Значит, необходимо выполнить небольшую подмену.
Суть идеи такова: диалоговое окно по умолчанию находится в относительном позиционировании (left: 50%, margin-left: -width/2). Перед началом перетаскивания мы просто переводим относительные координаты в абсолютные и срабатывает стандартный jQuery.ui.draggable со всеми его особенностями. По окончании перетаскивания мы просто возвращаем объекту относительные координаты. 


Реализация задуманной идеи:

$.widget('ui.reldraggable', $.ui.draggable, {
    create: function () {
        this.element.data('uiDraggable', this);
        this._super('_create');
    },
    _setOption: function (key, value) {
        this._super('_setOption', key, value);
    },
    disabled: function(){
        this.element.addClass("ui-draggable-disabled");
    },
    _mouseCapture: function(event) {
        var o = this.options;
        // among others, prevent a drag on a resizable-handle
        if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
            return false;
        }
        //Quit if we're not on a valid handle
        this.handle = this._getHandle(event);
        //if ( ! this.handle) return false;
   
        $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
           $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>").css({
                width: this.offsetWidth+"px", 
                height: this.offsetHeight+"px",
                position: "absolute", opacity: "0.001", 
                zIndex: 1000})
            .css($(this).offset())
            .appendTo("body");
        });
        return true;
    },
    _mouseStart: function(d){
        $(d.currentTarget).css({
            'left': d.currentTarget.offsetLeft,
            'margin-left': 0,
            'right': 'auto'
        });
  
        this._super(d, true);
    },
    _mouseStop: function(d){
        var a = $(this.element).offset().left,
            b = parseFloat($('body').width()),
            margin = parseInt(a - b/2) + 'px';
        
        $(this.element).css({
            'left'       : '50%',
            'right'      : 'auto',
            'margin-left': margin
        });
        this._super(d, true);
    }
});
Хочу заметить, что предложенный код будет работать только с jquery.ui.1.10.x. Для того, чтобы это работало в jquery.ui.1.9.x надо заменить _create на:
_create: function() {
    this.element.data('draggable', this);
    this._super('_create');
}