使用jQuery的平铺式窗口

使用 HTML、CSS 和 jQuery UI 构建一个简单的类似 i3wm 的窗口样式

支持三种布局:浮动、平铺和堆叠

首先定义一组按钮和容器,通过点击按钮切换布局

<div>
  <button onclick="setLayout('tile')">Tile</button>
  <button onclick="setLayout('stack')">Stack</button>
  <button onclick="setLayout('floating')">Floating</button>
</div>

<div id="layout">
  <div class="window" id="w-content">Tile Window 2</div>
  <div class="window" id="w-tag">Tile Window 2</div>
  <div class="window" id="w-archive">Tile Window 3</div>
</div>

然后设置整体样式

body {
  overflow: hidden;
  margin: 0;
  padding: 0;
}

.window {
  border: 1px solid #333;
  background-color: #f9f9f9;
  min-height: 100px;
  cursor: move;
  z-index: 1;
}

设置 tile 模式

tile

CSS 样式

.layout-tile {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 10px;
  height: 100vh;
  width: 100vw;
  position: relative;
}

.t-content {
  grid-column: 1 / 2;
  grid-row: 1 / 3;
}

.t-tag {
  grid-column: 2 / 3;
  grid-row: 1 / 2;
}

.t-archive {
  grid-column: 2 / 3;
  grid-row: 2 / 3;
}

js 代码

function swapWindows(windowA, windowB) {
  // Swap the inner HTML of the two windows
  const contentA = windowA.html();
  windowA.html(windowB.html());
  windowB.html(contentA);
}

$("#layout").addClass("layout-tile");

$("#w-content").addClass("t-content");
$("#w-tag").addClass("t-tag");
$("#w-archive").addClass("t-archive");

$(".layout-tile .window").on("mousedown", function () {
  currentWindow = $(this);
  $(".layout-tile").css("user-select", "none");
});

$(".layout-tile .window").on("mouseup", function (event) {
  const mouseX = event.pageX;
  const mouseY = event.pageY;
  let targetWindow = null;

  // Check if the mouse position is within the bounds of any window
  $(".layout-tile .window").each(function () {
    const targetPosition = $(this).offset();
    const targetWidth = $(this).outerWidth();
    const targetHeight = $(this).outerHeight();

    if (
      mouseX >= targetPosition.left &&
      mouseX <= targetPosition.left + targetWidth &&
      mouseY >= targetPosition.top &&
      mouseY <= targetPosition.top + targetHeight
    ) {
      targetWindow = $(this);
      return false;
    }
  });
  if (targetWindow && currentWindow && targetWindow[0] !== currentWindow[0]) {
    swapWindows(currentWindow, targetWindow);
  }
  currentWindow = null;
  $(".layout-tile .window").removeAttr("user-select");
});

代码解释

$(".layout-tile .window").on("mousedown", function () {
  currentWindow = $(this);
  $(".layout-tile").css("user-select", "none");
});
  • .layout-tile 类下的每个 .window 元素绑定 mousedown 事件。当用户按下鼠标按钮时,当前被按下的窗口被存储在 currentWindow 变量中。
  • 通过设置 user-select: none,禁用文本选择,防止在拖动时选择文本

$(".layout-tile .window").on("mouseup", function (event) {
  const mouseX = event.pageX;
  const mouseY = event.pageY;
  let targetWindow = null;
  • 为每个 .window 绑定 mouseup 事件。当用户释放鼠标按钮时,记录鼠标的 X 和 Y 坐标,并初始化 targetWindow 变量为 null
$(".layout-tile .window").each(function () {
  const targetPosition = $(this).offset();
  const targetWidth = $(this).outerWidth();
  const targetHeight = $(this).outerHeight();

  if (
    mouseX >= targetPosition.left &&
    mouseX <= targetPosition.left + targetWidth &&
    mouseY >= targetPosition.top &&
    mouseY <= targetPosition.top + targetHeight
  ) {
    targetWindow = $(this);
    return false;
  }
});
  • 遍历 .layout-tile 下的每个 .window 元素,检查鼠标释放时的位置是否在任何窗口的边界内。
  • targetPosition 获取当前窗口的位置,targetWidthtargetHeight 获取其宽度和高度。
  • 如果鼠标位置在某个窗口内,将该窗口存储在 targetWindow 中,并通过 return false 提前结束循环。
if (targetWindow && currentWindow && targetWindow[0] !== currentWindow[0]) {
  swapWindows(currentWindow, targetWindow);
}
  • 如果 targetWindowcurrentWindow 都存在,并且它们不是同一个窗口(即用户释放鼠标时并没有在同一个窗口上),则调用 swapWindows 函数,交换这两个窗口的内容。
  currentWindow = null;
  $(".layout-tile .window").removeAttr("user-select");
});
  • currentWindow 设置为 null,准备进行下一次操作。
  • 移除 user-select 属性,恢复文本选择的能力。

设置 stack 模式

stack

CSS 样式

.layout-stack {
  position: relative;
  height: 100%;
  width: 100%;
}
.s-content {
  top: 40px;
  left: 40px;
  height: 100vh;
  width: 100%;
  z-index: 3;
  position: absolute;
}

.s-tag {
  top: 20px;
  left: 20px;
  height: 100vh;
  width: 100%;
  z-index: 2;
  position: absolute;
}

.s-archive {
  top: 0px;
  left: 0px;
  height: 100vh;
  width: 100%;
  z-index: 1;
  position: absolute;
}

js 代码

$("#layout").addClass("layout-stack");

$("#w-content").addClass("s-content");
$("#w-tag").addClass("s-tag");
$("#w-archive").addClass("s-archive");

$(".layout-stack .window")
  .off("click")
  .on("click", function () {
    const currentZIndex = parseInt($(this).css("z-index"), 10);
    const zIndex3Window = $(".window").filter(function () {
      return parseInt($(this).css("z-index"), 10) === 3;
    });
    const clickedWindow = $(this);
    swapWindows(zIndex3Window, clickedWindow);
  });

代码解释

$("#layout").addClass("layout-stack");
  • 作用:给 #layout 元素添加 layout-stack 类,设置布局为 stack。
$("#w-content").addClass("s-content");
$("#w-tag").addClass("s-tag");
$("#w-archive").addClass("s-archive");
  • 作用:分别为 #w-content#w-tag#w-archive 元素添加对应的样式类,以便在堆叠布局下定义它们的样式和位置。
$(".layout-stack .window")
  .off("click")
  .on("click", function () {
  • 作用:为 .layout-stack 下的所有 .window 元素绑定点击事件。
  • .off("click") 是用来移除之前绑定的点击事件,确保不会重复绑定。
const currentZIndex = parseInt($(this).css("z-index"), 10);
  • 作用:获取当前被点击窗口的 z-index 值,存储在 currentZIndex 变量中。
const zIndex3Window = $(".window").filter(function () {
  return parseInt($(this).css("z-index"), 10) === 3;
});
  • 作用:查找所有窗口中 z-index 值为 3 的窗口,并将其存储在 zIndex3Window 变量中。因为 CSS 中设置 z-index 为 3 的窗口是最上面的窗口。
const clickedWindow = $(this);
  • 作用:将当前被点击的窗口元素存储在 clickedWindow 变量中,方便后续操作。
swapWindows(zIndex3Window, clickedWindow);
  • 作用:调用 swapWindows 函数,交换 zIndex3WindowclickedWindow 的内容。点击一个窗口时,它会与当前在最上层的窗口交换位置。

设置 floating 模式

floating

CSS 样式

.layout-floating {
  position: absolute;
  height: 100%;
  width: 100%;
}

.f-content {
  width: 100%;
}

.f-tag {
  width: 100%;
}

.f-archive {
  width: 100%;
}

js 代码

$("#layout").addClass("layout-floating");

$("#w-content").addClass("f-content");
$("#w-tag").addClass("f-tag");
$("#w-archive").addClass("f-archive");

$("#w-content").addClass("ui-draggable ui-draggable-handle ui-resizable");
$("#w-tag").addClass("ui-draggable ui-draggable-handle ui-resizable");
$("#w-archive").addClass("ui-draggable ui-draggable-handle ui-resizable");

$(".layout-floating .window")
  .draggable({
    containment: "window",
    start: function () {
      $(".window").css("z-index", 1);
      $(this).css("z-index", 2);
    },
  })
  .resizable();

$(".layout-floating .window").on("click", function () {
  $(".window").css("z-index", 1);
  $(this).css("z-index", 2);
});

代码解释

$("#layout").addClass("layout-floating");
  • 作用:给 #layout 元素添加 layout-floating 类,设置布局为浮动模式。
$("#w-content").addClass("f-content");
$("#w-tag").addClass("f-tag");
$("#w-archive").addClass("f-archive");
  • 作用:分别为 #w-content#w-tag#w-archive 元素添加各自的浮动类。这些类会定义它们在浮动布局下的样式。
$("#w-content").addClass("ui-draggable ui-draggable-handle ui-resizable");
$("#w-tag").addClass("ui-draggable ui-draggable-handle ui-resizable");
$("#w-archive").addClass("ui-draggable ui-draggable-handle ui-resizable");
  • 作用:为每个窗口元素添加 jQuery UI 的拖动和调整大小的类。这样,窗口就可以通过鼠标拖动或调整大小。
$(".layout-floating .window").draggable({
  containment: "window",
  start: function () {
    $(".window").css("z-index", 1);
    $(this).css("z-index", 2);
  },
});
  • 作用
    • 使 .layout-floating 类下的所有 .window 元素可拖动。
    • containment: "window":限制窗口只能在浏览器窗口内拖动。
    • start:当拖动开始时便将所有窗口的 z-index 设置为 1,将当前拖动的窗口的 z-index 设置为 2,以确保它在其他窗口上方。
$(".layout-floating .window").on("click", function () {
  $(".window").css("z-index", 1);
  $(this).css("z-index", 2);
});
  • 作用:为每个窗口添加点击事件:
    • 当窗口被点击时,将所有窗口的 z-index 设置为 1,确保它们在同一层级。
    • 将当前点击的窗口的 z-index 设置为 2,使其处于最上层。

comment: