拖动和调整CSS转换的元素的大小

例如,如果我们设置了一个-vendor-transform: rotate(40deg)在矩形<div>-vendor-transform: rotate(40deg) css属性,所有突然的拖动和resize变得非常奇怪和有缺陷。

下面是一个简单的jQueryUI示例: http : //jsfiddle.net/Ja4dY/1/

您会注意到,如果在转换时拖动或调整矩形的大小,它将跳起或向下跳动,光标将不会保留在正确的位置。 在我真正的代码我使用自定义代码的大小和拖动,但是我遇到了同样的问题。

那么,“问题”当然是一个元素的方向将会改变。 所以左边可以是正确的,顶部底部和中间和Javascript代码仍然处理每个方向,因为它不会被转换。

所以,问题是: 我们如何补偿变形 / 旋转元素?

任何好的资源/书籍/博客也是非常受欢迎的。

您可以使用getComputedStyle()获取应用于元素的当前变换matrix。 您可以使用它来将当前的鼠标位置转换为其在变换后的空间中的位置,并查看点击/拖动事件是否位于元素边界和/或angular落内。 良好的资源:

http://www.useragentman.com/blog/2011/01/07/css3-matrix-transform-for-the-mathematically-challenged/

http://www.eleqtriq.com/2010/05/css-3d-matrix-transformations/

顺便说一句,正如你所遇到的,这是不重要的代码。 我们必须为Sencha Animator做这件事,那是一个野兽。

问题是那些使元素可拖曳的函数,不pipe是否使用jQuery UI,都依赖于原生的getBoundingClientRect()函数来计算元素的位置等。

当应用CSS3变换(如旋转offset() ,jQuery UI中使用的getBoundingClientRect()或equalent jQuery offset()函数的值不再按预期工作,鼠标指针的位置因为元素的大小突然变得混乱旋转后错误。

为了解决这个问题,你需要添加一些辅助函数来重新计算值,并且有一个可用于jQuery UI可拖动的猴子补丁。

关于如何为自定义代码制作相同的补丁程序很难说,但是您可能必须以某种方式将它集成到自定义函数中,而且您需要编写一些代码,而且更难以实现一些作为辅助函数的工具,它可以用来自定义代码,但是请注意,这些计算是相当复杂的,参见下面的代码:

 function monkeyPatch_mouseStart() { var oldFn = $.ui.draggable.prototype._mouseStart ; $.ui.draggable.prototype._mouseStart = function(event) { var o = this.options; function getViewOffset(node) { var x = 0, y = 0, win = node.ownerDocument.defaultView || window; if (node) addOffset(node); return { left: x, top: y }; function getStyle(node) { return node.currentStyle || // IE win.getComputedStyle(node, ''); } function addOffset(node) { var p = node.offsetParent, style, X, Y; x += parseInt(node.offsetLeft, 10) || 0; y += parseInt(node.offsetTop, 10) || 0; if (p) { x -= parseInt(p.scrollLeft, 10) || 0; y -= parseInt(p.scrollTop, 10) || 0; if (p.nodeType == 1) { var parentStyle = getStyle(p) , localName = p.localName , parent = node.parentNode; if (parentStyle.position != 'static') { x += parseInt(parentStyle.borderLeftWidth, 10) || 0; y += parseInt(parentStyle.borderTopWidth, 10) || 0; if (localName == 'TABLE') { x += parseInt(parentStyle.paddingLeft, 10) || 0; y += parseInt(parentStyle.paddingTop, 10) || 0; } else if (localName == 'BODY') { style = getStyle(node); x += parseInt(style.marginLeft, 10) || 0; y += parseInt(style.marginTop, 10) || 0; } } else if (localName == 'BODY') { x += parseInt(parentStyle.borderLeftWidth, 10) || 0; y += parseInt(parentStyle.borderTopWidth, 10) || 0; } while (p != parent) { x -= parseInt(parent.scrollLeft, 10) || 0; y -= parseInt(parent.scrollTop, 10) || 0; parent = parent.parentNode; } addOffset(p); } } else { if (node.localName == 'BODY') { style = getStyle(node); x += parseInt(style.borderLeftWidth, 10) || 0; y += parseInt(style.borderTopWidth, 10) || 0; var htmlStyle = getStyle(node.parentNode); x -= parseInt(htmlStyle.paddingLeft, 10) || 0; y -= parseInt(htmlStyle.paddingTop, 10) || 0; } if ((X = node.scrollLeft)) x += parseInt(X, 10) || 0; if ((Y = node.scrollTop)) y += parseInt(Y, 10) || 0; } } } this.helper = this._createHelper(event); this._cacheHelperProportions(); if($.ui.ddmanager) $.ui.ddmanager.current = this; this._cacheMargins(); this.cssPosition = this.helper.css("position"); this.scrollParent = this.helper.scrollParent(); this.offset = this.positionAbs = getViewOffset(this.element[0]); this.offset = { top: this.offset.top - this.margins.top, left: this.offset.left - this.margins.left }; $.extend(this.offset, { click: { left: event.pageX - this.offset.left, top: event.pageY - this.offset.top }, parent: this._getParentOffset(), relative: this._getRelativeOffset() }); this.originalPosition = this.position = this._generatePosition(event); this.originalPageX = event.pageX; this.originalPageY = event.pageY; (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); if(o.containment) this._setContainment(); if(this._trigger("start", event) === false) { this._clear(); return false; } this._cacheHelperProportions(); if ($.ui.ddmanager && !o.dropBehaviour) $.ui.ddmanager.prepareOffsets(this, event); this.helper.addClass("ui-draggable-dragging"); this._mouseDrag(event, true); if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event); return true; }; } monkeyPatch_mouseStart(); 

这里有一个FIDDLE显示它按预期工作,jQuery UI的可拖动和可resize!

我发现这…这是一个工作的例子加上信息,演示和下载链接。

jquery-ui-rotation-using-css-transform – > live-demo

他使用自己的图书馆,但如果你对这个主题感兴趣,你可以阅读并学习他如何得到它。

欢呼声,祝你好运。

Gmo.-

顺便说一句,网站是在俄罗斯,但与谷歌翻译,你可以pipe理;-)

这不是在jQuery中的错误。 只是它不被支持。 如果你检查jQuery UI源代码,你会发现它不使用变换matrix来计算变换的对象和页面之间的差异。

你的例子,可能每个jQ UI拖拽实现都受到这个JQ UI源代码(jquery.ui.draggable.js文件v1.8.23的约314行)中的两个方法的原因的影响。 计算出的偏移量对偏移量的变化无关紧要,因为旋转是通过元素中心完成的。

你必须计算出什么是变化。 这是解决方法,快速和肮脏。 这个想法是检查转换元素的边界框有什么区别。

在这里检查示例http://jsfiddle.net/mjaric/9Nqrh/

忽略前两轮的部分,他们只是尽量减less代码行。 第三涉及用于计算差异的坐标系的平移。 它将在进行翻译之后偏移左侧和顶部(注意它是第一个在filter中)。

如果你想避免前两个旋转filter,你可以使用2D旋转公式的代码:

x'= x cos f – y sin f

y'= ycosf + xsinf

其中f是旋转angular度,但并不是那么简单,还包含更多的代码行,因为您需要x和y坐标与x相比较的左上angular的初始angular度,因此需要计算原始边界框的对angularangular度轴(正部分)。 然后计算xx'和y-y'的变化。 但是,我预测到一些变化的迹象,编码/debugging将需要更多的时间,那么我现在就有。 很抱歉,但我相信你可以在阅读这篇文章后找出该怎么做。

它看起来更好,如果我们重写cursorAt:

 $("#foo").mousedown(function (e) { var x = e.pageX - this.offsetLeft; var y = e.pageY - this.offsetTop; console.log(x); $("#foo").draggable("option", "cursorAt", {left: x, top:y}); }); 

更新小提琴: http : //jsfiddle.net/johnkoer/Ja4dY/8/

你说你对JQuery解决scheme不感兴趣,

  • 一个解决scheme是;

    我build议你写你自己的拖动和resize的function。 您可以处理旋转对象的大小调整和拖拽操作,以便在该度数的正弦和余弦上添加顶部和左侧。

  • 另一个解决scheme是

    您可以使用像Raphael JS这样的库来创build对象以进行转换,拖动和resize。 Raphael JS使用svg!

    有关Raphael JS的更多信息

  • 另一个解决scheme是;

    如果你不想使用像Raphael JS这样的库,你可以直接使用带有JQuery的SVG

    有关SVG的更多信息

现在不能写更多的细节,我明天扩展这个解决scheme。

希望这些帮助。

实际上,这似乎是jQuery中的一个bug。 一个简单的解决方法是:围绕可调整的div与容器div 。 将.draggable()设置为外部div ,将.resizable()设置为内部div 。 在Ubuntu上运行的Chromium似乎工作正常。 看小提琴 。

我已经着色了外部的div ,让你知道发生了什么。