使用CSS3的“jQuery定位”transform:scale“ [英] jQuery positioning with CSS3 "transform: scale"
问题描述
jQuery不能很好地与CSStransform:scale()
(但是transform:translate()工作正常)
jQuery doesn't work well with CSS "transform: scale()" (however with "transform: translate()" it works fine)
请看看这个简单的例子:
Please take a look at this simple example:
$(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>
根节点必须缩放,因为在我的真实应用程序我使用全屏模式,我需要适合内容窗口分辨率。
但是当我缩放父元素时,jQuery UI draggable和jQuery位置不能正常工作。
Root node has to be scaled, becouse in my real app I use fullscreen mode and I need to fit content to window resolution. But when I scale parent element, jQuery UI draggable and jQuery position doesn't work properly.
当然问题是如何使它正常工作?
Of course the question is how to make it work properly?
有很多类似的问题,但我没有找到正确的答案。
There are many similar question, but I didn't find proper answer.
推荐答案
我将我的内容放入iframe,并对iframe应用了转换 - 它可以正常工作:)
I put my content into iframe, and I applied transformation on iframe - it works properly :)
EDIT :的我的解决方案变成了越野车。但是如果有人想检查它,我保存的代码片
EDIT: Previous version of my solution turn out to be buggy. But if someone would like to check it I save the snippet
/*
* 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>
这篇关于使用CSS3的“jQuery定位”transform:scale“的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!