Создайте перетаскиваемое и изменяемое по размеру окно, которое выглядит и ощущается как окно в настольной ОС.
Весь код расположен ниже. Давайте вкратце рассмотрим HTML, а затем я объясню, что такое JS.
HTML
<div class="window">
<div class="resizer corner tl"></div>
<div class="resizer corner tr"></div>
<div class="resizer corner bl"></div>
<div class="resizer corner br"></div>
<div class="resizer t"></div>
<div class="resizer b"></div>
<div class="resizer l"></div>
<div class="resizer r"></div>
<div class="body">
<div class="topbar">
<div class="btns">
<div></div>
<div></div>
<div></div>
</div>
</div>
<!-- your content here -->
</div>
</div>
Здесь у нас есть 4 органайзера по углам и 4 по бокам окна. Мы сможем перетаскивать окно, удерживая его верхнюю панель.
JS
Объявим переменные-селекторы окна и топбара.
const xwindow = document.querySelector(".window")
const topbar = document.querySelector(".topbar")
Перетаскивание
Мы сможем перетаскивать окно, удерживая кнопку мыши и перемещая его. Слушатель mousemove
будет добавлен только при срабатывании mousedown
. Поэтому нам также нужен mouseup
, чтобы удалить mousemove
при отпускании кнопки мыши.
Давайте теперь добавим слушателя mousedown
в верхнюю панель и создадим функцию mousedown()
.
//topbar can be whatever you want to hold while dragging
topbar.addEventListener("mousedown", mousedown)
function mousedown(){
window.addEventListener("mousemove", mousemove)
window.addEventListener("mouseup", mouseup)
function mousemove(){}
function mouseup(){
window.removeEventListener("mousemove", mousemove)
window.removeEventListener("mouseup", mouseup)
}
}
Итак, теперь мы хотим, чтобы JS отслеживал положение курсора и перемещал окна при перемещении курсора. Для этого нам понадобится e
(или event
), которая кроме всего прочего хранит информацию о положении курсора. Передадим его в качестве аргумента функции.
function mousedown(e){
...
function mousemove(e){}
...
}
Чтобы изменить положение окна, мы можем сложить разницу между новым и предыдущим положением курсора и добавить эту разницу к левому и верхнему значениям окна (наше окно абсолютно позиционировано). Получим предыдущие значения координат курсора. До того, как мы переместим курсор, эти значения будут просто текущими значениями.
function mousedown(e){
...
let prevX = e.clientX
let prevY = e.clientY
function mousemove(e){}
...
}
Теперь найдем разницу между новым и предыдущим положениями.
...
let prevX = e.clientX
let prevY = e.clientY
function mousemove(e){
let newX = e.clientX - prevX
let newY = e.clientY - prevY
}
...
Метод JavaScript getBoundingClientRect()
возвращает объект DOMRect, предоставляющий информацию о размере элемента и его положении относительно области просмотра. С его помощью мы можем получить значения свойств top и left и установить новые значения.
function mousemove(e){
let newX = e.clientX - prevX
let newY = e.clientY - prevY
const rect = xwindow.getBoundingClientRect()
xwindow.style.left = rect.left + newX + "px"
xwindow.style.top = rect.top + newY + "px"
prevX = e.clientX
prevY = e.clientY
}
Поздравляем! Теперь мы можем перетаскивать наше окно.
Изменение размеров
Давайте воспользуемся всеми возможностями изменения размеров.
const resizers = document.querySelectorAll(".resizer")
Как и в случае с топбаром, мы хотим изменять размер окна только при удержании ресайзера. Поэтому давайте сделаем то же самое, но в цикле for
, чтобы применить его к каждому изменению размера.
for (let resizer of resizers){
resizer.addEventListener("mousedown", mousedown)
function mousedown(e){
window.addEventListener("mousemove", mousemove)
window.addEventListener("mouseup", mouseup)
let prevX = e.clientX
let prevY = e.clientY
function mousemove(e){
const rect = xwindow.getBoundingClientRect()
prevX = e.clientX
prevY = e.clientY
}
function mouseup(){
window.removeEventListener("mousemove", mousemove)
window.removeEventListener("mouseup", mouseup)
}
}
}
Теперь нам нужно сообщить JS, какой именно ресайзер мы держим. Мы можем легко проверить это по их классу. Например, tl
означает «top-left». Чтобы проверить, имеет ли текущий ресайзер такой класс, сначала нужно объявить currentResiser
и присвоить его e.target (элементу, который мы держим).
...
let prevY = e.clientY
let currentResizer = e.target
...
Чтобы проверить, есть ли какой-нибудь класс в списке классов ресайзера, мы используем currentResizer.classList.contains("tl")
в операторе if
. Если да, то нам нужно вычислить новый размер окон. Также, если это верхний, левый или верхне-левый ресайзер, нужно изменить top
и left
.
...
function mousemove(e){
const rect = xwindow.getBoundingClientRect()
if(currentResizer.classList.contains("br")){
xwindow.style.width = rect.width + (e.clientX - prevX) + "px"
xwindow.style.height = rect.height + (e.clientY - prevY) + "px"
}
else if(currentResizer.classList.contains("bl")){
xwindow.style.width = rect.width + (prevX - e.clientX) + "px"
xwindow.style.height = rect.height + (e.clientY - prevY) + "px"
xwindow.style.left = rect.left + (e.clientX - prevX) + "px"
}
else if(currentResizer.classList.contains("tr")){
xwindow.style.width = rect.width + (e.clientX - prevX) + "px"
xwindow.style.height = rect.height + (prevY - e.clientY) + "px"
xwindow.style.top = rect.top + (e.clientY - prevY) + "px"
}
else if(currentResizer.classList.contains("tl")){
xwindow.style.width = rect.width + (prevX - e.clientX) + "px"
xwindow.style.height = rect.height + (prevY - e.clientY) + "px"
xwindow.style.top = rect.top + (e.clientY - prevY) + "px"
xwindow.style.left = rect.left + (e.clientX - prevX) + "px"
}
else if(currentResizer.classList.contains("t")){
xwindow.style.height = rect.height + (prevY - e.clientY) + "px"
xwindow.style.top = rect.top + (e.clientY - prevY) + "px"
}
else if(currentResizer.classList.contains("b")){
xwindow.style.height = rect.height + (e.clientY - prevY) + "px"
}
else if(currentResizer.classList.contains("l")){
xwindow.style.width = rect.width + (prevX - e.clientX) + "px"
xwindow.style.left = rect.left + (e.clientX - prevX) + "px"
}
else if(currentResizer.classList.contains("r")){
xwindow.style.width = rect.width + (e.clientX - prevX) + "px"
}
prevX = e.clientX
prevY = e.clientY
}
...
Вуаля! Теперь мы также можем изменять размер.
Код
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="style.css">
<script defer src="index.js"></script>
</head>
<body>
<div class="window">
<div class="resizer corner tl"></div>
<div class="resizer corner tr"></div>
<div class="resizer corner bl"></div>
<div class="resizer corner br"></div>
<div class="resizer t"></div>
<div class="resizer b"></div>
<div class="resizer l"></div>
<div class="resizer r"></div>
<div class="body">
<div class="topbar">
<div class="btns">
<div></div>
<div></div>
<div></div>
</div>
</div>
<!-- your content here -->
</div>
</div>
</body>
</html>
SCSS
*{
margin: 0;
box-sizing: border-box;
}
.window{
width: 600px;
height: 400px;
min-width: 300px;
min-height: 200px;
position: absolute;
.resizer{
position: absolute;
z-index: 1;
width: 22px;
height: 22px;
background: #41a94c80; //delete
&.corner{
z-index: 2;
&.tl{ cursor: nw-resize; top: -5px; left: -5px; }
&.tr{ cursor: ne-resize; top: -5px; right: -5px; }
&.bl{ cursor: sw-resize; bottom: -5px; left: -5px; }
&.br{ cursor: se-resize; bottom: -5px; right: -5px; }
}
&.t, &.b{
width: 100%;
height: 14px;
}
&.l, &.r{
width: 14px;
height: 100%;
}
&.t{ cursor: n-resize; top: -5px; }
&.b{ cursor: s-resize; bottom: -5px; }
&.l{ cursor: w-resize; left: -5px; }
&.r{ cursor: e-resize; right: -5px; }
}
.body{
border-radius: 15px;
overflow: hidden;
height: 100%;
background: #f4f4f4;
.topbar{
width: 100%;
height: 60px;
background: #8c8c8c;
display: flex;
align-items: center;
padding: 0 30px;
.btns{
display: flex;
gap: 9px;
div{
height: 14px;
width: 14px;
border-radius: 50%;
&:nth-child(1){ background:#FF5F58; }
&:nth-child(2){ background:#FFBE2F; }
&:nth-child(3){ background:#2AC940; }
}
}
}
}
}
JS
const xwindow = document.querySelector(".window")
const topbar = document.querySelector(".topbar")
topbar.addEventListener("mousedown", mousedown)
function mousedown(e){
window.addEventListener("mousemove", mousemove)
window.addEventListener("mouseup", mouseup)
let prevX = e.clientX
let prevY = e.clientY
function mousemove(e){
let newX = e.clientX - prevX
let newY = e.clientY - prevY
const rect = xwindow.getBoundingClientRect()
xwindow.style.left = rect.left + newX + "px"
xwindow.style.top = rect.top + newY + "px"
prevX = e.clientX
prevY = e.clientY
}
function mouseup(){
window.removeEventListener("mousemove", mousemove)
window.removeEventListener("mouseup", mouseup)
}
}
const resizers = document.querySelectorAll(".resizer")
for (let resizer of resizers){
resizer.addEventListener("mousedown", mousedown)
function mousedown(e){
let currentResizer = e.target
let prevX = e.clientX
let prevY = e.clientY
window.addEventListener("mousemove", mousemove)
window.addEventListener("mouseup", mouseup)
function mousemove(e){
const rect = xwindow.getBoundingClientRect()
if(currentResizer.classList.contains("br")){
xwindow.style.width = rect.width + (e.clientX - prevX) + "px"
xwindow.style.height = rect.height + (e.clientY - prevY) + "px"
}
else if(currentResizer.classList.contains("bl")){
xwindow.style.width = rect.width + (prevX - e.clientX) + "px"
xwindow.style.height = rect.height + (e.clientY - prevY) + "px"
xwindow.style.left = rect.left + (e.clientX - prevX) + "px"
}
else if(currentResizer.classList.contains("tr")){
xwindow.style.width = rect.width + (e.clientX - prevX) + "px"
xwindow.style.height = rect.height + (prevY - e.clientY) + "px"
xwindow.style.top = rect.top + (e.clientY - prevY) + "px"
}
else if(currentResizer.classList.contains("tl")){
xwindow.style.width = rect.width + (prevX - e.clientX) + "px"
xwindow.style.height = rect.height + (prevY - e.clientY) + "px"
xwindow.style.top = rect.top + (e.clientY - prevY) + "px"
xwindow.style.left = rect.left + (e.clientX - prevX) + "px"
}
else if(currentResizer.classList.contains("t")){
xwindow.style.height = rect.height + (prevY - e.clientY) + "px"
xwindow.style.top = rect.top + (e.clientY - prevY) + "px"
}
else if(currentResizer.classList.contains("b")){
xwindow.style.height = rect.height + (e.clientY - prevY) + "px"
}
else if(currentResizer.classList.contains("l")){
xwindow.style.width = rect.width + (prevX - e.clientX) + "px"
xwindow.style.left = rect.left + (e.clientX - prevX) + "px"
}
else if(currentResizer.classList.contains("r")){
xwindow.style.width = rect.width + (e.clientX - prevX) + "px"
}
prevX = e.clientX
prevY = e.clientY
}
function mouseup(){
window.removeEventListener("mousemove", mousemove)
window.removeEventListener("mouseup", mouseup)
}
}
}