准确降低可缩放div上的可拖动元素

问题

我有一个小问题拖动元素到一个可伸缩的div容器。

一旦元素实际上在容器中,元素就会很好的拖拽,并按照它们应该的方式工作。

拖到可伸缩容器上的较大元素没有太多问题。

但是,当拖动较小的元素时,可以看到鼠标不再附加到所述元素上,并且当它被放下时,它会掉落一点。

我正试图find一个解决scheme,我的鼠标停留在元素上,并且掉落在应该放置的地方。

我已经解决了一点一滴的问题,你可以在下面看到,但这是让我疯狂的难题的最后一块。 如果有人有时间伸出援助之手,将不胜感激。

这是一个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 }); } }; 

使元素在缩放的容器上可以resize

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

创build一个可拖动的元素,我可以拖动到框中。

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

创build可拖动/可resize的元素,可能已经在框中并对这些元素应用拖动/resize的修复

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

对于拖动时的光标位置,请参阅此答案: 使jquery-ui可拖动方法中的ui.helper的中心为光标位置

基本上,你可以控制实例的光标位置,允许有更多的dynamic的cursorAt 。 喜欢这个:

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

然后在下拉菜单中 ,您需要考虑到变换,但是可以使用辅助坐标而不是可拖动来简化。 喜欢这个:

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

结果: https : //codepen.io/anon/pen/jamLBq

看起来是什么造成这看起来奇怪是以下几点:

首先,小div被devise为display: block 。 这意味着,即使div看起来很小,这个元素实际上也延伸到整个容器。

第二,一旦你在左边的屏幕上显示拖动的方块,鼠标光标和整个元素之间的关系在技术上是居中的,但是你将原始元素的尺寸缩小到一个更小的尺寸,并且当宽度和高度减小,结果将从原始div的左上angular开始以新的宽度和高度呈现。 (如果你将小button设置为display: inline ,你可以看到我的意思,试着从左上angular抓住它,尝试右下angular的那个,你会发现前者看起来不错,但后者是closures的) 。

所以我的build议是:

  1. 使可拖动元素display: inline
  2. 使左侧屏幕上的拖动元素具有右侧屏幕上原始元素的确切高度和宽度。

希望有所帮助!

我已经分叉你的codepen和玩弄它。

看看这里 ,看看它是否有助于你find“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) 

它给出了一个“几乎”准确的结果,但是你可以进一步调整它来抛光它。 希望这可以帮助。

在拖动时用光标附加元素只需要使用

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

并在“sm_textbox_item”的顶部和左侧参数稍微改变。

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

对于大的盒子,还需要在顶部和左侧元素进行一些调整(笔更新)。

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

我叉了你的笔,做了一些改变。 我知道这不是一个完美的解决scheme,我也试图解决这个点点滴滴。 你可以在这里查看

@ITWitch是正确的,在draggable()必须有一些错误。 样式margin: 0 auto;#sm_textbox_item是问题的根源。

尝试在draggableType指令中将其添加到可拖动选项以更正位置:

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

transform添加到元素的样式,然后使其可拖动时会发生此问题。 你将不得不做任何transform以获得完美的结果。 我花了2天时间debugging,直到我发现了,我不想让别人去经历那种痛苦。