You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular实现屏幕缩小时将换行工具项移入弹窗的方法

实现响应式工具栏:超出项移入弹窗

我有如下Angular项目中的工具数据数组:

public tools: Tool[] = [
    { image: './assets/img/OpenFolder.svg', title: 'Upload' },
    { image: './assets/img/Save.png', title: 'Save' },
    { image: './assets/img/Printer.png', title: 'Printing' },
    { image: './assets/img/Delete.png', title: 'Delete' },
    { image: './assets/img/Paste.svg', title: 'Paste' },
    { image: './assets/img/Copy.png', title: 'Copy' },
    { image: './assets/img/favicon.png', title: 'Rotate left' },
    { image: './assets/img/favicon.png', title: 'Rotate right' },
    { image: './assets/img/favicon.png', title: 'Flip vertical' },
    { image: './assets/img/favicon.png', title: 'Flip horizontal' },
    { image: './assets/img/favicon.png', title: 'Crop picture' },
    { image: './assets/img/favicon.png', title: 'Resize picture' },
    { image: './assets/img/favicon.png', title: 'Brightness' },
    { image: './assets/img/favicon.png', title: 'Color' },
    { image: './assets/img/favicon.png', title: 'Opacity' },
];

当前的HTML和SCSS代码如下:

<ul>
    <li *ngFor="let tool of tools" title="{{ tool.title }}"><img src="{{ tool.image }}"></li>
</ul>
ul {
    display: flex;
    flex-wrap: wrap;
    flex-direction: row;
}

当前效果是屏幕宽度小于max-width时,工具项会水平换行。我需要实现:当屏幕缩小到小于max-width时,把换行的项移入弹窗,只有点击按钮时弹窗才会显示。


解决方案

步骤1:组件逻辑(TypeScript)

监听窗口宽度变化,拆分工具数组为主栏显示项和弹窗隐藏项,同时控制弹窗的显示状态:

import { Component, OnInit, HostListener } from '@angular/core';

interface Tool {
  image: string;
  title: string;
}

@Component({
  selector: 'app-toolbar',
  templateUrl: './toolbar.component.html',
  styleUrls: ['./toolbar.component.scss']
})
export class ToolbarComponent implements OnInit {
  public tools: Tool[] = [
    { image: './assets/img/OpenFolder.svg', title: 'Upload' },
    { image: './assets/img/Save.png', title: 'Save' },
    { image: './assets/img/Printer.png', title: 'Printing' },
    { image: './assets/img/Delete.png', title: 'Delete' },
    { image: './assets/img/Paste.svg', title: 'Paste' },
    { image: './assets/img/Copy.png', title: 'Copy' },
    { image: './assets/img/favicon.png', title: 'Rotate left' },
    { image: './assets/img/favicon.png', title: 'Rotate right' },
    { image: './assets/img/favicon.png', title: 'Flip vertical' },
    { image: './assets/img/favicon.png', title: 'Flip horizontal' },
    { image: './assets/img/favicon.png', title: 'Crop picture' },
    { image: './assets/img/favicon.png', title: 'Resize picture' },
    { image: './assets/img/favicon.png', title: 'Brightness' },
    { image: './assets/img/favicon.png', title: 'Color' },
    { image: './assets/img/favicon.png', title: 'Opacity' },
  ];

  public visibleTools: Tool[] = [];
  public hiddenTools: Tool[] = [];
  public showPopup = false;
  private maxWidth = 768; // 自定义响应式阈值
  private toolItemWidth = 48; // 单个工具项的总宽度(含间距)

  ngOnInit(): void {
    this.updateToolGroups();
  }

  @HostListener('window:resize')
  onWindowResize(): void {
    this.updateToolGroups();
  }

  private updateToolGroups(): void {
    const windowWidth = window.innerWidth;
    if (windowWidth >= this.maxWidth) {
      // 屏幕足够宽,显示所有工具
      this.visibleTools = [...this.tools];
      this.hiddenTools = [];
      this.showPopup = false;
    } else {
      // 计算主栏可容纳的工具数量
      const containerAvailableWidth = windowWidth - 32; // 减去容器左右内边距
      const visibleCount = Math.floor(containerAvailableWidth / this.toolItemWidth);
      this.visibleTools = this.tools.slice(0, visibleCount);
      this.hiddenTools = this.tools.slice(visibleCount);
    }
  }

  togglePopup(): void {
    this.showPopup = !this.showPopup;
  }
}

步骤2:模板(HTML)

添加主工具栏、弹窗触发按钮和弹窗容器:

<div class="toolbar-wrapper">
  <!-- 主工具栏 -->
  <ul class="main-tools">
    <li *ngFor="let tool of visibleTools" title="{{ tool.title }}">
      <img src="{{ tool.image }}" alt="{{ tool.title }}">
    </li>
    <!-- 仅当有隐藏工具时显示弹窗按钮 -->
    <li *ngIf="hiddenTools.length > 0" class="more-btn" (click)="togglePopup()">
      <img src="./assets/img/more-icon.svg" alt="更多工具">
    </li>
  </ul>

  <!-- 工具弹窗 -->
  <div class="tools-popup" *ngIf="showPopup && hiddenTools.length > 0">
    <ul class="popup-tools">
      <li *ngFor="let tool of hiddenTools" title="{{ tool.title }}">
        <img src="{{ tool.image }}" alt="{{ tool.title }}">
      </li>
    </ul>
  </div>
</div>

步骤3:样式(SCSS)

完善布局样式,控制主栏不换行、弹窗定位和响应式显示:

.toolbar-wrapper {
  position: relative;
  padding: 8px;
}

.main-tools {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap; // 禁止主栏换行
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;

  li {
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border-radius: 4px;
    transition: background-color 0.2s;

    &:hover {
      background-color: #f5f5f5;
    }

    img {
      width: 24px;
      height: 24px;
    }
  }

  .more-btn {
    background-color: #f0f0f0;
  }
}

.tools-popup {
  position: absolute;
  top: calc(100% + 4px);
  right: 8px;
  background-color: #fff;
  border: 1px solid #e0e0e0;
  border-radius: 6px;
  box-shadow: 0 2px 12px rgba(0,0,0,0.1);
  z-index: 100;

  .popup-tools {
    display: flex;
    flex-direction: column;
    gap: 4px;
    list-style: none;
    margin: 0;
    padding: 8px;

    li {
      width: 40px;
      height: 40px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      border-radius: 4px;
      transition: background-color 0.2s;

      &:hover {
        background-color: #f5f5f5;
      }

      img {
        width: 24px;
        height: 24px;
      }
    }
  }
}

// 屏幕宽度达标时隐藏弹窗按钮
@media (min-width: 768px) {
  .more-btn {
    display: none !important;
  }
}

关键说明

  1. 参数调整:根据实际设计修改maxWidth(响应式阈值)和toolItemWidth(单个工具项总宽度),确保主栏容纳数量计算准确。
  2. 实时更新:通过resize监听事件,窗口大小变化时自动重新拆分工具组。
  3. 交互逻辑:无隐藏工具时自动关闭弹窗,避免无效显示;点击按钮切换弹窗状态。

内容的提问来源于stack exchange,提问作者Lê Anh Đức

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 22:25:18