Javascript Webkit和jQuery可拖动跳跃

Javascript Webkit和jQuery可拖动跳跃,javascript,jquery,css,webkit,Javascript,Jquery,Css,Webkit,作为一个实验,我创建了几个div并使用CSS3旋转它们 .items { position: absolute; cursor: pointer; background: #FFC400; -moz-box-shadow: 0px 0px 2px #E39900; -webkit-box-shadow: 1px 1px 2px #E39900; box-shadow: 0px 0px 2p

作为一个实验,我创建了几个div并使用CSS3旋转它们

    .items { 
        position: absolute;
        cursor: pointer;
        background: #FFC400;
        -moz-box-shadow: 0px 0px 2px #E39900;
        -webkit-box-shadow: 1px 1px 2px #E39900; 
        box-shadow: 0px 0px 2px #E39900;
        -moz-border-radius: 2px; 
        -webkit-border-radius: 2px;
        border-radius: 2px;
    }
然后,我随机对它们进行了样式设置,并通过jQuery使它们可以拖动

    $('.items').each(function() {
        $(this).css({
            top: (80 * Math.random()) + '%',
            left: (80 * Math.random()) + '%',
            width: (100 + 200 * Math.random()) + 'px',
            height: (10 + 10 * Math.random()) + 'px',
            '-moz-transform': 'rotate(' + (180 * Math.random()) + 'deg)',
            '-o-transform': 'rotate(' + (180 * Math.random()) + 'deg)',
            '-webkit-transform': 'rotate(' + (180 * Math.random()) + 'deg)',
        });
    });

    $('.items').draggable();
拖拽是有效的,但我注意到拖拽div时突然跳了起来,这只适用于webkit浏览器,而在Firefox中一切正常

如果我去掉位置:绝对风格,“跳跃”更糟糕。我认为webkit和gecko之间的转换原点可能有所不同,但默认情况下它们都位于元素的中心

我已经到处搜索过了,但只找到了关于滚动条或可排序列表的结果

这是我的问题的工作演示。尝试在Safari/Chrome和Firefox中查看它


这是webkit中的错误还是浏览器呈现webkit的方式?

这是由于DragTable依赖于jquery
offset()
函数和
offset()
使用本机js函数
getBoundingClientRect()
。最终,这是一个jquery核心无法补偿与
getBoundingClientRect()
相关的不一致性的问题。Firefox版本的
getBoundingClientRect()
忽略了css3转换(旋转),而chrome/safari(webkit)则没有

这就是问题的一个例证

一个老套的解决办法:

替换中的以下内容:


//The element's absolute position on the page minus margins
this.offset = this.positionAbs = { top: this.element[0].offsetTop, 
                                   left: this.element[0].offsetLeft };

最后是一个修补过的版本。

大卫·威克的答案非常有用。。。谢谢 在这里,我为可调整大小的编码了相同的解决方法,因为它有相同的问题:

在中搜索以下内容:

并替换为:

var o = this.options, iniPos = {top:this.element[0].offsetTop,left:this.element[0].offsetLeft}, el = this.element;

David Wick在上述总体方向上是正确的,但计算正确的坐标远比这复杂得多。这里有一个更精确的monkey补丁,它基于MIT许可的Firebug代码,应该可以在更多具有复杂DOM的情况下使用:

替换为:

//The element's absolute position on the page minus margins this.offset = this.positionAbs = this.element.offset(); //元素在页面上的绝对位置减去页边距 this.offset=this.positionAbs=this.element.offset(); 使用更少的黑客(确保获取全部内容;您需要滚动):

//元素在页面上的绝对位置减去页边距 this.offset=this.positionAbs=getViewOffset(this.element[0]); 函数getViewOffset(节点){ 变量x=0,y=0,win=node.ownerDocument.defaultView | | window; if(节点)addOffset(节点); 返回{left:x,top:y}; 函数getStyle(节点){ return node.currentStyle | |//IE getComputedStyle(节点“”); } 函数addOffset(节点){ var p=node.offsetParent,style,X,Y; x+=parseInt(node.offsetLeft,10)| 0; y+=parseInt(node.offsetTop,10)| 0; 如果(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; 如果(localName=='TABLE'){ x+=parseInt(parentStyle.paddingLeft,10)| 0; y+=parseInt(parentStyle.paddingTop,10)| | 0; } else if(localName==“BODY”){ style=getStyle(节点); 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!=父级){ x-=parseInt(parent.scrollLeft,10)| 0; y-=parseInt(parent.scrollTop,10)| 0; parent=parent.parentNode; } addOffset(p); } } 否则{ 如果(node.localName==“BODY”){ style=getStyle(节点); 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; } 如果((X=node.scrollLeft))X+=parseInt(X,10)| 0; 如果((Y=node.scrollTop))Y+=parseInt(Y,10)| | 0; } } }
很遗憾,DOM没有在本机公开这些计算。

@ecmanaut:很好的解决方案。谢谢你的努力。为了帮助别人,我把你的解决方案变成了猴子补丁。将下面的代码复制到一个文件中。加载jquery-ui.js后包括该文件,如下所示:

<script src="javascripts/jquery/jquery.js"></script>
<script src="javascripts/jquery/jquery-ui.js"></script>

<!-- the file containing the monkey-patch to draggable -->
<script src="javascripts/jquery/patch_draggable.js"></script>

您必须将可拖动元素的父容器设置为“position:relative”。

我绘制了一个图像,以指示在不同浏览器上旋转后的偏移量,如@David Wick的回答

如果您不想修补或修改jquery.ui.draggable.js,下面是要修复的代码

$(document).ready(function () {
    var recoupLeft, recoupTop;
    $('#box').draggable({
        start: function (event, ui) {
            var left = parseInt($(this).css('left'),10);
            left = isNaN(left) ? 0 : left;
            var top = parseInt($(this).css('top'),10);
            top = isNaN(top) ? 0 : top;
            recoupLeft = left - ui.position.left;
            recoupTop = top - ui.position.top;
        },
        drag: function (event, ui) {
            ui.position.left += recoupLeft;
            ui.position.top += recoupTop;
        }
    });
});

或者你可以看到

我使用了很多解决方案来让拖动正常工作。但是,它对dropzone的反应仍然是错误的(就像它没有旋转一样)。解决方案实际上是使用相对定位的父容器

这节省了我很多时间

<div id="drawarea">
    <div class="rect-container h">
        <div class="rect"></div>
    </div>
</div> 



.rect-container {
    position:relative; 
}

.rect容器{
位置:相对位置;
}
这里的完整解决方案(不是我提供的):


我也做了很多研究。就像这样,jQuery没有任何计划在将来改变当前的行为。所有提交的关于该主题的票证均已关闭。因此,首先让parentcontainers相对定位。它就像一个魔咒,应该是未来的。

我更喜欢这种解决方法,因为它保留了
<script src="javascripts/jquery/jquery.js"></script>
<script src="javascripts/jquery/jquery-ui.js"></script>

<!-- the file containing the monkey-patch to draggable -->
<script src="javascripts/jquery/patch_draggable.js"></script>
function monkeyPatch_mouseStart() {
     // don't really need this, but in case I did, I could store it and chain
     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;
                }
              }
            }


                //Create and append the visible helper
                this.helper = this._createHelper(event);

                //Cache the helper size
                this._cacheHelperProportions();

                //If ddmanager is used for droppables, set the global draggable
                if($.ui.ddmanager)
                    $.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();

                //The element's absolute position on the page minus margins
            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: { //Where the click happened, relative to the element
                        left: event.pageX - this.offset.left,
                        top: event.pageY - this.offset.top
                    },
                    parent: this._getParentOffset(),
                    relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
                });

                //Generate the original position
                this.originalPosition = this.position = this._generatePosition(event);
                this.originalPageX = event.pageX;
                this.originalPageY = event.pageY;

                //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
                if(o.containment)
                    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 ($.ui.ddmanager && !o.dropBehaviour)
                    $.ui.ddmanager.prepareOffsets(this, event);

                this.helper.addClass("ui-draggable-dragging");
                //JWL: Hier vindt de jump plaats
                this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position

                //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
                if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);

                return true;

     };

 }
monkeyPatch_mouseStart();
$(document).ready(function () {
    var recoupLeft, recoupTop;
    $('#box').draggable({
        start: function (event, ui) {
            var left = parseInt($(this).css('left'),10);
            left = isNaN(left) ? 0 : left;
            var top = parseInt($(this).css('top'),10);
            top = isNaN(top) ? 0 : top;
            recoupLeft = left - ui.position.left;
            recoupTop = top - ui.position.top;
        },
        drag: function (event, ui) {
            ui.position.left += recoupLeft;
            ui.position.top += recoupTop;
        }
    });
});
<div id="drawarea">
    <div class="rect-container h">
        <div class="rect"></div>
    </div>
</div> 



.rect-container {
    position:relative; 
}
$(document).ready(function(){

    // backup original handler
    var _mouseStart = $.ui.draggable.prototype._mouseStart;

    $.ui.draggable.prototype._mouseStart = function(event) {

        //remove the transform
        var transform = this.element.css('transform');
        this.element.css('transform', 'none');

        // call original handler
        var result = _mouseStart.call(this, event);

        //restore the transform
        this.element.css('transform', transform);

        return result;
    };
});