javascript - attribute - Caída precisa para el elemento arrastrable en div escalado
style html (6)
@ITWitch tiene razón, tiene que haber algún error en draggable()
. margin: 0 auto;
estilo margin: 0 auto;
En #sm_textbox_item
es la fuente del problema.
Intente agregar esto a las opciones arrastrables en su directiva draggableType para corregir la posición:
cursorAt: {left: -parseInt(window.getComputedStyle(element[0],null,null)[''margin-left''])},
EL PROBLEMA
Tengo un problema menor al arrastrar elementos a un contenedor div escalable.
Una vez que el elemento está realmente en el contenedor, los elementos se arrastran bien y funcionan como deben.
Los elementos más grandes que se arrastran al contenedor escalable no tienen demasiado problema.
Pero cuando se arrastran elementos más pequeños, puede ver que el mouse ya no está unido a dicho elemento y cuando se suelta, cae un poco donde se supone que debe caer.
Estoy tratando de encontrar una solución para que mi mouse permanezca en el elemento y caiga donde se supone que debe caer.
He resuelto los problemas poco a poco y puedes verlos a continuación, pero esta es la última pieza del rompecabezas que me está volviendo loco. Si alguien tiene el tiempo para echar una mano, sería muy apreciado.
Aquí hay un codepen: haga clic y arrastre los dos elementos azules al contenedor blanco para probarlo
Codepen
Vista de pantalla completa
GIF corto en acción
Esto ayudará a asegurarse de que el área que se puede soltar funciona con un contenedor a escala.
$.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
});
}
};
Habilitar el elemento para ser redimensionable en un contenedor escalado
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;
}
Lo hace tan arrastrado trabaja en un contenedor escalado
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()));
}
Creación de un elemento que se puede arrastrar y que puedo arrastrar al cuadro.
.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 + '')''});
}
});
}
};
})
Cree elementos que se puedan arrastrar / cambiar de tamaño que ya se encuentren en el cuadro y aplique la solución de arrastrar / redimensionar a estos
.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();
}
})
}
};
})
¿Qué sucede cuando se cae un artículo?
.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();
}
});
}
};
})
Por último, pero no menos importante, mi controlador.
.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 } }
});
Aquí está la base de mi 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>
Bifurqué tu codepen y jugué un poco con él.
Míralo AQUÍ , y ve si te ayuda a encontrar el "error".
Para su secuencia de comandos que se puede draggable
, cambié el código a esto, agregando margin-left
y 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 + '')''
});
}
Luego, para su secuencia de comandos droppable
, cambié la fórmula de element_top
y 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)
Da un resultado "casi" preciso, pero es posible que pueda modificarlo aún más para pulirlo. Espero que esto ayude.
Este problema se produce cuando agrega una transform
al estilo de un elemento, luego lo hace arrastrable. Tendrás que hacer sin transform
para tener un resultado perfecto. Pasé 2 días depurando hasta que lo descubrí, y no quería que otra persona sufriera ese dolor.
Para adjuntar elementos con el cursor durante el arrastre, solo necesita usar
cursorAt: { top: 6, left: -100 }
Y un pequeño cambio en los parámetros superior e izquierdo de "sm_textbox_item".
top: (y - documentBg.offset().top) / (percent) + "px",
left: (x - documentBg.offset().left) / (percent) + "px",
Para la caja grande de nuevo, se requieren algunos ajustes en el elemento superior e izquierdo (lápiz actualizado).
top: element_top-3, left: element_left+6.49,
Tuve tu bolígrafo e hice algunos cambios. Sé que esta no es una solución perfecta, también estoy tratando de resolver esto poco a poco. Puedes comprobarlo aquí
Para conocer la posición del cursor mientras arrastra, vea esta respuesta: Coloque la posición del cursor en el centro para ui.helper en el método arrastrable jquery-ui
Básicamente, puedes controlar la posición del cursor de la instancia, lo que permite tener algo más dinámico que cursorAt . Me gusta esto:
start: function(event, ui){
$(this).draggable(''instance'').offset.click = {
left: Math.floor(ui.helper.width() / 2),
top: Math.floor(ui.helper.height() / 2)
}
},
Luego, en la caída , debe tener en cuenta la transformación, pero puede simplificar utilizando las coordenadas auxiliares en lugar de la que se puede arrastrar . Me gusta esto:
element_top = (ui.helper.offset().top / percent) - (documentBg.offset().top / percent);
element_left = (ui.helper.offset().left / percent) - (documentBg.offset().left / percent);
Resultado: https://codepen.io/anon/pen/jamLBq
Parece que lo que hace que esto parezca extraño es lo siguiente:
En primer lugar, el div pequeño es estilo como display: block
. Esto significa que aunque parece que el div es pequeño, ese elemento en realidad se extiende a todo el contenedor.
En segundo lugar, una vez que muestra el cuadrado arrastrado en la pantalla izquierda, la relación entre el cursor del mouse y el elemento completo se centra técnicamente, pero está reduciendo el tamaño del elemento original a uno más pequeño, y cuando el ancho y la altura disminuyen , el resultado se representa con el nuevo ancho y alto a partir de la esquina superior izquierda del div original. (Si diseña el botón pequeño para que se display: inline
, puede ver lo que quiero decir. Trate de agarrarlo desde la esquina superior izquierda y pruebe el botón inferior derecho. Verá que el primero se ve bien pero el último está apagado) .
Así que mis sugerencias son:
- Hacer que los elementos de arrastre se
display: inline
- Haga que el elemento arrastrado en la pantalla izquierda tenga el alto y ancho exactos del elemento original en la pantalla derecha.
¡Espero que ayude!