使用CSS3进行jQuery定位"转换:scale"

时间:2016-10-22 17:23:44

标签: javascript jquery css3 css-transforms

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;
&#13;
&#13;

根节点必须缩放,因为在我的真实应用程序中我使用全屏模式,我需要使内容适合窗口分辨率。 但是当我缩放父元素时,jQuery UI可拖动和jQuery位置无法正常工作。

当然问题是如何让它正常工作?

有许多类似的问题,但我找不到合适的答案。

2 个答案:

答案 0 :(得分:3)

我按this answer调整了Martii Laine以说明收容和双击定位:

&#13;
&#13;
$(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;
&#13;
&#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>