說(shuō)在前面
元素拖拽和縮放現(xiàn)在也是一個(gè)很常見(jiàn)的功能,讓我們從實(shí)現(xiàn)div元素的拖拽縮放開(kāi)始來(lái)了解元素拖拽縮放的具體原理和實(shí)現(xiàn)方法吧。
效果展示
實(shí)現(xiàn)步驟
畫(huà)一個(gè)div
首先我們需要先畫(huà)一個(gè)div,并給它8個(gè)方位(上、下、左、右、左上、右上、右下、左下)加上縮放錨點(diǎn),我們可以通過(guò)這幾個(gè)錨點(diǎn)來(lái)對(duì)div進(jìn)行縮放,具體代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./index.css" />
</head>
<body>
<div class="box" id="drag">
<div class="resize-handle top-left"></div>
<div class="resize-handle top"></div>
<div class="resize-handle top-right"></div>
<div class="resize-handle right"></div>
<div class="resize-handle bottom-right"></div>
<div class="resize-handle bottom"></div>
<div class="resize-handle bottom-left"></div>
<div class="resize-handle left"></div>
</div>
</body>
<script src="./index.js"></script>
</html>
加點(diǎn)css
將錨點(diǎn)定位到其特定的位置上,并設(shè)置不同的鼠標(biāo)指示效果,具體代碼如下:
.box {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 200px;
height: 200px;
background-color: #f0f0f0;
cursor: move;
}
.resize-handle {
position: absolute;
width: 10px;
height: 10px;
background-color: #000;
}
.top-left {
top: -5px;
left: -5px;
cursor: nw-resize;
}
.top {
top: -5px;
left: calc(50% - 5px);
cursor: ns-resize;
}
.top-right {
top: -5px;
right: -5px;
cursor: ne-resize;
}
.right {
top: calc(50% - 5px);
right: -5px;
cursor: ew-resize;
}
.bottom-right {
bottom: -5px;
right: -5px;
cursor: se-resize;
}
.bottom {
bottom: -5px;
left: calc(50% - 5px);
cursor: ns-resize;
}
.bottom-left {
bottom: -5px;
left: -5px;
cursor: sw-resize;
}
.left {
top: calc(50% - 5px);
left: -5px;
cursor: ew-resize;
}
效果是這樣的:
完成拖拽和縮放功能
拖拽功能
我們需要鼠標(biāo)在div內(nèi)按下不松開(kāi)的時(shí)候,div可以跟著鼠標(biāo)的位置移動(dòng),這里我們可以從鼠標(biāo)的三種事件的觸發(fā)來(lái)入手:
首先我們需要監(jiān)聽(tīng)鼠標(biāo)按下事件。
const dragElement = document.getElementById("drag");
dragElement.addEventListener("mousedown", startDrag);
判斷點(diǎn)擊事件是否為錨點(diǎn)觸發(fā),是的話則不執(zhí)行拖拽邏輯。這里我們可以通過(guò)樣式名來(lái)判斷,錨點(diǎn)我們都給它加上了resize-handle
,我們只需要判斷樣式名是否包含resize-handle
就可以。
function startDrag(event) {
event.preventDefault();
const currentHandle = event.target;
const isResizeHandle = currentHandle.className.includes("resize-handle");
if (isResizeHandle) return;
}
記錄下鼠標(biāo)點(diǎn)擊的坐標(biāo)及拖拽元素所在位置,用于后面計(jì)算鼠標(biāo)移動(dòng)距離和對(duì)拖拽元素進(jìn)行移動(dòng),監(jiān)聽(tīng)鼠標(biāo)移動(dòng)和抬起事件
const startX = event.clientX;
const startY = event.clientY;
const startLeft = dragElement.offsetLeft;
const startTop = dragElement.offsetTop;
document.addEventListener("mousemove", drag);
document.addEventListener("mouseup", stopDrag);
- 2、鼠標(biāo)移動(dòng)(mousemove)
鼠標(biāo)移動(dòng)的時(shí)候計(jì)算鼠標(biāo)當(dāng)前位置與鼠標(biāo)點(diǎn)擊位置的相對(duì)距離,將拖拽元素的位置也移動(dòng)相應(yīng)的相對(duì)距離
function drag(event) {
const dx = event.clientX - startX;
const dy = event.clientY - startY;
const newLeft = startLeft + dx;
const newTop = startTop + dy;
dragElement.style.left = newLeft + "px";
dragElement.style.top = newTop + "px";
}
鼠標(biāo)抬起的時(shí)候?qū)⑹髽?biāo)移動(dòng)和鼠標(biāo)抬起的監(jiān)聽(tīng)事件移除。
function stopDrag() {
document.removeEventListener("mousemove", drag);
document.removeEventListener("mouseup", stopDrag);
}
到這里我們就完成了一個(gè)最基本的可拖拽元素了,接下來(lái)就該來(lái)實(shí)現(xiàn)縮放功能了。
縮放功能
我們希望在點(diǎn)擊縮放錨點(diǎn)進(jìn)行移動(dòng)的時(shí)候,元素會(huì)隨著鼠標(biāo)繼續(xù)縮小或放大,這里我們?nèi)匀皇且獜氖髽?biāo)觸發(fā)的三種事件來(lái)入手:
我們需要監(jiān)聽(tīng)所有錨點(diǎn)的鼠標(biāo)按下事件,首先我們需要先獲取所有的錨點(diǎn)元素:
const resizeHandles = document.getElementsByClassName("resize-handle");
再監(jiān)聽(tīng)所有錨點(diǎn)元素的鼠標(biāo)按下事件:
Array.from(resizeHandles).forEach((handle) => {
handle.addEventListener("mousedown", startResize);
});
記錄鼠標(biāo)按下的初始位置及縮放元素的初始位置和寬高,便于后面對(duì)縮放元素進(jìn)行縮放操作,并為縮放元素加上鼠標(biāo)移動(dòng)和鼠標(biāo)抬起的監(jiān)聽(tīng)事件。
function startResize(event) {
event.preventDefault();
const currentHandle = event.target;
const direction = currentHandle.className.split(" ")[1];
startX = event.clientX;
startY = event.clientY;
startWidth = dragElement.offsetWidth;
startHeight = dragElement.offsetHeight;
startLeft = dragElement.offsetLeft;
startTop = dragElement.offsetTop;
document.addEventListener("mousemove", resize);
document.addEventListener("mouseup", stopResize);
}
- 2、鼠標(biāo)移動(dòng)(mousemove)
鼠標(biāo)移動(dòng)的時(shí)候我們需要判斷當(dāng)前是從哪個(gè)錨點(diǎn)觸發(fā)的縮放事件,我們可以從錨點(diǎn)的樣式名className
來(lái)做區(qū)分。
const currentHandle = event.target;
const direction = currentHandle.className.split(" ")[1];
不同錨點(diǎn)我們需要對(duì)元素進(jìn)行不同的操作,我們可以分為四個(gè)方位來(lái)進(jìn)行判斷并區(qū)分操作:
(1)左邊
判斷樣式名是否包含left
,左邊錨點(diǎn)會(huì)觸發(fā)元素的寬度變化及左右位置的變化,這時(shí)候鼠標(biāo)左移相對(duì)于元素來(lái)說(shuō)是放大,右移相對(duì)于元素來(lái)說(shuō)是縮小,所以元素的寬度應(yīng)該減去鼠標(biāo)的位移距離。
if (direction.includes("left")) {
width = startWidth - dx + "px";
left = startLeft + dx / 2 + "px";
}
(2)右邊
判斷樣式名是否包含right
,右邊錨點(diǎn)會(huì)觸發(fā)元素的寬度變化及左右位置的變化,這時(shí)候鼠標(biāo)左移相對(duì)于元素來(lái)說(shuō)是縮小,右移相對(duì)于元素來(lái)說(shuō)是放大,所以元素的寬度應(yīng)該加上鼠標(biāo)的位移距離。
if (direction.includes("right")) {
width = startWidth + dx + "px";
left = startLeft + dx / 2 + "px";
}
(3)上邊
判斷樣式名是否包含top
,上邊錨點(diǎn)會(huì)觸發(fā)元素的高度變化及上下位置的變化,這時(shí)候鼠標(biāo)上移相對(duì)于元素來(lái)說(shuō)是放大,下移相對(duì)于元素來(lái)說(shuō)是縮小,所以元素的高度應(yīng)該減去鼠標(biāo)的位移距離。
if (direction.includes("top")) {
height = startHeight - dy + "px";
top = startTop + dy / 2 + "px";
}
(4)下邊
判斷樣式名是否包含bottom
,下邊錨點(diǎn)會(huì)觸發(fā)元素的高度變化及上下位置的變化,這時(shí)候鼠標(biāo)上移相對(duì)于元素來(lái)說(shuō)是縮小,下移相對(duì)于元素來(lái)說(shuō)是放大,所以元素的高度應(yīng)該加上鼠標(biāo)的位移距離。
if (direction.includes("bottom")) {
height = startHeight + dy + "px";
top = startTop + dy / 2 + "px";
}
我們還需要判斷當(dāng)前元素的寬度和高度是否小于等于0,等于0的時(shí)候我們不再對(duì)其進(jìn)行縮放操作,大于0則對(duì)元素進(jìn)行賦值操作。
if (parseInt(width) <= 0 || parseInt(height) <= 0) return;
dragElement.style.width = width;
dragElement.style.height = height;
dragElement.style.left = left;
dragElement.style.top = top;
完整代碼如下:
function resize(event) {
const dx = event.clientX - startX;
const dy = event.clientY - startY;
let width = startWidth,
height = startHeight,
left = startLeft,
top = startTop;
if (direction.includes("left")) {
width = startWidth - dx + "px";
left = startLeft + dx / 2 + "px";
}
if (direction.includes("right")) {
width = startWidth + dx + "px";
left = startLeft + dx / 2 + "px";
}
if (direction.includes("top")) {
height = startHeight - dy + "px";
top = startTop + dy / 2 + "px";
}
if (direction.includes("bottom")) {
height = startHeight + dy + "px";
top = startTop + dy / 2 + "px";
}
if (parseInt(width) <= 0 || parseInt(height) <= 0) return;
dragElement.style.width = width;
dragElement.style.height = height;
dragElement.style.left = left;
dragElement.style.top = top;
}
鼠標(biāo)抬起的時(shí)候?qū)⑹髽?biāo)移動(dòng)和鼠標(biāo)抬起的監(jiān)聽(tīng)事件移除。
function stopResize() {
document.removeEventListener("mousemove", resize);
document.removeEventListener("mouseup", stopResize);
}
到這里我們就完成了一個(gè)最基本的可拖拽縮放的元素了。
完整代碼
完整的JavaScrip代碼如下:
const dragElement = document.getElementById("drag");
// 拖拽功能
dragElement.addEventListener("mousedown", startDrag);
function startDrag(event) {
event.preventDefault();
const currentHandle = event.target;
const isResizeHandle = currentHandle.className.includes("resize-handle");
if (isResizeHandle) return;
const startX = event.clientX;
const startY = event.clientY;
const startLeft = dragElement.offsetLeft;
const startTop = dragElement.offsetTop;
document.addEventListener("mousemove", drag);
document.addEventListener("mouseup", stopDrag);
function drag(event) {
const dx = event.clientX - startX;
const dy = event.clientY - startY;
const newLeft = startLeft + dx;
const newTop = startTop + dy;
dragElement.style.left = newLeft + "px";
dragElement.style.top = newTop + "px";
}
function stopDrag() {
document.removeEventListener("mousemove", drag);
document.removeEventListener("mouseup", stopDrag);
}
}
// 縮放功能
const resizeHandles = document.getElementsByClassName("resize-handle");
Array.from(resizeHandles).forEach((handle) => {
handle.addEventListener("mousedown", startResize);
});
function startResize(event) {
event.preventDefault();
const currentHandle = event.target;
const direction = currentHandle.className.split(" ")[1];
const startX = event.clientX;
const startY = event.clientY;
const startWidth = dragElement.offsetWidth;
const startHeight = dragElement.offsetHeight;
const startLeft = dragElement.offsetLeft;
const startTop = dragElement.offsetTop;
document.addEventListener("mousemove", resize);
document.addEventListener("mouseup", stopResize);
function resize(event) {
const dx = event.clientX - startX;
const dy = event.clientY - startY;
let width = startWidth,
height = startHeight,
left = startLeft,
top = startTop;
if (direction.includes("left")) {
width = startWidth - dx + "px";
left = startLeft + dx / 2 + "px";
}
if (direction.includes("right")) {
width = startWidth + dx + "px";
left = startLeft + dx / 2 + "px";
}
if (direction.includes("top")) {
height = startHeight - dy + "px";
top = startTop + dy / 2 + "px";
}
if (direction.includes("bottom")) {
height = startHeight + dy + "px";
top = startTop + dy / 2 + "px";
}
if (parseInt(width) <= 0 || parseInt(height) <= 0) return;
dragElement.style.width = width;
dragElement.style.height = height;
dragElement.style.left = left;
dragElement.style.top = top;
}
function stopResize() {
document.removeEventListener("mousemove", resize);
document.removeEventListener("mouseup", stopResize);
}
}
該文章在 2023/11/1 9:47:47 編輯過(guò)