三十的博客

Flexbox 高阶挑战:提升你的布局技能 02

本文内容基于 AI 生成结果整理,可能包含不准确信息,仅供参考使用。
发布时间
阅读量 加载中...

项目一 卡片悬停动画效果

目标效果: 当鼠标悬停在卡片上时,卡片会有放大、阴影加深和轻微旋转的 3D 效果。

效果预览:

此演示仅在桌面端浏览器中可用

代码实现:

html
<!DOCTYPE html>
<html>
  <head>
    <style>
      .card-container {
        display: flex;
        flex-wrap: wrap;
        gap: 25px;
        padding: 30px;
        justify-content: center;
      }

      .card {
        flex: 0 1 280px;
        display: flex;
        flex-direction: column;
        border-radius: 10px;
        overflow: hidden;
        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        transition: all 0.3s ease;
        transform: perspective(1000px) rotateY(0deg);
        background: white;
      }

      .card:hover {
        transform: perspective(1000px) rotateY(5deg) scale(1.03);
        box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
      }

      .card-img {
        height: 180px;
        overflow: hidden;
      }

      .card-img img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        transition: transform 0.5s ease;
      }

      .card:hover .card-img img {
        transform: scale(1.1);
      }

      .card-content {
        padding: 20px;
        flex: 1;
      }

      .card-title {
        font-size: 1.3rem;
        margin-bottom: 10px;
        color: #333;
      }

      .card-desc {
        color: #666;
        margin-bottom: 15px;
        line-height: 1.5;
      }

      .card-footer {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 15px 20px;
        background: linear-gradient(to right, #f8f9fa, #e9ecef);
      }

      .price {
        font-weight: bold;
        color: #2c3e50;
      }

      .btn {
        padding: 8px 16px;
        background: linear-gradient(to right, #3498db, #2980b9);
        color: white;
        border: none;
        border-radius: 20px;
        cursor: pointer;
        transition: all 0.3s ease;
      }

      .btn:hover {
        background: linear-gradient(to right, #2980b9, #3498db);
        transform: translateY(-2px);
      }
    </style>
  </head>
  <body>
    <div class="card-container">
      <div class="card">
        <div class="card-img">
          <img src="https://picsum.photos/400/300?random=1" alt="产品图片" />
        </div>
        <div class="card-content">
          <h3 class="card-title">高级相机</h3>
          <p class="card-desc">
            专业级全画幅相机,适合摄影爱好者和专业人士使用。
          </p>
        </div>
        <div class="card-footer">
          <span class="price">$1,299</span>
          <button class="btn">立即购买</button>
        </div>
      </div>
      <!-- 可以添加更多卡片 -->
    </div>
  </body>
</html>

关键技巧:

项目二 复杂导航栏(带搜索和下拉菜单)

目标效果: 响应式导航栏,包含 logo、主导航、搜索框和下拉菜单,小屏幕时折叠为汉堡菜单。

效果预览:

此演示仅在桌面端浏览器中可用

代码实现:

html
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>改进导航栏</title>
    <style>
      :root {
        --primary-color: #2c3e50;
        --secondary-color: #3498db;
        --text-color: #ecf0f1;
        --hover-color: #2980b9;
        --dropdown-bg: #ffffff;
        --mobile-bg: #1a252f;
      }

      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      body {
        font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
        padding-top: 70px; /* 为固定导航栏留出空间 */
      }

      /* 导航栏基础样式 */
      .navbar {
        display: flex;
        justify-content: space-between;
        align-items: center;
        background-color: var(--primary-color);
        padding: 0 30px;
        height: 70px;
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        z-index: 1000;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
      }

      .logo {
        color: var(--text-color);
        font-size: 1.5rem;
        font-weight: bold;
        text-decoration: none;
        display: flex;
        align-items: center;
        height: 100%;
        padding: 0 15px;
      }

      /* 主导航链接 */
      .nav-links {
        display: flex;
        list-style: none;
      }

      .nav-links li {
        position: relative;
      }

      .nav-links > li > a {
        color: var(--text-color);
        text-decoration: none;
        padding: 0 15px;
        height: 70px;
        display: flex;
        align-items: center;
        transition: all 0.3s ease;
      }

      .nav-links > li > a:hover {
        background-color: rgba(255, 255, 255, 0.1);
        color: var(--text-color);
      }

      /* 下拉菜单 */
      .dropdown {
        position: relative;
      }

      .dropdown-content {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        background-color: var(--dropdown-bg);
        min-width: 200px;
        box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
        border-radius: 0 0 5px 5px;
        overflow: hidden;
        z-index: 1001;
        opacity: 0;
        transform: translateY(-10px);
        transition: all 0.3s ease;
      }

      .dropdown:hover .dropdown-content {
        display: block;
        opacity: 1;
        transform: translateY(0);
      }

      .dropdown-content a {
        color: var(--primary-color);
        padding: 12px 20px;
        display: block;
        text-decoration: none;
        transition: all 0.2s ease;
      }

      .dropdown-content a:hover {
        background-color: #f5f5f5;
        padding-left: 25px;
      }

      /* 搜索框 */
      .search-container {
        display: flex;
        align-items: center;
        margin-left: 20px;
      }

      .search-input {
        padding: 10px 15px;
        border: none;
        border-radius: 20px 0 0 20px;
        outline: none;
        width: 200px;
        transition: width 0.3s ease;
      }

      .search-input:focus {
        width: 250px;
      }

      .search-btn {
        padding: 10px 15px;
        background-color: var(--secondary-color);
        color: white;
        border: none;
        border-radius: 0 20px 20px 0;
        cursor: pointer;
        transition: background-color 0.3s ease;
      }

      .search-btn:hover {
        background-color: var(--hover-color);
      }

      /* 汉堡菜单 */
      .hamburger {
        display: none;
        cursor: pointer;
        padding: 15px;
      }

      .hamburger div {
        width: 25px;
        height: 3px;
        background-color: var(--text-color);
        margin: 5px 0;
        transition: all 0.3s ease;
      }

      /* 移动导航菜单 */
      .mobile-nav {
        display: none;
        width: 100%;
        background-color: var(--mobile-bg);
        position: absolute;
        top: 70px;
        left: 0;
        z-index: 999;
        box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
        max-height: 0;
        overflow: hidden;
        transition: max-height 0.5s ease;
      }

      .mobile-nav.active {
        display: block;
        max-height: 500px;
      }

      .mobile-nav li {
        border-bottom: 1px solid rgba(255, 255, 255, 0.1);
      }

      .mobile-nav a {
        color: var(--text-color);
        text-decoration: none;
        padding: 15px 30px;
        display: block;
        transition: all 0.3s ease;
      }

      .mobile-nav a:hover {
        background-color: rgba(255, 255, 255, 0.1);
        padding-left: 35px;
      }

      .mobile-dropdown-content {
        max-height: 0;
        overflow: hidden;
        transition: max-height 0.5s ease;
        background-color: rgba(0, 0, 0, 0.1);
      }

      .mobile-dropdown-content.active {
        max-height: 300px;
      }

      .mobile-dropdown-content a {
        padding-left: 45px;
      }

      /* 响应式设计 */
      @media (max-width: 992px) {
        .nav-links,
        .search-container {
          display: none;
        }

        .hamburger {
          display: block;
        }

        /* 汉堡菜单动画 */
        .hamburger.active div:nth-child(1) {
          transform: translateY(8px) rotate(45deg);
        }

        .hamburger.active div:nth-child(2) {
          opacity: 0;
        }

        .hamburger.active div:nth-child(3) {
          transform: translateY(-8px) rotate(-45deg);
        }
      }

      /* 小屏幕搜索框 */
      @media (max-width: 576px) {
        .search-container {
          width: 100%;
          margin: 15px 0;
        }

        .search-input {
          width: 100%;
        }

        .search-input:focus {
          width: 100%;
        }
      }
    </style>
  </head>
  <body>
    <nav class="navbar">
      <a href="#" class="logo">FlexSite</a>

      <ul class="nav-links">
        <li><a href="#">首页</a></li>
        <li class="dropdown">
          <a href="#">产品</a>
          <ul class="dropdown-content">
            <li><a href="#">产品1</a></li>
            <li><a href="#">产品2</a></li>
            <li><a href="#">产品3</a></li>
            <li><a href="#">产品4</a></li>
          </ul>
        </li>
        <li><a href="#">服务</a></li>
        <li><a href="#">关于我们</a></li>
        <li><a href="#">联系</a></li>
      </ul>

      <div class="search-container">
        <input type="text" class="search-input" placeholder="搜索..." />
        <button class="search-btn">搜索</button>
      </div>

      <div class="hamburger">
        <div></div>
        <div></div>
        <div></div>
      </div>
    </nav>

    <ul class="mobile-nav">
      <!-- 动态生成 -->
    </ul>

    <script>
      document.addEventListener("DOMContentLoaded", function () {
        const hamburger = document.querySelector(".hamburger");
        const mobileNav = document.querySelector(".mobile-nav");
        const navLinks = document.querySelectorAll(".nav-links > li");

        // 克隆桌面导航到移动导航
        navLinks.forEach((link) => {
          const clone = link.cloneNode(true);
          const dropdown = clone.querySelector(".dropdown-content");

          if (dropdown) {
            dropdown.classList.add("mobile-dropdown-content");
            const a = clone.querySelector("a");
            a.addEventListener("click", (e) => {
              e.preventDefault();
              dropdown.classList.toggle("active");
            });
          }

          mobileNav.appendChild(clone);
        });

        // 添加搜索框到移动导航
        const searchContainer = document.querySelector(".search-container");
        const searchClone = searchContainer.cloneNode(true);
        const searchItem = document.createElement("li");
        searchItem.appendChild(searchClone);
        mobileNav.appendChild(searchItem);

        // 汉堡菜单点击事件
        hamburger.addEventListener("click", () => {
          hamburger.classList.toggle("active");
          mobileNav.classList.toggle("active");

          // 关闭所有下拉菜单
          if (!mobileNav.classList.contains("active")) {
            document
              .querySelectorAll(".mobile-dropdown-content")
              .forEach((dropdown) => {
                dropdown.classList.remove("active");
              });
          }
        });

        // 点击文档其他区域关闭菜单
        document.addEventListener("click", (e) => {
          if (
            !e.target.closest(".navbar") &&
            !e.target.closest(".mobile-nav")
          ) {
            hamburger.classList.remove("active");
            mobileNav.classList.remove("active");
          }
        });
      });
    </script>

    <!-- 页面内容 -->
    <div style="padding: 20px;">
      <h1>页面内容</h1>
      <p>向下滚动查看固定导航栏效果</p>
      <!-- 添加足够多的内容使页面可滚动 -->
      <div style="height: 2000px;"></div>
    </div>
  </body>
</html>

关键技巧:

项目三 可调整大小的仪表盘面板

目标效果: 包含多个可拖动调整大小的面板的仪表盘,使用纯 CSS 实现。

效果预览:

此演示仅在桌面端浏览器中可用

代码实现:

html
<!DOCTYPE html>
<html>
  <head>
    <style>
      :root {
        --panel-header: #34495e;
        --panel-bg: #ecf0f1;
        --resize-handle: #bdc3c7;
      }

      body {
        font-family: Arial, sans-serif;
        margin: 0;
        padding: 0;
        background-color: #f5f6fa;
      }

      .dashboard {
        display: flex;
        flex-wrap: wrap;
        gap: 15px;
        padding: 15px;
        height: 100vh;
      }

      .panel {
        background-color: var(--panel-bg);
        border-radius: 8px;
        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        display: flex;
        flex-direction: column;
        min-width: 200px;
        min-height: 150px;
      }

      .panel-header {
        background-color: var(--panel-header);
        color: white;
        padding: 10px 15px;
        border-radius: 8px 8px 0 0;
        cursor: move;
        user-select: none;
      }

      .panel-content {
        flex: 1;
        padding: 15px;
        overflow: auto;
      }

      /* 调整大小手柄 */
      .resize-handle {
        width: 100%;
        height: 5px;
        background-color: var(--resize-handle);
        cursor: ns-resize;
        position: relative;
      }

      .resize-handle::after {
        content: "≡";
        position: absolute;
        left: 50%;
        top: -8px;
        transform: translateX(-50%);
        color: var(--panel-header);
        font-size: 16px;
      }

      /* 面板大小和位置 */
      .panel-1 {
        flex: 2 1 400px;
      }

      .panel-2 {
        flex: 1 1 300px;
      }

      .panel-3 {
        flex: 1 1 200px;
      }

      /* 响应式调整 */
      @media (max-width: 768px) {
        .panel {
          flex: 1 1 100%;
        }
      }
    </style>
  </head>
  <body>
    <div class="dashboard">
      <div class="panel panel-1" draggable="true">
        <div class="panel-header">主要数据</div>
        <div class="panel-content">
          <p>这里是主要数据面板内容。可以包含图表、统计数据等。</p>
          <p>尝试拖动标题栏移动面板,或使用底部手柄调整大小。</p>
        </div>
        <div class="resize-handle"></div>
      </div>

      <div class="panel panel-2" draggable="true">
        <div class="panel-header">次要指标</div>
        <div class="panel-content">
          <p>次要指标和快速浏览信息可以放在这里。</p>
        </div>
        <div class="resize-handle"></div>
      </div>

      <div class="panel panel-3" draggable="true">
        <div class="panel-header">通知</div>
        <div class="panel-content">
          <p>最新通知和消息提醒。</p>
        </div>
        <div class="resize-handle"></div>
      </div>
    </div>

    <script>
      document.querySelectorAll(".panel").forEach((panel) => {
        const header = panel.querySelector(".panel-header");
        let isDragging = false;
        let offsetX, offsetY;

        header.addEventListener("mousedown", (e) => {
          isDragging = true;
          offsetX = e.clientX - panel.getBoundingClientRect().left;
          offsetY = e.clientY - panel.getBoundingClientRect().top;
          panel.style.position = "absolute";
          panel.style.zIndex = 1000;
        });

        document.addEventListener("mousemove", (e) => {
          if (!isDragging) return;

          panel.style.left = e.clientX - offsetX + "px";
          panel.style.top = e.clientY - offsetY + "px";
        });

        document.addEventListener("mouseup", () => {
          isDragging = false;
        });
      });

      document.querySelectorAll(".resize-handle").forEach((handle) => {
        const panel = handle.parentElement;
        let isResizing = false;
        let startY, startHeight;

        handle.addEventListener("mousedown", (e) => {
          isResizing = true;
          startY = e.clientY;
          startHeight = parseInt(
            document.defaultView.getComputedStyle(panel).height,
            10
          );
          e.preventDefault(); // 防止文本选中
        });

        document.addEventListener("mousemove", (e) => {
          if (!isResizing) return;

          const newHeight = startHeight + e.clientY - startY + "px";
          panel.style.height = newHeight;
          panel.style.flex = "none"; // 覆盖flex属性
        });

        document.addEventListener("mouseup", () => {
          isResizing = false;
        });
      });
    </script>
  </body>
</html>

项目四 杂志式多栏文本布局

目标效果: 类似杂志的多栏文本布局,自动根据容器宽度调整列数。

效果预览:

此演示仅在桌面端浏览器中可用

代码实现:

html
<!DOCTYPE html>
<html>
  <head>
    <style>
      :root {
        --text-color: #333;
        --column-gap: 30px;
        --line-color: #e0e0e0;
      }

      body {
        font-family: "Georgia", serif;
        line-height: 1.6;
        color: var(--text-color);
        background-color: #f9f9f9;
        padding: 0;
        margin: 0;
      }

      .magazine {
        max-width: 1200px;
        margin: 0 auto;
        padding: 40px 20px;
        columns: 3;
        column-gap: var(--column-gap);
        column-rule: 1px solid var(--line-color);
      }

      .article {
        break-inside: avoid;
        margin-bottom: 30px;
      }

      .article-title {
        font-size: 1.8rem;
        line-height: 1.2;
        margin-bottom: 15px;
        color: #2c3e50;
      }

      .article-meta {
        font-style: italic;
        color: #7f8c8d;
        margin-bottom: 15px;
      }

      .article-content::first-letter {
        font-size: 3.5rem;
        float: left;
        line-height: 0.8;
        margin-right: 8px;
        color: #2c3e50;
      }

      .article-image {
        width: 100%;
        height: auto;
        margin: 15px 0;
        border-radius: 4px;
      }

      .pull-quote {
        font-size: 1.4rem;
        line-height: 1.4;
        color: #2c3e50;
        padding: 15px;
        margin: 20px 0;
        border-left: 3px solid #3498db;
        background-color: #f0f5f9;
        break-inside: avoid;
      }

      /* 响应式列数调整 */
      @media (max-width: 900px) {
        .magazine {
          columns: 2;
        }
      }

      @media (max-width: 600px) {
        .magazine {
          columns: 1;
        }
      }

      /* 装饰元素 */
      .section-header {
        column-span: all;
        text-align: center;
        margin: 40px 0;
        position: relative;
      }

      .section-header h2 {
        display: inline-block;
        padding: 0 20px;
        background-color: #f9f9f9;
        position: relative;
        z-index: 1;
        color: #3498db;
      }

      .section-header::before {
        content: "";
        position: absolute;
        top: 50%;
        left: 0;
        right: 0;
        height: 1px;
        background-color: var(--line-color);
        z-index: 0;
      }
    </style>
  </head>
  <body>
    <div class="magazine">
      <div class="section-header">
        <h2>今日特刊</h2>
      </div>

      <div class="article">
        <h3 class="article-title">Flexbox 布局的现代艺术</h3>
        <div class="article-meta">作者: CSS大师 · 发布于: 2023年6月15日</div>
        <div class="article-content">
          <p>
            在网页设计的世界里,Flexbox
            已经成为现代布局的基石。这种一维布局模型让我们能够轻松创建复杂的响应式设计,而不需要依赖传统的浮动或定位技巧。
          </p>

          <img
            src="https://picsum.photos/600/400?random=1"
            alt="布局示例"
            class="article-image"
          />

          <p>
            Flexbox 的核心概念是弹性容器和弹性项目。通过设置容器的 display
            属性为 flex,我们创建了一个弹性容器,其直接子元素自动成为弹性项目。
          </p>

          <div class="pull-quote">
            "Flexbox
            彻底改变了我们思考网页布局的方式,它让复杂的布局变得简单而直观。"
          </div>

          <p>
            弹性容器可以控制项目的排列方向、对齐方式、换行行为等。而弹性项目则可以通过各种属性控制自身的排列顺序、伸缩比例等。
          </p>
        </div>
      </div>

      <div class="article">
        <h3 class="article-title">响应式设计的未来趋势</h3>
        <div class="article-meta">作者: 设计先知 · 发布于: 2023年6月10日</div>
        <div class="article-content">
          <p>
            随着移动设备的普及,响应式设计已经从可选变成了必需。未来的网页设计将更加注重设备的适应性和用户体验的一致性。
          </p>

          <p>
            媒体查询、相对单位和弹性布局的结合,让我们能够创建出在各种屏幕尺寸下都能完美展现的设计。
          </p>

          <img
            src="https://picsum.photos/600/400?random=2"
            alt="响应式设计"
            class="article-image"
          />

          <p>
            未来的趋势可能包括:基于容器而非视口的查询、更加智能的布局系统、以及更精细的用户偏好设置响应。
          </p>
        </div>
      </div>

      <!-- 可以添加更多文章 -->
    </div>
  </body>
</html>

关键技巧:

#Flex #Layout #CSS基础