gpt4 book ai didi

javascript - 在 javascript 中创建可调整大小/可拖动/旋转的 View

转载 作者:行者123 更新时间:2023-12-03 06:49:33 35 4
gpt4 key购买 nike

我一直在尝试在 Javascript 中创建类似的东西:
enter image description here
如您所见,容器可以拖动、旋转和调整大小。大多数事情都可以正常工作,但是在旋转容器时调整容器大小会产生奇怪的输出。
我希望这会发生:
enter image description here
相反,我得到了这个:
enter image description here
这是完整的代码:
https://jsfiddle.net/c0krownz/
或者,

var box = document.getElementById("box");
var boxWrapper = document.getElementById("box-wrapper");

var initX, initY, mousePressX, mousePressY, initW, initH, initRotate;

function repositionElement(x, y) {
boxWrapper.style.left = x;
boxWrapper.style.top = y;
}

function resize(w, h) {
box.style.width = w + 'px';
box.style.height = h + 'px';
boxWrapper.style.width = w;
boxWrapper.style.height = h;
}


function getCurrentRotation(el) {
var st = window.getComputedStyle(el, null);
var tm = st.getPropertyValue("-webkit-transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform")
"none";
if (tm != "none") {
var values = tm.split('(')[1].split(')')[0].split(',');
var angle = Math.round(Math.atan2(values[1], values[0]) * (180 / Math.PI));
return (angle < 0 ? angle + 360 : angle);
}
return 0;
}

function rotateBox(deg) {
boxWrapper.style.transform = `rotate(${deg}deg)`;
}

// drag support
boxWrapper.addEventListener('mousedown', function (event) {
if (event.target.className.indexOf("dot") > -1) {
return;
}

initX = this.offsetLeft;
initY = this.offsetTop;
mousePressX = event.clientX;
mousePressY = event.clientY;


function eventMoveHandler(event) {
repositionElement(initX + (event.clientX - mousePressX) + 'px',
initY + (event.clientY - mousePressY) + 'px');
}

boxWrapper.addEventListener('mousemove', eventMoveHandler, false);

window.addEventListener('mouseup', function () {
boxWrapper.removeEventListener('mousemove', eventMoveHandler, false);
}, false);

}, false);
// done drag support

// handle resize
var rightMid = document.getElementById("right-mid");
var leftMid = document.getElementById("left-mid");
var topMid = document.getElementById("top-mid");
var bottomMid = document.getElementById("bottom-mid");

var leftTop = document.getElementById("left-top");
var rightTop = document.getElementById("right-top");
var rightBottom = document.getElementById("right-bottom");
var leftBottom = document.getElementById("left-bottom");

function resizeHandler(event, left = false, top = false, xResize = false, yResize = false) {
initX = boxWrapper.offsetLeft;
initY = boxWrapper.offsetTop;
mousePressX = event.clientX;
mousePressY = event.clientY;

initW = box.offsetWidth;
initH = box.offsetHeight;

initRotate = getCurrentRotation(boxWrapper);

function eventMoveHandler(event) {
var wDiff = event.clientX - mousePressX;
var hDiff = event.clientY - mousePressY;

var newW = initW, newH = initH, newX = initX, newY = initY;

if (xResize) {
if (left) {
newW = initW - wDiff;
newX = initX + wDiff;
} else {
newW = initW + wDiff;
}
}

if (yResize) {
if (top) {
newH = initH - hDiff;
newY = initY + hDiff;
} else {
newH = initH + hDiff;
}
}

resize(newW, newH);
repositionElement(newX, newY);
}

window.addEventListener('mousemove', eventMoveHandler, false);

window.addEventListener('mouseup', function () {
window.removeEventListener('mousemove', eventMoveHandler, false);
}, false);
}


rightMid.addEventListener('mousedown', e => resizeHandler(e, false, false, true, false));
leftMid.addEventListener('mousedown', e => resizeHandler(e, true, false, true, false));
topMid.addEventListener('mousedown', e => resizeHandler(e, false, true, false, true));
bottomMid.addEventListener('mousedown', e => resizeHandler(e, false, false, false, true));
leftTop.addEventListener('mousedown', e => resizeHandler(e, true, true, true, true));
rightTop.addEventListener('mousedown', e => resizeHandler(e, false, true, true, true));
rightBottom.addEventListener('mousedown', e => resizeHandler(e, false, false, true, true));
leftBottom.addEventListener('mousedown', e => resizeHandler(e, true, false, true, true));

// handle rotation
var rotate = document.getElementById("rotate");
rotate.addEventListener('mousedown', function (event) {
// if (event.target.className.indexOf("dot") > -1) {
// return;
// }

initX = this.offsetLeft;
initY = this.offsetTop;
mousePressX = event.clientX;
mousePressY = event.clientY;


var arrow = document.querySelector("#box");
var arrowRects = arrow.getBoundingClientRect();
var arrowX = arrowRects.left + arrowRects.width / 2;
var arrowY = arrowRects.top + arrowRects.height / 2;

function eventMoveHandler(event) {
var 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 () {
window.removeEventListener('mousemove', eventMoveHandler, false);
}, false);

}, false);



resize(300, 200);
repositionElement(100, 100);
.box {
background-color: #00BCD4;
position: relative;
user-select: none;
}

.box-wrapper {
position: absolute;
transform-origin: center center;
user-select: none;
}

.dot {
height: 10px;
width: 10px;
background-color: #1E88E5;
position: absolute;
border-radius: 100px;
border: 1px solid white;
user-select: none;
}

.dot:hover {
background-color: #0D47A1;
}

.dot.left-top {
top: -5px;
left: -5px;
/* cursor: nw-resize; */
}

.dot.left-bottom {
bottom: -5px;
left: -5px;
/* cursor: sw-resize; */
}

.dot.right-top {
top: -5px;
right: -5px;
/* cursor: ne-resize; */
}

.dot.right-bottom {
bottom: -5px;
right: -5px;
/* cursor: se-resize; */
}

.dot.top-mid {
top: -5px;
left: calc(50% - 5px);
/* cursor: n-resize; */
}

.dot.left-mid {
left: -5px;
top: calc(50% - 5px);
/* cursor: w-resize; */
}

.dot.right-mid {
right: -5px;
top: calc(50% - 5px);
/* cursor: e-resize; */
}

.dot.bottom-mid {
bottom: -5px;
left: calc(50% - 5px);
/* cursor: s-resize; */
}

.dot.rotate {
top: -30px;
left: calc(50% - 5px);
cursor: url('https://findicons.com/files/icons/1620/crystal_project/16/rotate_ccw.png'), auto;
}

.rotate-link {
position: absolute;
width: 1px;
height: 15px;
background-color: #1E88E5;
top: -20px;
left: calc(50% + 0.5px);
z-index: -1;
}
<div class="box-wrapper" id="box-wrapper">
<div class="box" id="box">
<div class="dot rotate" id="rotate"></div>
<div class="dot left-top" id="left-top"></div>
<div class="dot left-bottom" id="left-bottom"></div>
<div class="dot top-mid" id="top-mid"></div>
<div class="dot bottom-mid" id="bottom-mid"></div>
<div class="dot left-mid" id="left-mid"></div>
<div class="dot right-mid" id="right-mid"></div>
<div class="dot right-bottom" id="right-bottom"></div>
<div class="dot right-top" id="right-top"></div>
<div class="rotate-link"></div>
</div>
</div>

最佳答案

用单位分配 CSS
当您设置 element.style.topelement.style.left ,您需要指定单位(通常是像素,px,在进行这种类型的元素转换时)。在您的情况下,您只需在 eventMoveHandler 中设置单位,这使得它只能在移动处理程序中工作。
在下面的代码片段中,我将其更改为自动添加 pxrepositionElement ,并从 eventMoveHandler 中删除单位.我还删除了 boxWrapper.style.width = w;boxWrapper.style.height = h;resize ,因为他们没有单位,而且不清楚boxWrapper 在哪里使用了尺寸。
重新分配坐标
对我来说,从盒子的中心来考虑这个问题更容易。您的原始代码使用预旋转的左上角来跟踪位置,这在旋转的矩形上变得难以想象。另一方面,中心始终是中心。为了使用中心,我添加/更改了这个 css:

.box {
transform: translate(-50%, -50%);
}
.box-wrapper {
transform-origin: top left; /* changed from `center center` */
}
它还稍微简化了 resizeHandler 中的代码。/ eventMoveHandler :
if (xResize) {
if (left) {
newW = initW - wDiff;
} else {
newW = initW + wDiff;
}
newX += 0.5 * wDiff;
}
if (yResize) {
if (top) {
newH = initH - hDiff;
} else {
newH = initH + hDiff;
}
newY += 0.5 * hDiff;
}
现在 box-wrapperstyle.topstyle.left坐标实际上是 box 的中心.如果这个坐标系不起作用,我们可以重新访问它。
调整大小时考虑旋转
从这里开始,我们需要在调整盒子大小时考虑盒子的旋转。例如,当盒子旋转 90 度时,所有的 x 变化都变成了 y 变化。要转换它们,您可以使用 Math.cosMath.sin
var initRadians = initRotate * Math.PI / 180;
var cosFraction = Math.cos(initRadians);
var sinFraction = Math.sin(initRadians);
//...
var wDiff = (event.clientX - mousePressX);
var hDiff = (event.clientY - mousePressY);
var rotatedWDiff = cosFraction * wDiff + sinFraction * hDiff;
var rotatedHDiff = cosFraction * hDiff - sinFraction * wDiff;
//...
if (xResize) {
if (left) {
newW = initW - rotatedWDiff;
} else {
newW = initW + rotatedWDiff;
}
//...
}
if (yResize) {
if (top) {
newH = initH - rotatedHDiff;
} else {
newH = initH + rotatedHDiff;
}
//...
}

此外,当你更正你的位置时,你也应该使用 sin 和 cos 分数,因为 style.top 设置的位置和 style.left通常不考虑轮换:
if (xResize) {
//...
newX += 0.5 * rotatedWDiff * cosFraction;
newY += 0.5 * rotatedWDiff * sinFraction;
}
if (yResize) {
//...
newX -= 0.5 * rotatedHDiff * sinFraction;
newY += 0.5 * rotatedHDiff * cosFraction;
}
应用最小宽度和高度
正如评论中所指出的,当拖动的边缘或 Angular 落超出锚定边缘时,行为很奇怪。在这种情况下,您可能希望盒子被翻转,或者盒子停止调整大小。我将在这里使用最小宽度和高度,因为实现似乎更简单。
const minWidth = 40;
const minHeight = 40;
//...
if (xResize) {
if (left) {
newW = initW - rotatedWDiff;
if (newW < minWidth) {
newW = minWidth;
rotatedWDiff = initW - minWidth;
}
} else {
newW = initW + rotatedWDiff;
if (newW < minWidth) {
newW = minWidth;
rotatedWDiff = minWidth - initW;
}
}
//..
}
if (yResize) {
if (top) {
newH = initH - rotatedHDiff;
if (newH < minHeight) {
newH = minHeight;
rotatedHDiff = initH - minHeight;
}
} else {
newH = initH + rotatedHDiff;
if (newH < minHeight) {
newH = minHeight;
rotatedHDiff = minHeight - initH;
}
}
//...
}
旁白:删除事件监听器
与问题的核心无关的是删除事件监听器。具有讽刺意味的是,删除事件监听器的代码在 mouseup 中留下了一个事件监听器。 :
window.addEventListener('mouseup', function() {
window.removeEventListener('mousemove', eventMoveHandler, false);
}, false);
这不是什么大问题,因为如果重复运行该函数并不会做太多事情,而且闭包并不会真正占用那么多内存。但要真正清理它,我们可以将其更改为:
window.addEventListener('mouseup', function eventEndHandler() {
window.removeEventListener('mousemove', eventMoveHandler, false);
window.removeEventListener('mouseup', eventEndHandler, false);
}, false);
结果
总之,它看起来像:

var box = document.getElementById("box");
var boxWrapper = document.getElementById("box-wrapper");
const minWidth = 40;
const minHeight = 40;


var initX, initY, mousePressX, mousePressY, initW, initH, initRotate;

function repositionElement(x, y) {
boxWrapper.style.left = x + 'px';
boxWrapper.style.top = y + 'px';
}

function resize(w, h) {
box.style.width = w + 'px';
box.style.height = h + 'px';
}


function getCurrentRotation(el) {
var st = window.getComputedStyle(el, null);
var tm = st.getPropertyValue("-webkit-transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform")
"none";
if (tm != "none") {
var values = tm.split('(')[1].split(')')[0].split(',');
var angle = Math.round(Math.atan2(values[1], values[0]) * (180 / Math.PI));
return (angle < 0 ? angle + 360 : angle);
}
return 0;
}

function rotateBox(deg) {
boxWrapper.style.transform = `rotate(${deg}deg)`;
}

// drag support
boxWrapper.addEventListener('mousedown', function (event) {
if (event.target.className.indexOf("dot") > -1) {
return;
}

initX = this.offsetLeft;
initY = this.offsetTop;
mousePressX = event.clientX;
mousePressY = event.clientY;


function eventMoveHandler(event) {
repositionElement(initX + (event.clientX - mousePressX),
initY + (event.clientY - mousePressY));
}

boxWrapper.addEventListener('mousemove', eventMoveHandler, false);
window.addEventListener('mouseup', function eventEndHandler() {
boxWrapper.removeEventListener('mousemove', eventMoveHandler, false);
window.removeEventListener('mouseup', eventEndHandler);
}, false);

}, false);
// done drag support

// handle resize
var rightMid = document.getElementById("right-mid");
var leftMid = document.getElementById("left-mid");
var topMid = document.getElementById("top-mid");
var bottomMid = document.getElementById("bottom-mid");

var leftTop = document.getElementById("left-top");
var rightTop = document.getElementById("right-top");
var rightBottom = document.getElementById("right-bottom");
var leftBottom = document.getElementById("left-bottom");

function resizeHandler(event, left = false, top = false, xResize = false, yResize = false) {
initX = boxWrapper.offsetLeft;
initY = boxWrapper.offsetTop;
mousePressX = event.clientX;
mousePressY = event.clientY;

initW = box.offsetWidth;
initH = box.offsetHeight;

initRotate = getCurrentRotation(boxWrapper);
var initRadians = initRotate * Math.PI / 180;
var cosFraction = Math.cos(initRadians);
var sinFraction = Math.sin(initRadians);
function eventMoveHandler(event) {
var wDiff = (event.clientX - mousePressX);
var hDiff = (event.clientY - mousePressY);
var rotatedWDiff = cosFraction * wDiff + sinFraction * hDiff;
var rotatedHDiff = cosFraction * hDiff - sinFraction * wDiff;

var newW = initW, newH = initH, newX = initX, newY = initY;

if (xResize) {
if (left) {
newW = initW - rotatedWDiff;
if (newW < minWidth) {
newW = minWidth;
rotatedWDiff = initW - minWidth;
}
} else {
newW = initW + rotatedWDiff;
if (newW < minWidth) {
newW = minWidth;
rotatedWDiff = minWidth - initW;
}
}
newX += 0.5 * rotatedWDiff * cosFraction;
newY += 0.5 * rotatedWDiff * sinFraction;
}

if (yResize) {
if (top) {
newH = initH - rotatedHDiff;
if (newH < minHeight) {
newH = minHeight;
rotatedHDiff = initH - minHeight;
}
} else {
newH = initH + rotatedHDiff;
if (newH < minHeight) {
newH = minHeight;
rotatedHDiff = minHeight - initH;
}
}
newX -= 0.5 * rotatedHDiff * sinFraction;
newY += 0.5 * rotatedHDiff * cosFraction;
}

resize(newW, newH);
repositionElement(newX, newY);
}


window.addEventListener('mousemove', eventMoveHandler, false);
window.addEventListener('mouseup', function eventEndHandler() {
window.removeEventListener('mousemove', eventMoveHandler, false);
window.removeEventListener('mouseup', eventEndHandler);
}, false);
}


rightMid.addEventListener('mousedown', e => resizeHandler(e, false, false, true, false));
leftMid.addEventListener('mousedown', e => resizeHandler(e, true, false, true, false));
topMid.addEventListener('mousedown', e => resizeHandler(e, false, true, false, true));
bottomMid.addEventListener('mousedown', e => resizeHandler(e, false, false, false, true));
leftTop.addEventListener('mousedown', e => resizeHandler(e, true, true, true, true));
rightTop.addEventListener('mousedown', e => resizeHandler(e, false, true, true, true));
rightBottom.addEventListener('mousedown', e => resizeHandler(e, false, false, true, true));
leftBottom.addEventListener('mousedown', e => resizeHandler(e, true, false, true, true));

// handle rotation
var rotate = document.getElementById("rotate");
rotate.addEventListener('mousedown', function (event) {
// if (event.target.className.indexOf("dot") > -1) {
// return;
// }

initX = this.offsetLeft;
initY = this.offsetTop;
mousePressX = event.clientX;
mousePressY = event.clientY;


var arrow = document.querySelector("#box");
var arrowRects = arrow.getBoundingClientRect();
var arrowX = arrowRects.left + arrowRects.width / 2;
var arrowY = arrowRects.top + arrowRects.height / 2;

function eventMoveHandler(event) {
var 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() {
window.removeEventListener('mousemove', eventMoveHandler, false);
window.removeEventListener('mouseup', eventEndHandler);
}, false);
}, false);

resize(300, 200);
repositionElement(200, 200);
.box {
background-color: #00BCD4;
position: relative;
user-select: none;
transform: translate(-50%, -50%);
}

.box-wrapper {
position: absolute;
transform-origin: top left;
user-select: none;
}

.dot {
height: 10px;
width: 10px;
background-color: #1E88E5;
position: absolute;
border-radius: 100px;
border: 1px solid white;
user-select: none;
}

.dot:hover {
background-color: #0D47A1;
}

.dot.left-top {
top: -5px;
left: -5px;
/* cursor: nw-resize; */
}

.dot.left-bottom {
bottom: -5px;
left: -5px;
/* cursor: sw-resize; */
}

.dot.right-top {
top: -5px;
right: -5px;
/* cursor: ne-resize; */
}

.dot.right-bottom {
bottom: -5px;
right: -5px;
/* cursor: se-resize; */
}

.dot.top-mid {
top: -5px;
left: calc(50% - 5px);
/* cursor: n-resize; */
}

.dot.left-mid {
left: -5px;
top: calc(50% - 5px);
/* cursor: w-resize; */
}

.dot.right-mid {
right: -5px;
top: calc(50% - 5px);
/* cursor: e-resize; */
}

.dot.bottom-mid {
bottom: -5px;
left: calc(50% - 5px);
/* cursor: s-resize; */
}

.dot.rotate {
top: -30px;
left: calc(50% - 5px);
cursor: url('https://findicons.com/files/icons/1620/crystal_project/16/rotate_ccw.png'), auto;
}

.rotate-link {
position: absolute;
width: 1px;
height: 15px;
background-color: #1E88E5;
top: -20px;
left: calc(50% + 0.5px);
z-index: -1;
}
<div class="box-wrapper" id="box-wrapper">
<div class="box" id="box">
<div class="dot rotate" id="rotate"></div>
<div class="dot left-top" id="left-top"></div>
<div class="dot left-bottom" id="left-bottom"></div>
<div class="dot top-mid" id="top-mid"></div>
<div class="dot bottom-mid" id="bottom-mid"></div>
<div class="dot left-mid" id="left-mid"></div>
<div class="dot right-mid" id="right-mid"></div>
<div class="dot right-bottom" id="right-bottom"></div>
<div class="dot right-top" id="right-top"></div>
<div class="rotate-link"></div>
</div>
</div>

关于javascript - 在 javascript 中创建可调整大小/可拖动/旋转的 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64690514/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com