Webkit和jQuery可拖动跳跃
作为一个实验,我创build了几个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; }
然后我随机devise了它们,并通过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();
拖动的作品,但我注意到,只有在webkit浏览器中拖动div时突然跳跃,而在Firefox中的一切都很好。
如果我删除了绝对风格的位置 ,那么“跳跃”就更糟了。 我认为webkit和gecko之间的转换原点可能有所不同,但是它们都默认处于元素的中心。
我已经search过,但只有滚动条或sorting列表的结果。
这是我的问题的一个工作演示。 尝试在Safari / Chrome和Firefox中查看它。 http://jsbin.com/ucehu/
这是一个webkit中的错误,或浏览器如何呈现webkit?
这是可拖动的依赖于jquery offset()
函数和offset()
对本地js函数getBoundingClientRect()
。 最终这是jquery核心的一个问题,不能补偿与getBoundingClientRect()
相关的不一致。 Firefox的getBoundingClientRect()
版本会忽略css3变换(旋转),而chrome / safari(webkit)则不会。
这里是一个问题的例子。
一个hacky的解决方法:
replacejquery.ui.draggable.js中的以下内容
//The element's absolute position on the page minus margins this.offset = this.positionAbs = this.element.offset();
同
//The element's absolute position on the page minus margins this.offset = this.positionAbs = { top: this.element[0].offsetTop, left: this.element[0].offsetLeft };
最后是你的jsbin的monkeypatched版本。
我在@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; } }); });
或者你可以看到演示
David Wick对于上面的总体方向是正确的,但计算正确的坐标是比这更多的参与。 这是一个更准确的猴子补丁,基于麻省理工学院许可Firebug代码,这应该在更多的情况下,你有一个复杂的DOM:
取而代之:
//元素在页面上的绝对位置减去边距 this.offset = this.positionAbs = this.element.offset();
与lesshacky(一定要得到整个事情,你需要滚动):
//元素在页面上的绝对位置减去边距 this.offset = this.positionAbs = getViewOffset(this.element [0]); 函数getViewOffset(节点){ var x = 0,y = 0,win = node.ownerDocument.defaultView || 窗口; if(node)addOffset(node); 返回{left:x,top:y}; 函数getStyle(node){ 返回node.currentStyle || // IE win.getComputedStyle(node,''); } 函数addOffset(node){ 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; 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; } 如果((X = node.scrollLeft))x + = parseInt(X,10)|| 0; if((Y = node.scrollTop))y + = parseInt(Y,10)|| 0; } } }
这是一个遗憾的是,DOM本身不公开这些计算。
@ecmanaut:伟大的解决scheme。 感谢您的努力。 为了帮助他人,我把你的解决scheme变成了一个猴子补丁。 将以下代码复制到文件中。 加载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>
以下是复制/粘贴到patch_draggable.js的代码:
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();
大卫威克的答案是非常有益的…谢谢…在这里我编码的resize相同的解决方法,因为它有同样的问题:
在jquery.ui.resizable.js中search以下内容
var o = this.options, iniPos = this.element.position(), el = this.element;
并replace为:
var o = this.options, iniPos = {top:this.element[0].offsetTop,left:this.element[0].offsetLeft}, el = this.element;
我使用了很多解决scheme来正确拖动工作。 但是,它仍然反应到一个dropzone(像它不旋转)。 解决scheme实际上是使用相对定位的父容器。
这节省了我很多时间。
<div id="drawarea"> <div class="rect-container h"> <div class="rect"></div> </div> </div> .rect-container { position:relative; }
完整的解决scheme(这不是从我): http : //jsfiddle.net/Sp6qa/2/
我也研究了很多。 就像这样,jQuery没有任何计划在未来改变当前的行为。 所有提交的有关该主题的门票都closures了。 所以刚开始有相对定位的父容器。 它像一个魅力,应该是未来的保障。
我更喜欢这个解决方法,因为它保留了原始处理程序
它删除转换然后恢复它
$(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; }; });
演示 (从@廖三启jsbin开始)
您必须将可拖动元素的父容器设置为“position:relative”。
- 无法在一个周期结束时停止animation
- 在Android应用程序中embeddedChromium或Webkit
- 什么是正确的“-moz-appearance”值来隐藏<select>元素的下拉箭头
- 正确的方法来优化iOS / Mobile Safari的CSS 3animation?
- Javascript / Chrome – 如何从webkit检查器复制对象作为代码
- 如何做一个webkit的CSS无尽的旋转animation。
- 移动设备上的Twitter Bootstrap模式
- 有没有办法从Cocoa WebView对象使用WebKit Web检查器?
- 如何在Delphi应用程序中embeddedIE <n>以外的浏览器对象