我有这个脚本效果很好,它做我想做的......但唯一的问题是它在快速移动div时滞后。
我在互联网上花了几个小时试图找到解决方案,比如添加 pointermove、使用 translate3d、使用 RequestAnimationFrame、css as will-change 等...但没有任何效果
请参阅下面我尝试改进的代码
box-medling.js
(function ($) {
$.fn.boxModeling = function (options) {
const settings = $.extend({
boxSelector: 'box',
handlerClass: 'resize-handler',
minWidth: 40,
minHeight: 40,
resize: true,
rotate: true,
move: true,
raf: false,
startX: false,
startY: false,
deltaX: false,
deltaY: false,
initX: false,
initY: false,
}, options);
const boxHandlers =
'<div class="box-handlers">' +
'<div class="' + settings.handlerClass + ' resize left-top" style="top: -5px;left: -5px;"></div>' +
'<div class="' + settings.handlerClass + ' resize left-mid" style="left: -5px;top: calc(50% - 5px);"></div>' +
'<div class="' + settings.handlerClass + ' resize left-bot" style="bottom: -5px;left: -5px;"></div>' +
'<div class="' + settings.handlerClass + ' resize center-top" style="top: -5px;left: calc(50% - 5px);"></div>' +
'<div class="' + settings.handlerClass + ' resize center-bot" style="bottom: -5px;left: calc(50% - 5px);"></div>' +
'<div class="' + settings.handlerClass + ' resize right-top" style="top: -5px;right: -5px;"></div>' +
'<div class="' + settings.handlerClass + ' resize right-mid" style="right: -5px;top: calc(50% - 5px);"></div>' +
'<div class="' + settings.handlerClass + ' resize right-bot" style="bottom: -5px;right: -5px;"></div>' +
'<div class="' + settings.handlerClass + ' rotate" style="top: -30px;left: calc(50% - 5px);"></div>' +
'</div>';
return $(this).each(function () {
const box = this;
// $(box).css('transform', $(box).css('transform') + 'translate(-50%, -50%)');
let initX, initY, mousePressX, mousePressY, initW, initH, initRotate;
$(box).append(boxHandlers);
const hLeftTop = $(box).find('.' + settings.handlerClass + '.left-top');
const hLeftMid = $(box).find('.' + settings.handlerClass + '.left-mid');
const hLeftBot = $(box).find('.' + settings.handlerClass + '.left-bot');
const hCenterTop = $(box).find('.' + settings.handlerClass + '.center-top');
const hCenterBot = $(box).find('.' + settings.handlerClass + '.center-bot');
const hRightTop = $(box).find('.' + settings.handlerClass + '.right-top');
const hRightMid = $(box).find('.' + settings.handlerClass + '.right-mid');
const hRightBot = $(box).find('.' + settings.handlerClass + '.right-bot');
const hRotate = $(box).find('.' + settings.handlerClass + '.rotate');
function repositionElement(x, y) {
//alert(settings.deltaX);
//$(box).css('backface-visibility', 'hidden');
//$(box).css({'-webkit-transform': 'translateX(' + x + 'px) translateY(' + y + 'px)', 'transform': 'translateX(' + x + 'px) translateY(' + y + 'px)' });
$(box).css('left', x+ 'px'); // <=
//initX + (event.clientX - mousePressX)
$(box).css('top', y + 'px'); // <=
// box.style.transform = "translate3d("+settings.deltaX+"px,"+settings.deltaY+"px, 0px)";
// once the paint job is done we 'release' animation frame variable to allow next paint job:
settings.raf = null;
//console.dirxml(box);
}
function resize(w, h) {
$(box).css('width', w + 'px');
$(box).css('height', h + 'px');
//console.log('box : '+$(box).attr("data-id")+' wh : '+h+'-'+h);
}
function getRotation(e) {
const st = window.getComputedStyle(e, null);
const tm = st.getPropertyValue("-webkit-transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform") ||
"none";
if (tm !== "none") {
const values = tm.split('(')[1].split(')')[0].split(',');
const angle = Math.round(Math.atan2(Number.parseFloat(values[1]), Number.parseFloat(values[0])) * (180 / Math.PI));
return (angle < 0 ? angle + 360 : angle);
}
return 0;
}
function rotateBox(deg) {
$(box).css('transform', 'translate(-50%, -50%) rotate(' + deg + 'deg)'); // <=
//console.log('box : '+$(box).attr("data-id")+' angle : '+(deg < 0 ? deg + 360 : deg));
}
function dragSupport(event) {
if ($(event.target).hasClass(settings.handlerClass)) {
return;
}
initX = box.offsetLeft; // <=
initY = box.offsetTop; // <=
mousePressX = event.clientX;
mousePressY = event.clientY;
function eventMoveHandler(event) {
if (!settings.raf) {
x=initX + (event.clientX - mousePressX);
y=initY + (event.clientY - mousePressY);
let functionWithZeroParams = () => {
repositionElement(x,y)
}
settings.raf = requestAnimationFrame(functionWithZeroParams);
}
//console.log(event.clientX+"-"+event.clientY);
// repositionElement(initX + (event.clientX - mousePressX), initY + (event.clientY - mousePressY));
}
box.addEventListener('pointermove', eventMoveHandler, { passive: true }); // <=
["pointerup", "pointercancel"].forEach((event) => {
window.addEventListener('pointerup', function eventEndHandler() {
if (settings.raf) {
cancelAnimationFrame(settings.raf);
settings.raf = null;
};
showObjectState()
box.removeEventListener('pointermove', eventMoveHandler); // <=
window.removeEventListener('pointerup', eventEndHandler);
}, { passive: true });
})
}
function showObjectState() {
var gcss ='transform :translate(-50%, -50%) rotate('+getRotation(box)+'deg); left : '+$(box).css('left')+' ;top : '+$(box).css('top')+' ;width : '+$(box).css('width')+' ;height : '+$(box).css('height')+';';
console.log(gcss)
if($(box).attr("data-id")!="popup") {
$('#selectedboxid').val($(box).attr("data-id"));
$(".box").css("border", "black dashed 10px");
$("#popup").css("border", "solid red 10px");
$(box).css("border", "red dashed 10px");
$.post( "manage.php", { action:'location', boxid: $(box).attr("data-id"), projectid:1, css:gcss })
.done(function( data ) {
});
}
}
function resizeHandler(event, left, top, xResize, yResize) {
initX = box.offsetLeft; // <=
initY = box.offsetTop; // <=
mousePressX = event.clientX;
mousePressY = event.clientY;
initW = box.offsetWidth;
initH = box.offsetHeight;
initRotate = getRotation(box); // <=
const initRadians = initRotate * Math.PI / 180;
const cosFraction = Math.cos(initRadians);
const sinFraction = Math.sin(initRadians);
function eventMoveHandler(event) {
const wDiff = (event.clientX - mousePressX);
const hDiff = (event.clientY - mousePressY);
let rotatedWDiff = cosFraction * wDiff + sinFraction * hDiff;
let rotatedHDiff = cosFraction * hDiff - sinFraction * wDiff;
let newW = initW, newH = initH, newX = initX, newY = initY;
if (xResize) {
if (left) {
newW = initW - rotatedWDiff;
if (newW < settings.minWidth) {
newW = settings.minWidth;
rotatedWDiff = initW - settings.minWidth;
}
} else {
newW = initW + rotatedWDiff;
if (newW < settings.minWidth) {
newW = settings.minWidth;
rotatedWDiff = settings.minWidth - initW;
}
}
newX += 0.5 * rotatedWDiff * cosFraction;
newY += 0.5 * rotatedWDiff * sinFraction;
}
if (yResize) {
if (top) {
newH = initH - rotatedHDiff;
if (newH < settings.minHeight) {
newH = settings.minHeight;
rotatedHDiff = initH - settings.minHeight;
}
} else {
newH = initH + rotatedHDiff;
if (newH < settings.minHeight) {
newH = settings.minHeight;
rotatedHDiff = settings.minHeight - initH;
}
}
newX -= 0.5 * rotatedHDiff * sinFraction;
newY += 0.5 * rotatedHDiff * cosFraction;
}
resize(newW, newH);
repositionElement(newX, newY);
}
// window addEventListener
window.addEventListener('mousemove', eventMoveHandler, { passive: true });
window.addEventListener('mouseup', function eventEndHandler() {
showObjectState()
window.removeEventListener('mousemove', eventMoveHandler, false);
window.removeEventListener('mouseup', eventEndHandler);
}, false);
}
function rotate(event) {
initX = box.offsetLeft; // <=
initY = box.offsetTop; // <=
mousePressX = event.clientX;
mousePressY = event.clientY;
const arrowRects = box.getBoundingClientRect();
const arrowX = arrowRects.left + arrowRects.width / 2;
const arrowY = arrowRects.top + arrowRects.height / 2;
function eventMoveHandler(event) {
const angle = Math.atan2(event.clientY - arrowY, event.clientX - arrowX) + Math.PI / 2;
rotateBox(angle * 180 / Math.PI);
}
window.addEventListener('mousemove', eventMoveHandler, false);
window.addEventListener('mouseup', function eventEndHandler() {
showObjectState()
window.removeEventListener('mousemove', eventMoveHandler, false);
window.removeEventListener('mouseup', eventEndHandler);
}, false);
}
function editingStyle(event) {
console.dir(event);
$('.' + settings.handlerClass).hide();
$(box).css('z-index', getComputedStyle(document.body).getPropertyValue('--zi-' + $(box).data('id')) ); // <= <=
if ($(event.target).hasClass(settings.boxSelector)) {
$(event.target).find('.' + settings.handlerClass).show();
$(event.target).css('z-index', '1000'); // <=
} else if ($(event.target).hasClass(settings.handlerClass)) {
$(event.target).show();
$(event.target).siblings('.' + settings.handlerClass).show();
$(event.target).parents('.' + settings.boxSelector).css('z-index', '1000'); // <=
}
if (!settings.resize) {
$($(event.target).parents().find('.' + settings.handlerClass + '.resize').hide());
}
if (!settings.rotate) {
$($(event.target).parents().find('.' + settings.handlerClass + '.rotate').hide());
}
$("#popup").css("z-index", "99999");
$(".ck").css("z-index", "99999");
}
function popup(event) {
$("#popup").show();
$("#popup").css("left", ((parseInt($(box).css('left'))-parseInt($(box).css('width')))-14)+'px');
$("#popup").css("top", $(box).css('top'));
$("#popup").css("z-index", "99999");
}
//box.addEventListener('dblclick', function (event) { if(event.target.role!="textbox") {return popup(event);} }, false);
// listeners
if (settings.move) {
box.addEventListener('mousedown', function (event) { if(event.target.role!="textbox") { return dragSupport(event);} }, false);
}
if (settings.resize) {
hLeftTop.mousedown(function (event) { return resizeHandler(event, true, true, true, true); });
hLeftMid.mousedown(function (event) { return resizeHandler(event, true, false, true, false); });
hLeftBot.mousedown(function (event) { return resizeHandler(event, true, false, true, true); });
hCenterTop.mousedown(function (event) { return resizeHandler(event, false, true, false, true); });
hCenterBot.mousedown(function (event) { return resizeHandler(event, false, false, false, true); });
hRightTop.mousedown(function (event) { return resizeHandler(event, false, true, true, true); });
hRightMid.mousedown(function (event) { return resizeHandler(event, false, false, true, false); });
hRightBot.mousedown(function (event) { return resizeHandler(event, false, false, true, true); });
}
if (settings.rotate) {
hRotate.get(0).addEventListener('mousedown', function (event) { return rotate(event); }, false);
}
$(document).mousedown(function (event) { return editingStyle(event); });
});
};
}(jQuery));
index.html
<!DOCTYPE html>
<html lang="">
<head>
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
<style>
@font-face {
font-family: 'Virgil';
src: url('Virgil.woff2') format('woff2'),
}
body {
font-family: 'Virgil', Fallback, sans-serif;
}
.lead { font-size: 1.5rem; font-weight: 300; }
.container { margin: 150px auto; max-width: 960px; }
body { background: #fafafa; }
.box {
position: absolute;
user-select: none;
transform: translate(-50%, -50%);
will-change: transform, left, top;
contain: layout;
}
.resize-handler {
height: 10px;
width: 10px;
background-color: #0055ff;
position: absolute;
border-radius: 100px;
border: 1px solid #ffffff;
user-select: none;
display: none;
}
.resize-handler:hover {background-color: #0000cc;}
.resize-handler.rotate {cursor: url('https://findicons.com/files/icons/1620/crystal_project/16/rotate_ccw.png'), auto;}
:root {
/* id-1 */
--left-1: 200px;
--top-1: 480px;
--width-1: 200px;
--height-1: 200px;
--bg-1: #ffff00cc;
--zi-1: 1;
/* id-2 */
--left-2: 500px;
--top-2: 480px;
--width-2: 300px;
--height-2: 200px;
--bg-2: #0050ffcc;
--zi-2: 2;
}
#popup {
background:white;
}
</style>
</head>
<body id="body">
<div class="container" >
<div id="boxarea">
<div class="box" id="popup" data-id="popup" style="position:absolute;left: 50px; top: 40px; z-index:99999; width:700px; height:850px; border:10px solid red">
<div onmousedown="this.parentNode.setAttribute('draggable', false);">Drag me! :)</div>
</div>
</div>
<script type="text/javascript">
function getUniqId() {
const dateString = Date.now().toString(36);
const randomness = Math.random().toString(36).substr(2);
return dateString + randomness;
}
var mouseX, mouseY;
$(document).mousemove(function(e) {
mouseX = e.pageX;
mouseY = e.pageY;
}).mouseover(); // call the handler immediately
function createBox(name,css) {
if(css=="") { css= 'left: '+mouseX+'px; top: '+mouseY+'px; z-index: var(--zi-2); width: var(--width-2); height: var(--height-2); border:10px dashed black'; }
if(name=="pop") {
uid='popup';
var obj =document.getElementById("popup") ;
obj.style.cssText = 'left: 550px; top: 450px; z-index:99999; width:700px; height:850px; border:10px solid red';
}else {
if(name!="pop"&& name=="") { var uid= getUniqId();} else { uid=name; }
var obj = document.createElement('div');
obj.class = "box";
obj.setAttribute('class' , "box");
obj.setAttribute('id' , uid);
obj.setAttribute('data-id' , uid);
document.getElementById("body").appendChild(obj);
obj.style.cssText = css;
}
$(obj).boxModeling({
rotate: true,
resize: true,
move: true,
});
}
// A $( document ).ready() block.
$( document ).ready(function() {
createBox('pop','');
createBox('','');
});
</script>
</body>
<script src="box-modeling.js"></script>
</html>
但我认为我在 JS 方面不够好,因为没有任何效果,所以最后的希望在这里!
谢谢
如果你看一下
editingStyle
函数,你会发现它首先用这行代码重置所有框z-index
:
$(box).css('z-index',
getComputedStyle(document.body).getPropertyValue('--zi-'+ $(box).data('id'))
);
然后,之后的几行将
z-index: 10000
应用于当前抓取的框。
简而言之,这将始终使抓取的盒子位于所有其他盒子的顶部。
所以,代码中的不同之处在于,您在这一行中手动应用了
z-index
的 99999
:
$("#popup").css("z-index", "99999");
这会导致其他被抓取的盒子掉到它下面并完全失去
mousemove
事件。
问题根本不在于性能,而在于哪个盒子在其他盒子之上。
注意:尽量保持你的代码尽可能干净和小,通常简单的代码可以获得更好的性能:)