javascript - mdn - w3school




縮放div上可拖動元素的精確下降 (4)

@ITWitch是對的, draggable()必須有一些bug。 款式margin: 0 auto;#sm_textbox_item是問題的根源。

嘗試將此添加到draggableType指令中的可拖動選項以更正位置:

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

問題

我在將元素拖到可伸縮的div容器上時遇到了一個小問題。

一旦元素實際上在容器中,元素就會拖動並按照預期的方式工作。

拖動到可伸縮容器上的較大元素沒有太多問題。

但是當拖動較小的元素時,您可以看到鼠標不再附加到所述元素上,當它被丟棄時,它會在它應該下降的地方稍微下降。

我正在嘗試找到一個解決方案,我的鼠標停留在元素上,它會掉落到應該放下的位置。

我已經一點一點地解決了問題,你可以在下面看到,但這是讓我瘋狂的最後一塊拼圖。 如果有人有時間伸出援助之手,我將不勝感激。

這是一個codepen - 單擊並將兩個藍色元素拖到白色容器上以試用它

Codepen

全屏視圖

行動中的短GIF

這將有助於確保可放置區域與縮放容器一起使用。

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

使元素可以在縮放容器上調整大小

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

使它在拖放的縮放容器上工作

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

創建一個可以拖動到框中的可拖動元素。

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

創建可能已在框中的可拖動/可調整大小的元素,並將拖動/調整大小修復應用於這些元素

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

    }
  };
})

刪除項目時會發生什麼

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

最後但同樣重要的是,我的控制器

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

});

這是我的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>

transform添加到元素的樣式,然後使其可拖動時會發生此問題。 你必須做到沒有transform才能獲得完美的結果。 我花了2天的時間調試,直到找到它,我不想讓別人經歷那種痛苦。


我已經分叉你的codepen並玩弄它。

看看它在這裡 ,看看它是否有助於你找到“bug”。

對於您的draggable腳本,我將代碼更改為此,添加margin-leftmargin-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 + ')'
    });
}

然後,對於您的droppable腳本,我更改了element_topelement_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)

它給出了“幾乎”準確的結果,但您可以進一步調整它以進行拋光。 希望這可以幫助。


看起來是什麼導致這看起來很奇怪如下:

首先,小div的樣式為display: block 。 這意味著即使它看起來像div很小,該元素實際上延伸到它的整個容器。

其次,一旦在左側屏幕上顯示拖動的方塊,鼠標光標和元素整體之間的關係在技術上居中,但是您將原始元素的大小切割為較小的一個,並且當寬度和高度減小時,結果將使用從原始div的左上角開始的新寬度和高度進行渲染。 (如果你設置小按鈕的樣式display: inline ,你可以看到我的意思。嘗試從左上角抓住它並嘗試右下角。你會看到前者看起來很好,但後者是關閉的) 。

所以我的建議是:

  1. 使draggabble元素display: inline
  2. 使左側屏幕上的拖動元素成為右側屏幕上原始元素的精確高度和寬度。

希望有所幫助!





jquery-ui-droppable