web-dev-qa-db-fra.com

Drop précis pour élément déplaçable sur div dived

LE PROBLÈME

Je rencontre un problème mineur en faisant glisser des éléments sur un conteneur div évolutif.

Une fois que l'élément est réellement dans le conteneur, les éléments glissent bien et fonctionnent comme ils sont censés le faire.

Les éléments plus volumineux glissés sur le conteneur évolutif ne posent pas trop de problèmes.

Mais lorsque des éléments plus petits sont déplacés, vous pouvez voir que la souris n’est plus attachée à cet élément et, une fois supprimée, elle tombe un peu là où elle est supposée tomber.

J'essaie de trouver une solution pour que ma souris reste sur l'élément et qu'elle tombe là où elle est supposée tomber.

J'ai résolu les problèmes petit à petit et vous pouvez voir ci-dessous, mais c'est la dernière pièce du puzzle qui me rend fou. Si quelqu'un a le temps de donner un coup de main, ce serait grandement apprécié.

Voici un codepen - cliquez et faites glisser les deux éléments bleus sur le conteneur blanc pour l'essayer

Codepen

Affichage plein écran

Court GIF en action


Ceci vous aidera à vous assurer que la zone de dépôt fonctionne avec un conteneur mis à l’échelle.

$.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
    });
  }
};

Activer l'élément à redimensionner sur un conteneur mis à l'échelle

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;
}

Fait en sorte que le glissement fonctionne sur un conteneur mis à l'échelle

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()));
}

Créer un élément déplaçable que je peux faire glisser sur la boîte.

.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 + ')'});
          }
      });
    }
  };
})

Créez des éléments déplaçables/redimensionnables qui peuvent déjà se trouver dans la zone et appliquez le correctif glisser/redimensionner à ceux-ci

.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();
            }
        })

    }
  };
})

Que se passe-t-il lorsqu'un élément est supprimé

.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, mon contrôleur

.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 } }

});

Voici la base de mon 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

Voir la réponse à la position du curseur pendant le glissement: Positionne le curseur au centre de ui.helper dans la méthode jquery-ui draggable

Fondamentalement, vous pouvez contrôler la position du curseur de l'instance, ce qui permet d'avoir quelque chose de plus dynamique que cursorAt. Comme ça:

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

Ensuite, sur le drop, vous devez prendre en compte la transformation, mais vous pouvez simplifier en utilisant les coordonnées helper au lieu du draggable. Comme ça:

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

Résultat: https://codepen.io/anon/pen/jamLBq

5
Julien Grégoire

Il semble que ce qui provoque ceci - regarde étrange soit le suivant:

Premièrement, la petite div est stylée en tant que display: block. Cela signifie que même s'il semble que la div est petite, cet élément s'étend à tout son conteneur.

Deuxièmement, une fois que le carré glissé est affiché sur l’écran de gauche, la relation entre le curseur de la souris et l’ensemble de l’élément est techniquement centrée, mais vous réduisez la taille de l’élément d’origine en un élément plus petit, et lorsque la largeur et la hauteur diminuent , le résultat est rendu avec les nouvelles largeur et hauteur en partant du coin supérieur gauche de la div d'origine. (Si vous appelez display: inline le petit bouton, vous pouvez voir ce que je veux dire. Essayez de le saisir dans le coin supérieur gauche et essayez celui en bas à droite. Vous verrez que le premier a l’air beau mais le dernier est désactivé).

Donc mes suggestions sont:

  1. Rendre les éléments draggabble display: inline
  2. Faites en sorte que l’élément déplacé sur l’écran de gauche présente la hauteur et la largeur exactes de l’élément d’origine sur l’écran de droite.

J'espère que cela pourra aider!

1
WeHateNick

Ce problème se produit lorsque vous ajoutez une transform au style d'un élément, puis que vous le rendez déplaçable. Vous devrez vous débrouiller sans transform pour obtenir un résultat parfait. J'ai passé 2 jours à déboguer jusqu'à ce que je le découvre, et je ne voulais pas que quelqu'un d'autre passe par cette douleur.

0
Aanand Kainth

Pour attacher des éléments avec le curseur pendant le glissement, il vous suffit d'utiliser 

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

Et un petit changement dans les paramètres haut et gauche de "sm_textbox_item".

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

Pour la grande boîte à nouveau, il faut encore quelques éléments Tweak en haut et à gauche (stylo mis à jour).

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

J'ai fourré votre stylo et fait quelques changements. Je sais que ce n'est pas une solution parfaite, j'essaie également de résoudre ce problème petit à petit. Vous pouvez le vérifier ici

0
Nidhin Chandran

J'ai bifurqué ton codepen et joué avec.

Jetez un coup d'oeil ICI, et voyez si cela vous aide à trouver le "bogue".

Pour votre script draggable, j'ai changé le code en ceci, en ajoutant margin-left et margin-right:

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 + ')'
    });
}

Ensuite, pour votre script droppable, j'ai changé la formule pour element_top et element_left:

// 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)

Il donne un résultat "presque" précis, mais vous pourrez peut-être le peaufiner davantage pour le peaufiner. J'espère que cela t'aides.

0
ITWitch