使用CSS3的“jQuery定位”transform:scale“ [英] jQuery positioning with CSS3 "transform: scale"

查看:419
本文介绍了使用CSS3的“jQuery定位”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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆