/*
* 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) {
\t // Preserve chaining for setter
\t if (arguments.length) {
\t \t return options === undefined ?
\t \t \t this :
\t \t \t this.each(function(i) {
\t \t \t \t jQuery.offset.setOffset(this, options, i);
\t \t \t });
\t }
\t var docElem, win, rect, doc,
\t \t elem = this[ 0 ];
\t if (!elem) {
\t \t return;
\t }
\t // Support: IE <=11 only
\t // Running getBoundingClientRect on a
\t // disconnected node in IE throws an error
\t if (!elem.getClientRects().length) {
\t \t return { top: 0, left: 0 };
\t }
\t var transform = $(document.body).css('transform');
\t $(document.body).css('transform', 'none');
\t rect = elem.getBoundingClientRect();
\t $(document.body).css('transform', transform);
\t // Make sure element is not hidden (display: none)
\t if (rect.width || rect.height) {
\t \t doc = elem.ownerDocument;
\t \t win = getWindow(doc);
\t \t docElem = doc.documentElement;
\t \t return {
\t \t \t top: rect.top + (win.pageYOffset - docElem.clientTop)/window.transformScale,
\t \t \t left: rect.left + (win.pageXOffset - docElem.clientLeft)/window.transformScale,
\t \t };
\t }
\t // Return zeros for disconnected and hidden elements (gh-2310)
\t return rect;
};
})();
/*
* Position fix
*/
(function() {
var cachedScrollbarWidth,
\t max = Math.max,
\t abs = Math.abs,
\t rhorizontal = /left|center|right/,
\t rvertical = /top|center|bottom/,
\t roffset = /[\+\-]\d+(\.[\d]+)?%?/,
\t rposition = /^\w+/,
\t rpercent = /%$/,
\t _position = $.fn.position;
function getOffsets(offsets, width, height) {
\t return [
\t \t parseFloat(offsets[ 0 ]) * (rpercent.test(offsets[ 0 ]) ? width/100 : 1),
\t \t parseFloat(offsets[ 1 ]) * (rpercent.test(offsets[ 1 ]) ? height/100 : 1)
\t ];
}
function parseCss(element, property) {
\t return parseInt($.css(element, property), 10) || 0;
}
function getDimensions(elem) {
\t var raw = elem[ 0 ];
\t if (raw.nodeType === 9) {
\t \t return {
\t \t \t width: elem.width()/window.transformScale,
\t \t \t height: elem.height()/window.transformScale,
\t \t \t offset: { top: 0, left: 0 }
\t \t };
\t }
\t if ($.isWindow(raw)) {
\t \t return {
\t \t \t width: elem.width()/window.transformScale,
\t \t \t height: elem.height()/window.transformScale,
\t \t \t offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
\t \t };
\t }
\t if (raw.preventDefault) {
\t \t return {
\t \t \t width: 0,
\t \t \t height: 0,
\t \t \t offset: { top: raw.pageY, left: raw.pageX }
\t \t };
\t }
\t return {
\t \t width: elem.outerWidth()/window.transformScale,
\t \t height: elem.outerHeight()/window.transformScale,
\t \t offset: elem.offset()
\t };
}
jQuery.fn.position = function(options) {
\t if (!options || !options.of) {
\t \t return _position.apply(this, arguments);
\t }
\t // Make a copy, we don't want to modify arguments
\t options = $.extend({}, options);
\t var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
\t \t target = $(options.of),
\t \t within = $.position.getWithinInfo(options.within),
\t \t scrollInfo = $.position.getScrollInfo(within),
\t \t collision = (options.collision || "flip").split(" "),
\t \t offsets = {};
\t dimensions = getDimensions(target);
\t if (target[ 0 ].preventDefault) {
\t \t // Force left top to allow flipping
\t \t options.at = "left top";
\t }
\t targetWidth = dimensions.width;
\t targetHeight = dimensions.height;
\t targetOffset = dimensions.offset;
\t // Clone to reuse original targetOffset later
\t basePosition = $.extend({}, targetOffset);
\t // Force my and at to have valid horizontal and vertical positions
\t // if a value is missing or invalid, it will be converted to center
\t $.each([ "my", "at" ], function() {
\t \t var pos = (options[ this ] || "").split(" "),
\t \t \t horizontalOffset,
\t \t \t verticalOffset;
\t \t if (pos.length === 1) {
\t \t \t pos = rhorizontal.test(pos[ 0 ]) ?
\t \t \t \t pos.concat([ "center" ]) :
\t \t \t \t rvertical.test(pos[ 0 ]) ?
\t \t \t \t \t [ "center" ].concat(pos) :
\t \t \t \t \t [ "center", "center" ];
\t \t }
\t \t pos[ 0 ] = rhorizontal.test(pos[ 0 ]) ? pos[ 0 ] : "center";
\t \t pos[ 1 ] = rvertical.test(pos[ 1 ]) ? pos[ 1 ] : "center";
\t \t // Calculate offsets
\t \t horizontalOffset = roffset.exec(pos[ 0 ]);
\t \t verticalOffset = roffset.exec(pos[ 1 ]);
\t \t offsets[ this ] = [
\t \t \t horizontalOffset ? horizontalOffset[ 0 ] : 0,
\t \t \t verticalOffset ? verticalOffset[ 0 ] : 0
\t \t ];
\t \t // Reduce to just the positions without the offsets
\t \t options[ this ] = [
\t \t \t rposition.exec(pos[ 0 ])[ 0 ],
\t \t \t rposition.exec(pos[ 1 ])[ 0 ]
\t \t ];
\t });
\t // Normalize collision option
\t if (collision.length === 1) {
\t \t collision[ 1 ] = collision[ 0 ];
\t }
\t if (options.at[ 0 ] === "right") {
\t \t basePosition.left += targetWidth;
\t } else if (options.at[ 0 ] === "center") {
\t \t basePosition.left += targetWidth/2;
\t }
\t if (options.at[ 1 ] === "bottom") {
\t \t basePosition.top += targetHeight;
\t } else if (options.at[ 1 ] === "center") {
\t \t basePosition.top += targetHeight/2;
\t }
\t atOffset = getOffsets(offsets.at, targetWidth, targetHeight);
\t basePosition.left += atOffset[ 0 ];
\t basePosition.top += atOffset[ 1 ];
\t return this.each(function() {
\t \t var collisionPosition, using,
\t \t \t elem = $(this),
\t \t \t elemWidth = elem.outerWidth()/window.transformScale,
\t \t \t elemHeight = elem.outerHeight()/window.transformScale,
\t \t \t marginLeft = parseCss(this, "marginLeft"),
\t \t \t marginTop = parseCss(this, "marginTop"),
\t \t \t collisionWidth = elemWidth + marginLeft + parseCss(this, "marginRight") +
\t \t \t \t scrollInfo.width,
\t \t \t collisionHeight = elemHeight + marginTop + parseCss(this, "marginBottom") +
\t \t \t \t scrollInfo.height,
\t \t \t position = $.extend({}, basePosition),
\t \t \t myOffset = getOffsets(offsets.my, elem.outerWidth()/window.transformScale, elem.outerHeight()/window.transformScale);
\t \t if (options.my[ 0 ] === "right") {
\t \t \t position.left -= elemWidth;
\t \t } else if (options.my[ 0 ] === "center") {
\t \t \t position.left -= elemWidth/2;
\t \t }
\t \t if (options.my[ 1 ] === "bottom") {
\t \t \t position.top -= elemHeight;
\t \t } else if (options.my[ 1 ] === "center") {
\t \t \t position.top -= elemHeight/2;
\t \t }
\t \t position.left += myOffset[ 0 ];
\t \t position.top += myOffset[ 1 ];
\t \t collisionPosition = {
\t \t \t marginLeft: marginLeft,
\t \t \t marginTop: marginTop
\t \t };
\t \t $.each([ "left", "top" ], function(i, dir) {
\t \t \t if (jQuery.ui.position[ collision[ i ] ]) {
\t \t \t \t jQuery.ui.position[ collision[ i ] ][ dir ](position, {
\t \t \t \t \t targetWidth: targetWidth,
\t \t \t \t \t targetHeight: targetHeight,
\t \t \t \t \t elemWidth: elemWidth,
\t \t \t \t \t elemHeight: elemHeight,
\t \t \t \t \t collisionPosition: collisionPosition,
\t \t \t \t \t collisionWidth: collisionWidth,
\t \t \t \t \t collisionHeight: collisionHeight,
\t \t \t \t \t offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
\t \t \t \t \t my: options.my,
\t \t \t \t \t at: options.at,
\t \t \t \t \t within: within,
\t \t \t \t \t elem: elem
\t \t \t \t });
\t \t \t }
\t \t });
\t \t if (options.using) {
\t \t \t // Adds feedback as second argument to using callback, if present
\t \t \t using = function(props) {
\t \t \t \t var left = targetOffset.left - position.left,
\t \t \t \t \t right = left + targetWidth - elemWidth,
\t \t \t \t \t top = targetOffset.top - position.top,
\t \t \t \t \t bottom = top + targetHeight - elemHeight,
\t \t \t \t \t feedback = {
\t \t \t \t \t \t target: {
\t \t \t \t \t \t \t element: target,
\t \t \t \t \t \t \t left: targetOffset.left,
\t \t \t \t \t \t \t top: targetOffset.top,
\t \t \t \t \t \t \t width: targetWidth,
\t \t \t \t \t \t \t height: targetHeight
\t \t \t \t \t \t },
\t \t \t \t \t \t element: {
\t \t \t \t \t \t \t element: elem,
\t \t \t \t \t \t \t left: position.left,
\t \t \t \t \t \t \t top: position.top,
\t \t \t \t \t \t \t width: elemWidth,
\t \t \t \t \t \t \t height: elemHeight
\t \t \t \t \t \t },
\t \t \t \t \t \t horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
\t \t \t \t \t \t vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
\t \t \t \t \t };
\t \t \t \t if (targetWidth < elemWidth && abs(left + right) < targetWidth) {
\t \t \t \t \t feedback.horizontal = "center";
\t \t \t \t }
\t \t \t \t if (targetHeight < elemHeight && abs(top + bottom) < targetHeight) {
\t \t \t \t \t feedback.vertical = "middle";
\t \t \t \t }
\t \t \t \t if (max(abs(left), abs(right)) > max(abs(top), abs(bottom))) {
\t \t \t \t \t feedback.important = "horizontal";
\t \t \t \t } else {
\t \t \t \t \t feedback.important = "vertical";
\t \t \t \t }
\t \t \t \t options.using.call(this, props, feedback);
\t \t \t };
\t \t }
\t \t elem.offset($.extend(position, { using: using }));
\t });
};
})();
/*
* Draggable fix
*/
(function() {
jQuery.ui.draggable.prototype._refreshOffsets = function(event) {
\t this.offset = {
\t \t top: this.positionAbs.top - this.margins.top,
\t \t left: this.positionAbs.left - this.margins.left,
\t \t scroll: false,
\t \t parent: this._getParentOffset(),
\t \t relative: this._getRelativeOffset()
\t };
\t this.offset.click = {
\t \t left: event.pageX/window.transformScale - this.offset.left,
\t \t top: event.pageY/window.transformScale - this.offset.top
\t };
};
jQuery.ui.draggable.prototype._generatePosition = function(event, constrainPosition) {
\t var containment, co, top, left,
\t \t o = this.options,
\t \t scrollIsRootNode = this._isRootNode(this.scrollParent[ 0 ]),
\t \t pageX = event.pageX/window.transformScale,
\t \t pageY = event.pageY/window.transformScale;
\t // Cache the scroll
\t if (!scrollIsRootNode || !this.offset.scroll) {
\t \t this.offset.scroll = {
\t \t \t top: this.scrollParent.scrollTop(),
\t \t \t left: this.scrollParent.scrollLeft()
\t \t };
\t }
\t /*
\t * - Position constraining -
\t * Constrain the position to a mix of grid, containment.
\t */
\t // If we are not dragging yet, we won't check for options
\t if (constrainPosition) {
\t \t if (this.containment) {
\t \t \t if (this.relativeContainer) {
\t \t \t \t co = this.relativeContainer.offset();
\t \t \t \t containment = [
\t \t \t \t \t this.containment[ 0 ] + co.left,
\t \t \t \t \t this.containment[ 1 ] + co.top,
\t \t \t \t \t this.containment[ 2 ] + co.left,
\t \t \t \t \t this.containment[ 3 ] + co.top
\t \t \t \t ];
\t \t \t } else {
\t \t \t \t containment = this.containment;
\t \t \t }
\t \t \t var width = 0;
\t \t \t var height = 0;
\t \t \t if(window.transformScale != 1)
\t \t \t {
\t \t \t \t var width = this.helper.outerWidth();
\t \t \t \t var height = this.helper.outerHeight();
\t \t \t }
\t \t \t if (pageX - this.offset.click.left < containment[ 0 ]) {
\t \t \t \t pageX = containment[ 0 ] + this.offset.click.left;
\t \t \t }
\t \t \t if (pageY - this.offset.click.top < containment[ 1 ]) {
\t \t \t \t pageY = containment[ 1 ] + this.offset.click.top;
\t \t \t }
\t \t \t if (pageX - this.offset.click.left + width > containment[ 2 ]) {
\t \t \t \t pageX = containment[ 2 ] + this.offset.click.left - width;
\t \t \t }
\t \t \t if (pageY - this.offset.click.top + height > containment[ 3 ]) {
\t \t \t \t pageY = containment[ 3 ] + this.offset.click.top - height;
\t \t \t }
\t \t }
\t \t if (o.grid) {
\t \t \t //Check for grid elements set to 0 to prevent divide by 0 error causing invalid
\t \t \t // argument errors in IE (see ticket #6950)
\t \t \t top = o.grid[ 1 ] ? this.originalPageY + Math.round((pageY -
\t \t \t \t this.originalPageY)/o.grid[ 1 ]) * o.grid[ 1 ] : this.originalPageY;
\t \t \t pageY = containment ? ((top - this.offset.click.top >= containment[ 1 ] ||
\t \t \t \t top - this.offset.click.top > containment[ 3 ]) ?
\t \t \t \t \t top :
\t \t \t \t \t ((top - this.offset.click.top >= containment[ 1 ]) ?
\t \t \t \t \t \t top - o.grid[ 1 ] : top + o.grid[ 1 ])) : top;
\t \t \t left = o.grid[ 0 ] ? this.originalPageX +
\t \t \t \t Math.round((pageX - this.originalPageX)/o.grid[ 0 ]) * o.grid[ 0 ] :
\t \t \t \t this.originalPageX;
\t \t \t pageX = containment ? ((left - this.offset.click.left >= containment[ 0 ] ||
\t \t \t \t left - this.offset.click.left > containment[ 2 ]) ?
\t \t \t \t \t left :
\t \t \t \t \t ((left - this.offset.click.left >= containment[ 0 ]) ?
\t \t \t \t \t \t left - o.grid[ 0 ] : left + o.grid[ 0 ])) : left;
\t \t }
\t \t if (o.axis === "y") {
\t \t \t pageX = this.originalPageX;
\t \t }
\t \t if (o.axis === "x") {
\t \t \t pageY = this.originalPageY;
\t \t }
\t }
\t return {
\t \t top: (
\t \t \t // The absolute mouse position
\t \t \t pageY -
\t \t \t // Click offset (relative to the element)
\t \t \t this.offset.click.top -
\t \t \t // Only for relative positioned nodes: Relative offset from element to offset parent
\t \t \t this.offset.relative.top -
\t \t \t // The offsetParent's offset without borders (offset + border)
\t \t \t this.offset.parent.top +
\t \t \t (this.cssPosition === "fixed" ?
\t \t \t \t -this.offset.scroll.top :
\t \t \t \t (scrollIsRootNode ? 0 : this.offset.scroll.top))
\t \t),
\t \t left: (
\t \t \t // The absolute mouse position
\t \t \t pageX -
\t \t \t // Click offset (relative to the element)
\t \t \t this.offset.click.left -
\t \t \t // Only for relative positioned nodes: Relative offset from element to offset parent
\t \t \t this.offset.relative.left -
\t \t \t // The offsetParent's offset without borders (offset + border)
\t \t \t this.offset.parent.left +
\t \t \t (this.cssPosition === "fixed" ?
\t \t \t \t -this.offset.scroll.left :
\t \t \t \t (scrollIsRootNode ? 0 : this.offset.scroll.left))
\t \t)
\t };
};
jQuery.ui.draggable.prototype._mouseStart = function(event) {
\t var o = this.options;
\t //Create and append the visible helper
\t this.helper = this._createHelper(event);
\t this._addClass(this.helper, "ui-draggable-dragging");
\t //Cache the helper size
\t this._cacheHelperProportions();
\t //If ddmanager is used for droppables, set the global draggable
\t if (jQuery.ui.ddmanager) {
\t \t jQuery.ui.ddmanager.current = this;
\t }
\t /*
\t * - Position generation -
\t * This block generates everything position related - it's the core of draggables.
\t */
\t //Cache the margins of the original element
\t this._cacheMargins();
\t //Store the helper's css position
\t this.cssPosition = this.helper.css("position");
\t this.scrollParent = this.helper.scrollParent(true);
\t this.offsetParent = this.helper.offsetParent();
\t this.hasFixedAncestor = this.helper.parents().filter(function() {
\t \t \t return $(this).css("position") === "fixed";
\t \t }).length > 0;
\t //The element's absolute position on the page minus margins
\t this.positionAbs = this.element.offset();
\t this._refreshOffsets(event);
\t //Generate the original position
\t this.originalPosition = this.position = this._generatePosition(event, false);
\t this.originalPageX = event.pageX/window.transformScale;
\t this.originalPageY = event.pageY/window.transformScale;
\t //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
\t (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
\t //Set a containment if given in the options
\t this._setContainment();
\t //Trigger event + callbacks
\t if (this._trigger("start", event) === false) {
\t \t this._clear();
\t \t return false;
\t }
\t //Recache the helper size
\t this._cacheHelperProportions();
\t //Prepare the droppable offsets
\t if (jQuery.ui.ddmanager && !o.dropBehaviour) {
\t \t jQuery.ui.ddmanager.prepareOffsets(this, event);
\t }
\t // Execute the drag once - this causes the helper not to be visible before getting its
\t // correct position
\t this._mouseDrag(event, true);
\t // If the ddmanager is used for droppables, inform the manager that dragging has started
\t // (see #5003)
\t if (jQuery.ui.ddmanager) {
\t \t jQuery.ui.ddmanager.dragStart(this, event);
\t }
\t return true;
};
jQuery.ui.draggable.prototype._mouseDrag = function(event, noPropagation) {
\t // reset any necessary cached properties (see #5009)
\t if (this.hasFixedAncestor) {
\t \t this.offset.parent = this._getParentOffset();
\t }
\t //Compute the helpers position
\t this.position = this._generatePosition(event, true);
\t this.positionAbs = this._convertPositionTo("absolute");
\t //Call plugins and callbacks and use the resulting position if something is returned
\t if (!noPropagation) {
\t \t var ui = this._uiHash();
\t \t if (this._trigger("drag", event, ui) === false) {
\t \t \t this._mouseUp(new $.Event("mouseup", event));
\t \t \t return false;
\t \t }
\t \t this.position = ui.position;
\t }
\t this.helper[ 0 ].style.left = this.position.left + "px";
\t this.helper[ 0 ].style.top = this.position.top + "px";
\t if (jQuery.ui.ddmanager) {
\t \t jQuery.ui.ddmanager.drag(this, event);
\t }
\t return false;
};
})();
body {
\t position: \t relative;
\t margin: \t \t 0;
\t transform-origin: \t 0 0 0;
}
#root {
\t position: \t fixed;
\t top: \t \t 20px;
\t left: \t \t 20px;
\t width: \t \t 500px;
\t height: \t \t 500px;
\t border: \t \t solid 2px green;
}
#box {
\t position: \t absolute;
\t top: \t \t 100px;
\t left: \t \t 50px;
\t display: \t inline-block;
\t width: \t \t 50px;
\t height: \t \t 50px;
\t background: \t yellow;
\t border: \t \t 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>
+0.5用於拖動 - 但它仍然是不完美的。您的位置解決方案是不可接受的,cuz設置可能會有所不同Thx任何方式 – l00k
告訴我它是如何變化的,我可以調整代碼。 – ConnorsFan
它可能是左,中,右,上,下。我還需要全面的功能與碰撞檢測,fliping等 – l00k