it-swarm.com.de

Genaues Ablegen für ziehbares Element auf skaliertem Div

DAS PROBLEM

Ich habe ein kleines Problem beim Ziehen von Elementen auf einen skalierbaren Div-Container.

Sobald sich das Element tatsächlich im Container befindet, ziehen sich die Elemente fein und funktionieren so, wie sie sollen.

Größere Elemente, die auf den skalierbaren Container gezogen werden, sind nicht besonders problematisch.

Wenn Sie jedoch kleinere Elemente ziehen, sehen Sie, dass die Maus nicht mehr an diesem Element befestigt ist. Wenn Sie sie fallen lassen, fällt sie ein wenig an der Stelle ab, an der sie fallen gelassen werden soll.

Ich versuche eine Lösung zu finden, bei der meine Maus auf dem Element bleibt und es dort abfällt, wo es abfallen soll.

Ich habe die Probleme Stück für Stück gelöst, und Sie sehen unten, aber dies ist das letzte Puzzleteil, das mich verrückt macht. Wenn jemand die Zeit hat, Hand anzulegen, wäre es sehr dankbar.

Hier ist ein Codepen - klicke und ziehe die zwei blauen Elemente auf den weißen Behälter, um es auszuprobieren

Codepen

Vollbildansicht

Kurzes GIF in Aktion


Dies hilft sicherzustellen, dass der ablegbare Bereich mit einem skalierten Container funktioniert.

$.ui.ddmanager.prepareOffsets = function(t, event) {
  var i, j, m = $.ui.ddmanager.droppables[t.options.scope] || [],
    type = event ? event.type : null,
    list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
  droppablesLoop: for (i = 0; i < m.length; i++) {
    if (m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0], (t.currentItem || t.element)))) {
      continue;
    }
    for (j = 0; j < list.length; j++) {
      if (list[j] === m[i].element[0]) {
        m[i].proportions().height = 0;
        continue droppablesLoop;
      }
    }
    m[i].visible = m[i].element.css("display") !== "none";
    if (!m[i].visible) {
      continue;
    }
    if (type === "mousedown") {
      m[i]._activate.call(m[i], event);
    }
    m[i].offset = m[i].element.offset();
    m[i].proportions({
      width: m[i].element[0].offsetWidth * percent,
      height: m[i].element[0].offsetHeight * percent
    });
  }
};

Ermöglicht die Größenänderung des Elements in einem skalierten Container

function resizeFix(event, ui) {
  var changeWidth = ui.size.width - ui.originalSize.width,
    newWidth = ui.originalSize.width + changeWidth / percent,
    changeHeight = ui.size.height - ui.originalSize.height,
    newHeight = ui.originalSize.height + changeHeight / percent;
  ui.size.width = newWidth;
  ui.size.height = newHeight;
}

Bewirkt, dass Drag & Drop in einem skalierten Container funktioniert

function dragFix(event, ui) { 
    var containmentArea = $("#documentPage_"+ui.helper.parent().parent().attr('id').replace(/^(\w+)_/, "")),
        contWidth = containmentArea.width(), contHeight = containmentArea.height();
    ui.position.left = Math.max(0, Math.min(ui.position.left / percent , contWidth - ui.helper.width()));
    ui.position.top = Math.max(0, Math.min(ui.position.top  / percent,  contHeight- ui.helper.height()));
}

Erstellen eines ziehbaren Elements, das ich auf die Box ziehen kann.

.directive('draggableTypes', function() {
  return {
    restrict:'A',
    link: function(scope, element, attrs) {
      element.draggable({
        zIndex:3000, 
        appendTo: 'body', 
        helper: function(e, ui){ 
          var formBox = angular.element($("#formBox"));
          percent = formBox.width() / scope.templateData.pdf_width;
          if(element.attr('id') == 'textbox_item')
              return $('<div class="text" style="text-align:left;font-size:14px;width:200px;height:20px;line-height:20px;">New Text Box.</div>').css({ 'transform': 'scale(' + percent + ')', '-moz-transform': 'scale(' + percent + ')', '-webkit-transform': 'scale(' + percent + ')', '-ms-transform': 'scale(' + percent + ')'});
          if(element.attr('id') == 'sm_textbox_item')
              return $('<div class="text" style="text-align:left;font-size:14px;width:5px;height:5px;line-height:20px;"></div>').css({ 'transform': 'scale(' + percent + ')', '-moz-transform': 'scale(' + percent + ')', '-webkit-transform': 'scale(' + percent + ')', '-ms-transform': 'scale(' + percent + ')'});
          }
      });
    }
  };
})

Erstellen Sie ziehbare/veränderbare Elemente, die sich möglicherweise bereits in der Box befinden, und wenden Sie die Korrektur zum Ziehen/Verändern der Größe auf diese an

.directive('textboxDraggable', function() {
  return {
    restrict:'A',
    link: function(scope, element, attrs) {

        element.draggable({ 
            cursor: "move",
            drag: dragFix,
            start: function(event, ui) {
                var activeId = element.attr('id');
                scope.activeElement.id = activeId;
                scope.activeElement.name = scope.templateItems[activeId].info.name;
                scope.$apply();
            }
        });

        element.resizable({
            minWidth: 25,
            minHeight: 25,
            resize: resizeFix,
            stop: function( event, ui ) {

                var activeId = element.attr('id');

                scope.activeElement.duplicateName = false;
                scope.activeElement.id = activeId;
                scope.activeElement.name = scope.templateItems[activeId].info.name;

                scope.templateItems[activeId]['style']['width'] = element.css('width');
                scope.templateItems[activeId]['style']['height'] = element.css('height');

                scope.$apply();
            }
        })

    }
  };
})

Was passiert, wenn ein Gegenstand fallengelassen wird

.directive('droppable', function($compile) {
  return {
    restrict: 'A',
    link: function(scope,element,attrs){
        element.droppable({
            drop:function(event,ui) {
                 var draggable = angular.element(ui.draggable),
                    draggable_parent = draggable.parent().parent(),
                    drag_type = draggable.attr('id'),
                    documentBg = element,
                    x = ui.offset.left,
                    y = ui.offset.top,
                    element_top = (y - documentBg.offset().top - draggable.height() * (percent - 1) / 2) / percent,
                    element_left = (x - documentBg.offset().left - draggable.width() * (percent - 1) / 2) / percent,
                    timestamp = new Date().getTime();

                    //just get the document page of where the mouse is if its a new element
                    if(draggable_parent.attr('id') == 'template_builder_box_container' || draggable_parent.attr('id') == 'template_builder_container')
                        var documentPage = documentBg.parent().parent().attr('id').replace(/^(\w+)_/, "");
                    //if you are dragging an element that was already on the page, get parent of draggable and not parent of where mouse is
                    else var documentPage = draggable_parent.parent().parent().attr('id').replace(/^(\w+)_/, "");

                    if(drag_type == "textbox_item")
                    {
                        scope.activeElement.id = scope.templateItems.Push({
                            info: {'page': documentPage,'name': 'textbox_'+timestamp, 'type': 'text'},
                            style: {'text-align':'left','font-size':'14px','top':element_top+'px','left':element_left+'px', 'width':'200px', 'height':'20px'}
                        }) - 1;

                        scope.activeElement.name = 'textbox_'+timestamp;
                    }
                    else if(drag_type == "sm_textbox_item")
                    {
                        scope.activeElement.id = scope.templateItems.Push({
                            info: {'page': documentPage,'name': '', 'type': 'text'},
                            style: {'text-align':'left','font-size':'14px','top':element_top+'px','left':element_left+'px', 'width':'5px', 'height':'5px'}
                        }) - 1;

                        scope.activeElement.name = 'textbox_'+timestamp;
                    }
                    else {
                        scope.templateItems[scope.activeElement.id]['style']['top'] = draggable.css('top');
                        scope.templateItems[scope.activeElement.id]['style']['left'] = draggable.css('left');
                    }

                scope.$apply();
            }
        });
    }
  };
})

last but not least, mein Controller

.controller('testing', function($scope, $rootScope, $state, $stateParams) {
  $scope.templateItems = [];
  $scope.activeElement = { id: undefined, name: undefined };
  $scope.templateData = {"id":"12345", "max_pages":1,"pdf_width":385,"pdf_height":800};
  $scope.clickElement = function(index)   { $scope.activeElement = { id: index, name: $scope.templateItems[index].info.name } }

});

Hier ist die Basis meines HTML

<div id="formBox" ng-style="formbox(templateData.pdf_width)" zoom>
    <div class="trimSpace" ng-style="trimSpace(templateData.pdf_width)" zoom>
        <div id="formScale" ng-style="formScale(templateData.pdf_width)" zoom>
            <form action="#" id="{{ templateData.id }}_form">
                <div ng-repeat="key in [] | range:templateData.max_pages">              
                    <div class="formContainer" id="{{ templateData.id + '_' + (key+1) }}" ng-style="{width: templateData.pdf_width+'px', height: templateData.pdf_height+'px'}">
                        <div class="formContent">
                            <div class="formBackground" id="documentPage_{{ (key+1) }}" droppable>
                                <div ng-hide="preview" ng-repeat="item in templateItems">
                                    <div ng-if="item.info.page == (key+1) && item.info.type == 'text'" id="{{ $index }}" data-type="{{ item.info.type }}" ng-click="clickElement($index)" class="text" ng-style="item.style" textbox-draggable>{{ item.info.name }}</div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>          
            </form>
        </div>
    </div>
</div>
43
bryan

Die Cursorposition beim Ziehen finden Sie in dieser Antwort: Cursorposition in der Mitte für ui.helper in der jquery-ui ziehbaren Methode erstellen

Grundsätzlich können Sie die Cursorposition der Instanz steuern, um etwas dynamischeres als CursorAt zu haben. So was:

start: function(event, ui){
    $(this).draggable('instance').offset.click = {
        left: Math.floor(ui.helper.width() / 2),
        top: Math.floor(ui.helper.height() / 2)
    }
},

Bei drop müssen Sie die Transformation berücksichtigen. Sie können dies jedoch vereinfachen, indem Sie die helper - Koordinaten anstelle von draggable verwenden. So was:

element_top = (ui.helper.offset().top / percent) - (documentBg.offset().top / percent);
element_left = (ui.helper.offset().left / percent) - (documentBg.offset().left / percent);

Ergebnis: https://codepen.io/anon/pen/jamLBq

5

Es sieht so aus, als ob das look komisch ist:

Zunächst wird das kleine div als display: block bezeichnet. Das heißt, obwohl das div klein zu sein scheint, erstreckt sich das Element tatsächlich auf den gesamten Container.

Zweitens ist das Verhältnis zwischen dem Mauszeiger und dem gesamten Element technisch zentriert, sobald Sie das gezogene Quadrat auf dem linken Bildschirm anzeigen. Dabei wird die Größe des ursprünglichen Elements auf eine kleinere Größe reduziert und die Breite und Höhe verringert Das Ergebnis wird mit der neuen Breite und Höhe wiedergegeben, beginnend an der oberen linken Ecke des ursprünglichen div. (Wenn Sie den kleinen Button als display: inline definieren, können Sie sehen, was ich meine. Versuchen Sie, ihn von der oberen linken Ecke aus zu ergreifen, und versuchen Sie es mit dem unteren rechten. Sie werden sehen, dass der erstere gut aussieht, der zweite jedoch ausgeschaltet ist.

Meine Vorschläge sind also:

  1. Machen Sie die Draggabble-Elemente display: inline
  2. Stellen Sie das gezogene Element auf dem linken Bildschirm auf die genaue Höhe und Breite des ursprünglichen Elements auf dem rechten Bildschirm ein.

Hoffentlich hilft das!

1
WeHateNick

Dieses Problem tritt auf, wenn Sie dem Stil eines Elements eine transform hinzufügen und es dann ziehbar machen. Sie müssen auf transform verzichten, um ein perfektes Ergebnis zu erzielen. Ich verbrachte 2 Tage damit, zu debuggen, bis ich es herausfand, und ich wollte nicht, dass jemand anderes diesen Schmerz durchmachte.

0
Aanand Kainth

Für das Anhängen von Elementen mit dem Cursor während des Ziehens müssen Sie nur verwenden 

cursorAt: { top: 6, left: -100 }

Und eine kleine Änderung in den oberen und linken Parametern von "sm_textbox_item".

top: (y - documentBg.offset().top) / (percent) + "px",
left: (x - documentBg.offset().left) / (percent) + "px",

Für die große Box ist wieder ein Tweak im oberen und linken Element erforderlich (Stift aktualisiert).

top: element_top-3, left: element_left+6.49,

Ich habe Ihren Stift gegabelt und ein paar Änderungen vorgenommen. Ich weiß, dass dies keine perfekte Lösung ist, ich versuche auch, das Stück für Stück zu lösen. Sie können hier überprüfen

0
Nidhin Chandran

@ITWitch hat recht, es muss einen Fehler in draggable() geben. Der Stil margin: 0 auto; In #sm_textbox_item Ist die Ursache des Problems.

Versuchen Sie, dies zu den ziehbaren Optionen in Ihrer draggableType-Direktive hinzuzufügen, um die Position zu korrigieren:

cursorAt: {left: -parseInt(window.getComputedStyle(element[0],null,null)['margin-left'])},

0

Ich habe dein Codepen gegabelt und damit rumgespielt.

Schau es dir HIER an und schau, ob es dir hilft, den "Fehler" zu finden.

Für Ihr draggable-Skript habe ich den Code dahingehend geändert und margin-left und margin-right hinzugefügt:

if(element.attr('id') == 'sm_textbox_item') { /* the small draggable box */
  var el = {
    pos: element.offset(), // position of the small box
    height: element.outerHeight() + 20,
    left: 0
  }
  var deduct = $('#formBox').innerWidth() - 20; // width of the element that's left of small box's container
  el.left = el.pos.left - deduct;

  return $('<div class="text" style="text-align:left; font-size:14px; width:5px; height:5px; line-height:20px;"></div>')
    .css({
      'margin-left': el.left + 'px',
      'margin-top': el.pos.top - el.height + 'px',

      'transform': 'scale(' + percent + ')',
      '-moz-transform': 'scale(' + percent + ')',
      '-webkit-transform': 'scale(' + percent + ')',
      '-ms-transform': 'scale(' + percent + ')'
    });
}

Dann habe ich für Ihr droppable-Skript die Formel für element_top und element_left geändert:

// old formula
element_top = (y - documentBg.offset().top - draggable.height() * (percent - 1) / 2) / percent
element_left = (x - documentBg.offset().left - draggable.width() * (percent - 1) / 2) / percent

// new formula
element_top = (y - documentBg.offset().top) / (percent * 0.915)
element_left = (x - documentBg.offset().left) / (percent * 0.915)

Es gibt ein "fast" genaues Ergebnis, aber Sie können es möglicherweise weiter verfeinern, um es zu polieren. Hoffe das hilft.

0
ITWitch