42 votes

Déplacement précis pour un élément déplaçable sur un div mis à l'échelle

LE PROBLÈME

Je vais avoir un problème mineur en faisant glisser des éléments sur une solution évolutive div conteneur.

Une fois que l'élément est en fait dans le conteneur, les éléments de glisser amende et le travail de la façon dont ils sont censés.

Les grands éléments qui sont déposés sur le évolutive conteneur n'ont pas trop un problème.

Mais lorsque les petits éléments sont traînés, vous pouvez voir que la souris n'est plus attaché à dit élément et quand il est tombé, il tombe un peu à côté de là où il est censé tomber.

Je suis en train d'essayer de trouver une solution pour que ma souris reste sur l'élément et il tombe où il est censé tomber.

J'ai résolu les problèmes peu à peu et que 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, il serait grandement apprécié.

Voici une codepen - cliquez et faites glisser les deux éléments bleus sur le récipient blanc pour l'essayer

Codepen

Affichage Plein Écran

Court GIF en Action


Cette wil aider à s'assurer que la drop zone de travaux avec une échelle de conteneur.

$.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 à être redimensionnables sur une échelle de conteneur

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

Le fait ainsi glisser fonctionne sur une échelle de conteneur

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

La création d'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éer déplaçable/redimensionner les éléments qui peuvent être déjà dans la boîte et en appliquant le glisser/redimensionner correctif à ces

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

    }
  };
})

Ce qui se passe 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();
            }
        });
    }
  };
})

dernier mais non le moindre, 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 code 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>

5voto

Julien Grégoire Points 7707

Pour la position du curseur lors d'un déplacement, voir cette réponse : Faire de la position du Curseur au centre de l'interface utilisateur.aide dans jquery-ui déplaçable méthode

Fondamentalement, vous pouvez contrôler la position du curseur de l'instance, permettant d'avoir quelque chose de plus dynamique que cursorAt. Comme ceci:

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

Puis sur la goutte, vous avez besoin de prendre en compte la transformation, mais vous pouvez simplifier la vie en utilisant le helper coordonnées à la place de la déplaçable. Comme ceci:

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

1voto

WeHateNick Points 54

Il ressemble à ce qui est la cause de cette apparence étrange est la suivante:

Tout d'abord, la petite div style comme display: block. Cela signifie que, même si elle ressemble à la div est de petite taille, que l'élément s'étend à l'ensemble du conteneur.

Deuxièmement, une fois que vous montrez à l'traîné carré sur la gauche de l'écran, la relation entre le curseur de la souris et de l'élément de l'ensemble est techniquement centré, mais vous réduisez la taille de l'élément d'origine pour un plus petit, et quand la largeur et la hauteur d'obtenir diminué, le résultat est rendu à la nouvelle largeur et la hauteur en partant du coin supérieur gauche de l'original de la div. (Si vous le style le petit bouton pour être display: inline, vous pouvez voir ce que je veux dire. Essayez de saisir dans le coin supérieur gauche et le essayer le coin inférieur droit. Vous verrez que l'ancien air bien, mais ce dernier est hors tension).

Mes propositions sont donc:

  1. Faire de la draggabble éléments display: inline
  2. Faire l'élément traîné sur la gauche de l'écran de la hauteur et de la largeur de l'élément d'origine sur la droite de l'écran.

Espérons que ça aide!

0voto

Flame Denise Points 25

J'ai fourche votre codepen et joué un peu avec elle.

Prendre un coup d'oeil ICI, et de voir si cela vous aide à trouver le "bug".

Pour votre draggable script, j'ai changé le code pour cela, l'ajout d' 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 + ')'
    });
}

Alors, pour votre droppable script, 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 "presque" résultat précis, mais vous pouvez être en mesure d'ajuster encore le polir. Espérons que cette aide.

0voto

Nidhin Chandran Points 641

Pour fixer les éléments à l'aide du curseur pendant le déplacement, vous devez simplement utiliser

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

Et d'un peu de changement en haut et à gauche des paramètres de "sm_textbox_item".

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

Pour la grande boîte encore un peu d'ajuster en haut et à gauche de l'élément est requis (stylo-mis à jour).

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

Je bifurquais votre stylet et fait quelques changements. Je sais que ce n'est pas une solution parfaite, je suis aussi en train d'essayer de résoudre ce bit par bit. Vous pouvez le vérifier ici

0voto

@ITWitch a raison, il doit y avoir un bogue dans draggable() . Style margin: 0 auto; en #sm_textbox_item est la source du problème.

Essayez d’ajouter ceci aux options déplaçables de votre directive draggableType pour corriger la position:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X