jQuery在CSS"转换:scale()" (但是" transform:translate()"它工作正常)
请看一下这个简单的例子:
$(document).ready(function() {
$('#root').dblclick(function() {
$('#box').position({
my: 'right bottom',
at: 'right bottom',
of: $('#root')
});
})
$('#box').draggable({
containment: $('#root'),
});
});

body {
position: relative;
margin: 0;
}
#root {
position: absolute;
top: 20px;
left: 20px;
width: 500px;
height: 500px;
border: solid 2px red;
transform-origin: 0 0 0;
transform: scale(0.5);
}
#box {
position: absolute;
top: 100px;
left: 50px;
display: inline-block;
width: 50px;
height: 50px;
background: red;
border: solid 1px black;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
drag red box :)
<br/>double click in square to position box
<div id="root">
<div id="box"></div>
</div>
&#13;
根节点必须缩放,因为在我的真实应用程序中我使用全屏模式,我需要使内容适合窗口分辨率。 但是当我缩放父元素时,jQuery UI可拖动和jQuery位置无法正常工作。
当然问题是如何让它正常工作?
有许多类似的问题,但我找不到合适的答案。
答案 0 :(得分:3)
我按this answer调整了Martii Laine以说明收容和双击定位:
$(document).ready(function () {
var $root = $('#root');
var $box = $('#box');
var minLeft = parseFloat($root.css("paddingLeft"));
var minTop = parseFloat($root.css("paddingTop"));
var maxLeft = minLeft + $root.width() - $box.outerWidth();
var maxTop = minTop + $root.height() - $box.outerHeight();
$root.dblclick(function () {
$box.css({
left: maxLeft,
top: maxTop
});
})
var zoom = 0.5;
var click = { x: 0, y: 0 };
$box.draggable({
start: function (event) {
click.x = event.clientX;
click.y = event.clientY;
},
drag: function (event, ui) {
var original = ui.originalPosition;
var left = (event.clientX - click.x + original.left) / zoom;
var top = (event.clientY - click.y + original.top) / zoom;
ui.position = {
left: Math.max(minLeft, Math.min(maxLeft, left)),
top: Math.max(minTop, Math.min(maxTop, top))
};
}
});
});
&#13;
body
{
position: relative;
margin: 0;
}
#root
{
position: absolute;
top: 20px;
left: 20px;
width: 500px;
height: 500px;
border: solid 2px red;
transform-origin: 0 0 0;
transform: scale(0.5);
}
#box
{
position: absolute;
top: 100px;
left: 50px;
display: inline-block;
width: 50px;
height: 50px;
background: red;
border: solid 1px black;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
drag red box :)
<br/>double click in square to position box
<div id="root">
<div id="box"></div>
</div>
&#13;
如果在根div中双击时需要其他对齐,则代码为:
$root.dblclick(function () {
$box.css({
left: maxLeft,
top: maxTop
});
})
可以改编如下:
left: minLeft, // Left aligned
left: maxLeft, // Right aligned
left: (minLeft + maxLeft) / 2, // Centered (horizontally)
top: minTop, // At the top
top: maxTop, // At the Bottom
top: (minTop + maxTop) / 2, // Centered (vertically)
答案 1 :(得分:0)
我将内容放入iframe,然后在iframe上应用转换 - 它正常工作:)
编辑:我的解决方案的早期版本变成了错误。但是,如果有人想检查它,我保存了代码段
/*
* jQuery UI FIX
* Take focus on window.transformScale
*/
/*
* Offset fix
*/
(function() {
function getWindow(elem) { return jQuery.isWindow(elem) ? elem : elem.nodeType === 9 && elem.defaultView; }
jQuery.fn.offset = function( options ) {
// Preserve chaining for setter
if ( arguments.length ) {
return options === undefined ?
this :
this.each( function( i ) {
jQuery.offset.setOffset( this, options, i );
} );
}
var docElem, win, rect, doc,
elem = this[ 0 ];
if ( !elem ) {
return;
}
// Support: IE <=11 only
// Running getBoundingClientRect on a
// disconnected node in IE throws an error
if ( !elem.getClientRects().length ) {
return { top: 0, left: 0 };
}
var transform = $(document.body).css('transform');
$(document.body).css('transform', 'none');
rect = elem.getBoundingClientRect();
$(document.body).css('transform', transform);
// Make sure element is not hidden (display: none)
if ( rect.width || rect.height ) {
doc = elem.ownerDocument;
win = getWindow( doc );
docElem = doc.documentElement;
return {
top: rect.top + (win.pageYOffset - docElem.clientTop) / window.transformScale,
left: rect.left + (win.pageXOffset - docElem.clientLeft) / window.transformScale,
};
}
// Return zeros for disconnected and hidden elements (gh-2310)
return rect;
};
})();
/*
* Position fix
*/
(function() {
var cachedScrollbarWidth,
max = Math.max,
abs = Math.abs,
rhorizontal = /left|center|right/,
rvertical = /top|center|bottom/,
roffset = /[\+\-]\d+(\.[\d]+)?%?/,
rposition = /^\w+/,
rpercent = /%$/,
_position = $.fn.position;
function getOffsets( offsets, width, height ) {
return [
parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
];
}
function parseCss( element, property ) {
return parseInt( $.css( element, property ), 10 ) || 0;
}
function getDimensions( elem ) {
var raw = elem[ 0 ];
if ( raw.nodeType === 9 ) {
return {
width: elem.width() / window.transformScale,
height: elem.height() / window.transformScale,
offset: { top: 0, left: 0 }
};
}
if ( $.isWindow( raw ) ) {
return {
width: elem.width() / window.transformScale,
height: elem.height() / window.transformScale,
offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
};
}
if ( raw.preventDefault ) {
return {
width: 0,
height: 0,
offset: { top: raw.pageY, left: raw.pageX }
};
}
return {
width: elem.outerWidth() / window.transformScale,
height: elem.outerHeight() / window.transformScale,
offset: elem.offset()
};
}
jQuery.fn.position = function( options ) {
if ( !options || !options.of ) {
return _position.apply( this, arguments );
}
// Make a copy, we don't want to modify arguments
options = $.extend( {}, options );
var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
target = $( options.of ),
within = $.position.getWithinInfo( options.within ),
scrollInfo = $.position.getScrollInfo( within ),
collision = ( options.collision || "flip" ).split( " " ),
offsets = {};
dimensions = getDimensions( target );
if ( target[ 0 ].preventDefault ) {
// Force left top to allow flipping
options.at = "left top";
}
targetWidth = dimensions.width;
targetHeight = dimensions.height;
targetOffset = dimensions.offset;
// Clone to reuse original targetOffset later
basePosition = $.extend( {}, targetOffset );
// Force my and at to have valid horizontal and vertical positions
// if a value is missing or invalid, it will be converted to center
$.each( [ "my", "at" ], function() {
var pos = ( options[ this ] || "" ).split( " " ),
horizontalOffset,
verticalOffset;
if ( pos.length === 1 ) {
pos = rhorizontal.test( pos[ 0 ] ) ?
pos.concat( [ "center" ] ) :
rvertical.test( pos[ 0 ] ) ?
[ "center" ].concat( pos ) :
[ "center", "center" ];
}
pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
// Calculate offsets
horizontalOffset = roffset.exec( pos[ 0 ] );
verticalOffset = roffset.exec( pos[ 1 ] );
offsets[ this ] = [
horizontalOffset ? horizontalOffset[ 0 ] : 0,
verticalOffset ? verticalOffset[ 0 ] : 0
];
// Reduce to just the positions without the offsets
options[ this ] = [
rposition.exec( pos[ 0 ] )[ 0 ],
rposition.exec( pos[ 1 ] )[ 0 ]
];
} );
// Normalize collision option
if ( collision.length === 1 ) {
collision[ 1 ] = collision[ 0 ];
}
if ( options.at[ 0 ] === "right" ) {
basePosition.left += targetWidth;
} else if ( options.at[ 0 ] === "center" ) {
basePosition.left += targetWidth / 2;
}
if ( options.at[ 1 ] === "bottom" ) {
basePosition.top += targetHeight;
} else if ( options.at[ 1 ] === "center" ) {
basePosition.top += targetHeight / 2;
}
atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
basePosition.left += atOffset[ 0 ];
basePosition.top += atOffset[ 1 ];
return this.each( function() {
var collisionPosition, using,
elem = $( this ),
elemWidth = elem.outerWidth() / window.transformScale,
elemHeight = elem.outerHeight() / window.transformScale,
marginLeft = parseCss( this, "marginLeft" ),
marginTop = parseCss( this, "marginTop" ),
collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) +
scrollInfo.width,
collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) +
scrollInfo.height,
position = $.extend( {}, basePosition ),
myOffset = getOffsets( offsets.my, elem.outerWidth() / window.transformScale, elem.outerHeight() / window.transformScale );
if ( options.my[ 0 ] === "right" ) {
position.left -= elemWidth;
} else if ( options.my[ 0 ] === "center" ) {
position.left -= elemWidth / 2;
}
if ( options.my[ 1 ] === "bottom" ) {
position.top -= elemHeight;
} else if ( options.my[ 1 ] === "center" ) {
position.top -= elemHeight / 2;
}
position.left += myOffset[ 0 ];
position.top += myOffset[ 1 ];
collisionPosition = {
marginLeft: marginLeft,
marginTop: marginTop
};
$.each( [ "left", "top" ], function( i, dir ) {
if ( jQuery.ui.position[ collision[ i ] ] ) {
jQuery.ui.position[ collision[ i ] ][ dir ]( position, {
targetWidth: targetWidth,
targetHeight: targetHeight,
elemWidth: elemWidth,
elemHeight: elemHeight,
collisionPosition: collisionPosition,
collisionWidth: collisionWidth,
collisionHeight: collisionHeight,
offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
my: options.my,
at: options.at,
within: within,
elem: elem
} );
}
} );
if ( options.using ) {
// Adds feedback as second argument to using callback, if present
using = function( props ) {
var left = targetOffset.left - position.left,
right = left + targetWidth - elemWidth,
top = targetOffset.top - position.top,
bottom = top + targetHeight - elemHeight,
feedback = {
target: {
element: target,
left: targetOffset.left,
top: targetOffset.top,
width: targetWidth,
height: targetHeight
},
element: {
element: elem,
left: position.left,
top: position.top,
width: elemWidth,
height: elemHeight
},
horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
};
if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
feedback.horizontal = "center";
}
if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
feedback.vertical = "middle";
}
if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
feedback.important = "horizontal";
} else {
feedback.important = "vertical";
}
options.using.call( this, props, feedback );
};
}
elem.offset( $.extend( position, { using: using } ) );
} );
};
})();
/*
* Draggable fix
*/
(function() {
jQuery.ui.draggable.prototype._refreshOffsets = function( event ) {
this.offset = {
top: this.positionAbs.top - this.margins.top,
left: this.positionAbs.left - this.margins.left,
scroll: false,
parent: this._getParentOffset(),
relative: this._getRelativeOffset()
};
this.offset.click = {
left: event.pageX / window.transformScale - this.offset.left,
top: event.pageY / window.transformScale - this.offset.top
};
};
jQuery.ui.draggable.prototype._generatePosition = function( event, constrainPosition ) {
var containment, co, top, left,
o = this.options,
scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ),
pageX = event.pageX / window.transformScale,
pageY = event.pageY / window.transformScale;
// Cache the scroll
if ( !scrollIsRootNode || !this.offset.scroll ) {
this.offset.scroll = {
top: this.scrollParent.scrollTop(),
left: this.scrollParent.scrollLeft()
};
}
/*
* - Position constraining -
* Constrain the position to a mix of grid, containment.
*/
// If we are not dragging yet, we won't check for options
if ( constrainPosition ) {
if ( this.containment ) {
if ( this.relativeContainer ) {
co = this.relativeContainer.offset();
containment = [
this.containment[ 0 ] + co.left,
this.containment[ 1 ] + co.top,
this.containment[ 2 ] + co.left,
this.containment[ 3 ] + co.top
];
} else {
containment = this.containment;
}
var width = 0;
var height = 0;
if(window.transformScale != 1)
{
var width = this.helper.outerWidth();
var height = this.helper.outerHeight();
}
if ( pageX - this.offset.click.left < containment[ 0 ] ) {
pageX = containment[ 0 ] + this.offset.click.left;
}
if ( pageY - this.offset.click.top < containment[ 1 ] ) {
pageY = containment[ 1 ] + this.offset.click.top;
}
if ( pageX - this.offset.click.left + width > containment[ 2 ] ) {
pageX = containment[ 2 ] + this.offset.click.left - width;
}
if ( pageY - this.offset.click.top + height > containment[ 3 ] ) {
pageY = containment[ 3 ] + this.offset.click.top - height;
}
}
if ( o.grid ) {
//Check for grid elements set to 0 to prevent divide by 0 error causing invalid
// argument errors in IE (see ticket #6950)
top = o.grid[ 1 ] ? this.originalPageY + Math.round( ( pageY -
this.originalPageY ) / o.grid[ 1 ] ) * o.grid[ 1 ] : this.originalPageY;
pageY = containment ? ( ( top - this.offset.click.top >= containment[ 1 ] ||
top - this.offset.click.top > containment[ 3 ] ) ?
top :
( ( top - this.offset.click.top >= containment[ 1 ] ) ?
top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : top;
left = o.grid[ 0 ] ? this.originalPageX +
Math.round( ( pageX - this.originalPageX ) / o.grid[ 0 ] ) * o.grid[ 0 ] :
this.originalPageX;
pageX = containment ? ( ( left - this.offset.click.left >= containment[ 0 ] ||
left - this.offset.click.left > containment[ 2 ] ) ?
left :
( ( left - this.offset.click.left >= containment[ 0 ] ) ?
left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : left;
}
if ( o.axis === "y" ) {
pageX = this.originalPageX;
}
if ( o.axis === "x" ) {
pageY = this.originalPageY;
}
}
return {
top: (
// The absolute mouse position
pageY -
// Click offset (relative to the element)
this.offset.click.top -
// Only for relative positioned nodes: Relative offset from element to offset parent
this.offset.relative.top -
// The offsetParent's offset without borders (offset + border)
this.offset.parent.top +
( this.cssPosition === "fixed" ?
-this.offset.scroll.top :
( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
),
left: (
// The absolute mouse position
pageX -
// Click offset (relative to the element)
this.offset.click.left -
// Only for relative positioned nodes: Relative offset from element to offset parent
this.offset.relative.left -
// The offsetParent's offset without borders (offset + border)
this.offset.parent.left +
( this.cssPosition === "fixed" ?
-this.offset.scroll.left :
( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
)
};
};
jQuery.ui.draggable.prototype._mouseStart = function( event ) {
var o = this.options;
//Create and append the visible helper
this.helper = this._createHelper( event );
this._addClass( this.helper, "ui-draggable-dragging" );
//Cache the helper size
this._cacheHelperProportions();
//If ddmanager is used for droppables, set the global draggable
if ( jQuery.ui.ddmanager ) {
jQuery.ui.ddmanager.current = this;
}
/*
* - Position generation -
* This block generates everything position related - it's the core of draggables.
*/
//Cache the margins of the original element
this._cacheMargins();
//Store the helper's css position
this.cssPosition = this.helper.css( "position" );
this.scrollParent = this.helper.scrollParent( true );
this.offsetParent = this.helper.offsetParent();
this.hasFixedAncestor = this.helper.parents().filter( function() {
return $( this ).css( "position" ) === "fixed";
} ).length > 0;
//The element's absolute position on the page minus margins
this.positionAbs = this.element.offset();
this._refreshOffsets( event );
//Generate the original position
this.originalPosition = this.position = this._generatePosition( event, false );
this.originalPageX = event.pageX / window.transformScale;
this.originalPageY = event.pageY / window.transformScale;
//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) );
//Set a containment if given in the options
this._setContainment();
//Trigger event + callbacks
if ( this._trigger( "start", event ) === false ) {
this._clear();
return false;
}
//Recache the helper size
this._cacheHelperProportions();
//Prepare the droppable offsets
if ( jQuery.ui.ddmanager && !o.dropBehaviour ) {
jQuery.ui.ddmanager.prepareOffsets( this, event );
}
// Execute the drag once - this causes the helper not to be visible before getting its
// correct position
this._mouseDrag( event, true );
// If the ddmanager is used for droppables, inform the manager that dragging has started
// (see #5003)
if ( jQuery.ui.ddmanager ) {
jQuery.ui.ddmanager.dragStart( this, event );
}
return true;
};
jQuery.ui.draggable.prototype._mouseDrag = function( event, noPropagation ) {
// reset any necessary cached properties (see #5009)
if ( this.hasFixedAncestor ) {
this.offset.parent = this._getParentOffset();
}
//Compute the helpers position
this.position = this._generatePosition( event, true );
this.positionAbs = this._convertPositionTo( "absolute" );
//Call plugins and callbacks and use the resulting position if something is returned
if ( !noPropagation ) {
var ui = this._uiHash();
if ( this._trigger( "drag", event, ui ) === false ) {
this._mouseUp( new $.Event( "mouseup", event ) );
return false;
}
this.position = ui.position;
}
this.helper[ 0 ].style.left = this.position.left + "px";
this.helper[ 0 ].style.top = this.position.top + "px";
if ( jQuery.ui.ddmanager ) {
jQuery.ui.ddmanager.drag( this, event );
}
return false;
};
})();
body {
position: relative;
margin: 0;
transform-origin: 0 0 0;
}
#root {
position: fixed;
top: 20px;
left: 20px;
width: 500px;
height: 500px;
border: solid 2px green;
}
#box {
position: absolute;
top: 100px;
left: 50px;
display: inline-block;
width: 50px;
height: 50px;
background: yellow;
border: solid 2px black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script>
/*
* EXAMPLE CODE
*/
// this variable is required to make extension work
window.transformScale = 0.5;
$(document).ready(function() {
$(document.body).attr('style', 'transform: scale('+ window.transformScale +')');
$('#root').dblclick(function() {
$('#box').position({
my: 'right bottom',
at: 'right bottom',
of: $('#root')
});
})
$('#box').draggable({
containment: $('#root'),
});
});
</script>
<div id="root">
<div id="box"></div>
</div>