javascript - 如何根据js中的父位置动态调整div大小
问题描述
我一直在研究如何动态调整 div 大小的代码。但是代码卡在 1 个元素上,我做了一些工作以转换为多个 div 大小调整器。
现在在调整大小时鼠标和 div 之间会出现一个空格,我的目标是确保每个元素都根据父位置分别调整大小,并使用精确的鼠标位置。任何会改变我的观点的方法都是适用的。不绑定到调整大小的节点,而是直接保持 div 边框也是可能的。
到目前为止我已经取得的成就:
- 使多个 div 可根据鼠标位置调整大小。
-在函数中保存第一个位置信息。
-计算孩子和父母之间的差异。
我想达到的目标:
- 确保支架在调整大小时保持在鼠标位置下,鼠标和 div 之间没有任何空间。
在几次尝试调整大小后,我看到父元素边距、填充...等导致出现空间。和 div 开始调整 div 和鼠标位置之间的空间。
我需要一个递归解决方案来正确调整和重新定位 div。
可能是另一种方法可用于仅计算父级内部的 x,y,w,h 但我需要一些关于如何用鼠标实现这一点的解释......
function resizeable() {
var resizers = document.querySelectorAll('.n, .s, .w, .e, .nw, .ne, .se, .sw');
const min = 40;
for (let i = 0; i < resizers.length; i++) {
const currentResizer = resizers[i];
const element = currentResizer.parentElement;
const parent = currentResizer.parentElement.parentElement;
let p;
let c;
let original_w = 0;
let original_h = 0;
let parent_x = 0;
let parent_y = 0;
let child_x = 0;
let child_y = 0;
let mouse_x = 0;
let mouse_y = 0;
let scale_x = 0;
let scale_y = 0;
// Mouse events
currentResizer.addEventListener('mousedown', function(e) {
first(e);
document.addEventListener('mousemove', resize);
document.addEventListener('mouseup', stopResize);
e.preventDefault();
});
// Log
function log(e){
var str = 'original_w['+original_w+'] original_h['+original_h+'] \n'+
'parent_x['+parent_x+'] parent_y['+parent_y+'] \n'+
'child_x['+child_x+'] child_y['+child_y+'] \n'+
'scale_x['+scale_x+'] scale_y['+scale_y+'] \n'+
'mouse_x['+mouse_x+'] mouse_y['+mouse_y+'] \n'+
'e.pageX['+e.pageX+'] e.pageY['+e.pageY+'] \n'+
'obj.left['+element.style.left+'] obj.top['+element.style.top+']';
console.log(str);
/**/
str = '<table>'+
'<tr>'+
'<th colspan="2">First Locations:</td>'+
'</tr>'+
'<tr>'+
'<td>original_w['+original_w+']</td>'+
'<td>original_h['+original_h+']</td>'+
'</tr>'+
'<tr>'+
'<td>parent_x['+parent_x+']</td>'+
'<td>parent_y['+parent_y+']</td>'+
'</tr>'+
'<tr>'+
'<td>child_x['+child_x+']</td>'+
'<td>child_y['+child_y+']</td>'+
'</tr>'+
'<tr>'+
'<td>scale_x['+scale_x+']</td>'+
'<td>scale_y['+scale_y+']</td>'+
'</tr>'+
'<tr>'+
'<td>mouse_x['+mouse_x+']</td>'+
'<td>mouse_y['+mouse_y+']</td>'+
'</tr>'+
'<tr>'+
'<td>e.pageX['+e.pageX+']</td>'+
'<td>e.pageY['+e.pageY+']</td>'+
'</tr>'+
'<tr>'+
'<td>obj.left['+element.style.left+']</td>'+
'<td>obj.top['+element.style.top+']</td>'+
'</tr>'
'</table>';
var m = document.getElementById("nfo"); // Debug element
m.innerHTML = str;
}
// First location & width
function first(e) {
c = element.getBoundingClientRect();
child_y = c.top;
child_x = c.left;
p = parent.getBoundingClientRect();
parent_y = p.top;
parent_x = p.left;
original_w = parseFloat(c.width).toFixed(2);
original_h = parseFloat(c.height).toFixed(2);
scale_y = parseFloat(c.height / element.offsetHeight).toFixed(2);
scale_x = parseFloat(c.width / element.offsetWidth).toFixed(2);
mouse_y = e.pageY;
mouse_x = e.pageX;
log(e);
}
// Resize process
function resize(e) {
element.style.position = "absolute";
if (currentResizer.classList.contains('se')) {
const width = e.pageX - child_x;
const height = e.pageY - child_y;
if (width > min) {
element.style.width = (width / scale_x) + 'px';
}
if (height > min) {
element.style.height = (height / scale_y) + 'px';
}
}
else if (currentResizer.classList.contains('sw')) {
const width = original_w - (e.pageX - child_x);
const height = e.pageY - child_y;
if (height > min) {
element.style.height = (height / scale_y) + 'px';
}
if (width > min) {
element.style.left = e.pageX - parent_x + 'px';
element.style.width = (width / scale_x) + 'px';
}
}
else if (currentResizer.classList.contains('ne')) {
const width = e.pageX - child_x;
const height = original_h - (e.pageY - mouse_y);
if (width > min) {
element.style.width = (width / scale_x) + 'px';
}
if (height > min) {
element.style.height = (height / scale_y) + 'px';
element.style.top = e.pageY - parent_y + 'px';
}
}
else if (currentResizer.classList.contains('nw')) {
const width = original_w - (e.pageX - child_x);
const height = original_h - (e.pageY - mouse_y);
if (width > min) {
element.style.left = e.pageX - parent_x + 'px';
element.style.width = (width / scale_x) + 'px';
}
if (height > min) {
element.style.height = (height / scale_y) + 'px';
element.style.top = e.pageY - parent_y + 'px';
}
}
else if (currentResizer.classList.contains('e')) {
const width = e.pageX - child_x;
if (width > min) {
element.style.width = (width / scale_x) + 'px';
}
}
else if (currentResizer.classList.contains('s')) {
const height = e.pageY - child_y;
if (height > min) {
element.style.height = (height / scale_y) + 'px';
}
}
else if (currentResizer.classList.contains('w')) {
const width = original_w - (e.pageX - child_x);
if (width > min) {
element.style.width = (width / scale_x) + 'px';
element.style.left = (e.pageX - parent_x) + 'px';
}
}
else if (currentResizer.classList.contains('n')) {
const height = original_h - (e.pageY - mouse_y);
if (height > min) {
element.style.height = (height / scale_y) + 'px';
element.style.top = e.pageY - parent_y + 'px';
}
}
log(e);
}
// When mouse released stop
function stopResize(e) {
first(e);
document.removeEventListener('mousemove', resize);
}
}
}
resizeable();
.another_element_on_the_way {
position: relative;
width: 100px;
height: 100px;
float: left;
}
#nfo {
position: relative;
float: left;
}
div {
position: absolute;
background-color: grey;
}
.holder {
float: left;
clear: left;
position: relative;
margin: 20px;
width: 550px;
height: 600px;
}
.scaled:hover:before, .regular:hover:before {
content: '';
position: absolute;
top: -3px;
left: -3px;
width: calc(100% - 6px);
height: calc(100% - 6px);
border: 6px solid #ccc;
}
.regular:nth-child(1){
top: 5px;
left: 5px;
width: 120px;
height: 120px;
background-color: red;
}
.regular:nth-child(3){
top: 270px;
left: 60px;
width: 240px;
height: 180px;
background-color: blue;
}
.scaled {
top: 150px;
left: 25px;
width: 160px;
height: 160px;
transform: scale(0.6) translate(0, 0);
transform-origin: top left 0px;
background-color: green;
overflow: visible;
}
.previewHeader {
position: absolute;
top: 10px;
left: 10px;
background-color: #eee;
border: 1px solid #dedede;
}
.n, .s, .w, .e, .nw, .ne, .se, .sw {
position: absolute;
width: 18px;
height: 18px;
border: 1px solid grey;
border-radius: 20px;
background-color: #fff;
}
.n:hover, .s:hover, .w:hover, .e:hover,
.nw:hover, .ne:hover, .se:hover, .sw:hover {
background-color: red;
}
.nw {
top: -10px;
left: -10px;
cursor: nw-resize;
}
.ne {
top: -10px;
left: calc(100% - 10px);
cursor: ne-resize;
}
.sw {
top: calc(100% - 10px);
left: -10px;
cursor: sw-resize;
}
.se {
top: calc(100% - 10px);
left: calc(100% - 10px);
cursor: se-resize;
}
.n {
top: -10px;
left: calc(50% - 10px);
cursor: n-resize;
}
.w {
top: calc(50% - 10px);
left: -10px;
cursor: w-resize;
}
.e {
top: calc(50% - 10px);
left: calc(100% - 10px);
cursor: e-resize;
}
.s {
top: calc(100% - 10px);
left: calc(50% - 10px);
cursor: s-resize;
}
<div class="another_element_on_the_way">
</div>
<div class="another_element_on_the_way">
</div>
<div class="another_element_on_the_way">
</div>
<div class="another_element_on_the_way">
</div>
<div class="another_element_on_the_way">
</div>
<div class="another_element_on_the_way">
</div>
<div class="holder">
<div class="regular">
<div class="previewHeader">Resizable</div>
<div class="nw"></div>
<div class="ne"></div>
<div class="sw"></div>
<div class="se"></div>
<div class="n"></div>
<div class="s"></div>
<div class="w"></div>
<div class="e"></div>
</div>
<div class="scaled">
<div class="previewHeader">Scaled</div>
<div class="nw"></div>
<div class="ne"></div>
<div class="sw"></div>
<div class="se"></div>
<div class="n"></div>
<div class="s"></div>
<div class="w"></div>
<div class="e"></div>
</div>
<div class="regular">
<div class="previewHeader">Resizable</div>
<div class="nw"></div>
<div class="ne"></div>
<div class="sw"></div>
<div class="se"></div>
<div class="n"></div>
<div class="s"></div>
<div class="w"></div>
<div class="e"></div>
</div>
</div>
<div id="nfo"> X & Y</div>
还有一个更清晰的代码笔: https ://codepen.io/BerkerYuceer/pen/gOYwqdb
解决方案
您可以使用正则表达式getComputedStyle
获取transform
值并将1 / scale
值应用于所需的坐标。我只能为East
持有人解决这个问题(我对坐标几何太笨了:P):
let match = getComputedStyle(element)
.transform.match(/matrix\((-?\d*\.?\d+),\s*0,\s*0,\s*(-?\d*\.?\d+),\s*0,\s*0\)/);
let scale = 1; //for .regular folks
if (match && +match[1] != 0)
scale = 1 / +match[1]; //because we need to unapply the transformation
样本:
function resizeable() {
var resizers = document.querySelectorAll('.n, .s, .w, .e, .nw, .ne, .se, .sw');
const min = 40;
for (let i = 0; i < resizers.length; i++) {
const currentResizer = resizers[i];
const element = currentResizer.parentElement;
const parent = currentResizer.parentElement.parentElement;
let p;
let c;
let original_w = 0;
let original_h = 0;
let parent_x = 0;
let parent_y = 0;
let child_x = 0;
let child_y = 0;
let mouse_x = 0;
let mouse_y = 0;
let scale_x = 0;
let scale_y = 0;
let scroll_x = 0;
let scroll_y = 0;
// Mouse events
currentResizer.addEventListener('mousedown', function(e) {
first(e);
document.addEventListener('mousemove', resize);
document.addEventListener('mouseup', stopResize);
e.preventDefault();
});
// Log
function log(e){
var str = 'original_w['+original_w+'] original_h['+original_h+'] \n'+
'parent_x['+parent_x+'] parent_y['+parent_y+'] \n'+
'scroll_x['+scroll_x+'] scroll_y['+scroll_y+'] \n'+
'child_x['+child_x+'] child_y['+child_y+'] \n'+
'scale_x['+scale_x+'] scale_y['+scale_y+'] \n'+
'mouse_x['+mouse_x+'] mouse_y['+mouse_y+'] \n'+
'e.pageX['+e.pageX+'] e.pageY['+e.pageY+'] \n'+
'obj.left['+element.style.left+'] obj.top['+element.style.top+']';
console.log(str);
/**/
str = '<table>'+
'<tr>'+
'<th colspan="2">First Locations:</td>'+
'</tr>'+
'<tr>'+
'<td>original_w['+original_w+']</td>'+
'<td>original_h['+original_h+']</td>'+
'</tr>'+
'<tr>'+
'<td>parent_x['+parent_x+']</td>'+
'<td>parent_y['+parent_y+']</td>'+
'</tr>'+
'<tr>'+
'<td>scroll_x['+scroll_x+']</td>'+
'<td>scroll_y['+scroll_y+']</td>'+
'</tr>'+
'<tr>'+
'<td>child_x['+child_x+']</td>'+
'<td>child_y['+child_y+']</td>'+
'</tr>'+
'<tr>'+
'<td>scale_x['+scale_x+']</td>'+
'<td>scale_y['+scale_y+']</td>'+
'</tr>'+
'<tr>'+
'<td>mouse_x['+mouse_x+']</td>'+
'<td>mouse_y['+mouse_y+']</td>'+
'</tr>'+
'<tr>'+
'<td>e.pageX['+e.pageX+']</td>'+
'<td>e.pageY['+e.pageY+']</td>'+
'</tr>'+
'<tr>'+
'<td>obj.left['+element.style.left+']</td>'+
'<td>obj.top['+element.style.top+']</td>'+
'</tr>'
'</table>';
var m = document.getElementById("nfo"); // Debug element
m.innerHTML = str;
}
// First location & width
function first(e) {
c = element.getBoundingClientRect();
child_y = c.top;
child_x = c.left;
p = parent.getBoundingClientRect();
parent_y = p.top;
parent_x = p.left;
scroll_y = window.scrollY;
scroll_x = window.scrollX;
original_w = parseFloat(c.width).toFixed(2);
original_h = parseFloat(c.height).toFixed(2);
scale_y = parseFloat(c.height / element.offsetHeight).toFixed(2);
scale_x = parseFloat(c.width / element.offsetWidth).toFixed(2);
mouse_y = e.pageY - scroll_y;
mouse_x = e.pageX - scroll_x;
log(e);
}
// Resize process
function resize(e) {
element.style.position = "absolute";
if (currentResizer.classList.contains('se')) {
const width = e.pageX - scroll_x - child_x ;
const height = e.pageY - scroll_y - child_y ;
if (width > min) {
element.style.width = (width / scale_x) + 'px';
}
if (height > min) {
element.style.height = (height / scale_y) + 'px';
}
}
else if (currentResizer.classList.contains('sw')) {
const width = original_w - (e.pageX - scroll_x - child_x);
const height = e.pageY - scroll_y - child_y;
if (height > min) {
element.style.height = (height / scale_y) + 'px';
}
if (width > min) {
element.style.left = e.pageX - scroll_x - parent_x + 'px';
element.style.width = (width / scale_x) + 'px';
}
}
else if (currentResizer.classList.contains('ne')) {
const width = e.pageX - child_x - scroll_x;
const height = original_h - (e.pageY - mouse_y - scroll_y);
if (width > min) {
element.style.width = (width / scale_x) + 'px';
}
if (height > min) {
element.style.height = (height / scale_y) + 'px';
element.style.top = e.pageY - parent_y - scroll_y + 'px';
}
}
else if (currentResizer.classList.contains('nw')) {
const width = original_w - (e.pageX - scroll_x - child_x);
const height = original_h - (e.pageY - scroll_y - mouse_y);
if (width > min) {
element.style.left = e.pageX - parent_x - scroll_x + 'px';
element.style.width = (width / scale_x) + 'px';
}
if (height > min) {
element.style.height = (height / scale_y) + 'px';
element.style.top = e.pageY - parent_y - scroll_y + 'px';
}
}
else if (currentResizer.classList.contains('e')) {
const width = e.pageX - scroll_x - child_x;
if (width > min) {
element.style.width = (width / scale_x) + 'px';
}
}
else if (currentResizer.classList.contains('s')) {
const height = e.pageY - scroll_y - child_y;
if (height > min) {
element.style.height = (height / scale_y) + 'px';
}
}
else if (currentResizer.classList.contains('w')) {
const width = original_w - (e.pageX - scroll_x - child_x);
if (width > min) {
element.style.width = (width / scale_x) + 'px';
element.style.left = (e.pageX - scroll_x - parent_x) + 'px';
}
}
else if (currentResizer.classList.contains('n')) {
const height = original_h - (e.pageY - scroll_y - mouse_y);
if (height > min) {
element.style.height = (height / scale_y) + 'px';
element.style.top = e.pageY - scroll_y - parent_y + 'px';
}
}
log(e);
}
// When mouse released stop
function stopResize(e) {
first(e);
document.removeEventListener('mousemove', resize);
}
}
}
resizeable();
body {
width: 1200px;
}
.another_element_on_the_top {
position: relative;
float: left;
margin: 10px;
width: 100px;
height: 100px;
}
.another_element_on_the_left {
position: relative;
float: left;
clear: left;
margin: 10px;
width: 100px;
height: 100px;
}
#nfo {
position: relative;
float: left;
}
div {
position: absolute;
background-color: grey;
}
.holder {
float: left;
position: relative;
margin: -470px 20px 20px 20px;
width: 550px;
height: 600px;
}
.scaled:hover:before, .regular:hover:before {
content: '';
position: absolute;
top: -3px;
left: -3px;
width: calc(100% - 6px);
height: calc(100% - 6px);
border: 6px solid #ccc;
}
.regular:nth-child(1){
top: 5px;
left: 5px;
width: 120px;
height: 120px;
background-color: red;
}
.regular:nth-child(3){
top: 270px;
left: 60px;
width: 240px;
height: 180px;
background-color: blue;
}
.scaled {
top: 150px;
left: 25px;
width: 160px;
height: 160px;
transform: scale(0.6) translate(0, 0);
transform-origin: top left 0px;
background-color: green;
overflow: visible;
}
.previewHeader {
position: absolute;
top: 10px;
left: 10px;
background-color: #eee;
border: 1px solid #dedede;
}
.n, .s, .w, .e, .nw, .ne, .se, .sw {
position: absolute;
width: 18px;
height: 18px;
border: 1px solid grey;
border-radius: 20px;
background-color: #fff;
}
.n:hover, .s:hover, .w:hover, .e:hover,
.nw:hover, .ne:hover, .se:hover, .sw:hover {
background-color: red;
}
.nw {
top: -10px;
left: -10px;
cursor: nw-resize;
}
.ne {
top: -10px;
left: calc(100% - 10px);
cursor: ne-resize;
}
.sw {
top: calc(100% - 10px);
left: -10px;
cursor: sw-resize;
}
.se {
top: calc(100% - 10px);
left: calc(100% - 10px);
cursor: se-resize;
}
.n {
top: -10px;
left: calc(50% - 10px);
cursor: n-resize;
}
.w {
top: calc(50% - 10px);
left: -10px;
cursor: w-resize;
}
.e {
top: calc(50% - 10px);
left: calc(100% - 10px);
cursor: e-resize;
}
.s {
top: calc(100% - 10px);
left: calc(50% - 10px);
cursor: s-resize;
}
<div class="another_element_on_the_top">
</div>
<div class="another_element_on_the_top">
</div>
<div class="another_element_on_the_top">
</div>
<div class="another_element_on_the_top">
</div>
<div class="another_element_on_the_top">
</div>
<div class="another_element_on_the_top">
</div>
<div class="another_element_on_the_top">
</div>
<div class="another_element_on_the_top">
</div>
<div class="another_element_on_the_top">
</div>
<div class="another_element_on_the_top">
</div>
<div class="another_element_on_the_left">
</div>
<div class="another_element_on_the_left">
</div>
<div class="another_element_on_the_left">
</div>
<div class="another_element_on_the_left">
</div>
<div class="another_element_on_the_left">
</div>
<div class="holder">
<div class="regular">
<div class="previewHeader">Resizable</div>
<div class="nw"></div>
<div class="ne"></div>
<div class="sw"></div>
<div class="se"></div>
<div class="n"></div>
<div class="s"></div>
<div class="w"></div>
<div class="e"></div>
</div>
<div class="scaled">
<div class="previewHeader">Scaled</div>
<div class="nw"></div>
<div class="ne"></div>
<div class="sw"></div>
<div class="se"></div>
<div class="n"></div>
<div class="s"></div>
<div class="w"></div>
<div class="e"></div>
</div>
<div class="regular">
<div class="previewHeader">Resizable</div>
<div class="nw"></div>
<div class="ne"></div>
<div class="sw"></div>
<div class="se"></div>
<div class="n"></div>
<div class="s"></div>
<div class="w"></div>
<div class="e"></div>
</div>
</div>
<div id="nfo"> X & Y</div>
或在另一个问题中使用此方法。
var element = document.querySelector('...');
var scaleX = element.getBoundingClientRect().width / element.offsetWidth;
“这很有效,因为 getBoundingClientRect 返回实际尺寸,而 offsetWidth/Height 是未缩放的尺寸。”
编辑:添加了 window.scrollX/Y。现在它可用于滚动页面。
以供将来在页面的任何部分参考,它可以正常工作。即使对象被缩放。
推荐阅读
- javascript - 有没有办法从我的例子中检查和取消选中“复选框”?
- gridview - 如何在函数中使用 $categoryId
- python - Python - 字典 - 如果循环变量没有改变
- blob - react-native 将文件从本地存储上传到 firebase 网络请求失败
- python - 气流,从 dag 调用 dags 会导致重复的 dagruns
- java - 我想在 Android Studio 的 Retrofit 中将温度值从单位转换为英制
- visual-studio-2019 - Visual Studio 2019 在 .Net Core 3.1 解决方案中创建 .NetStandard 2.0.3 库而不是 2.1
- spring - 在 minikube 中安装后 Spring Cloud Dataflow UI 异常
- haskell - 如何解决 Haskell 代码错误“<>' is not a (visible) method of class `Monoid'”?
- c# - C# Entity Framework 和 DB - 从 smallint 更改为 int