响应式屏幕_检测角度的响应式屏幕尺寸

响应式屏幕

Most of the time, we use CSS media queries to handle responsive, screen size changes to layout our content differently. However, there are times where CSS media queries alone isn't sufficient for that. We need to handle the responsiveness in our code.

大多数情况下,我们使用CSS媒体查询来处理响应式屏幕尺寸更改,以不同方式布局内容。 但是,有时仅靠CSS媒体查询还不足以做到这一点。 我们需要处理代码中的响应能力。

In this article, I would like to share about how to detect responsive breakpoints in Angular, with a twist - we don't maintaining responsive breakpoint sizes in your Typescript code (because responsive breakpoints are already defined in CSS).

在本文中,我想分享一下如何在Angular中检测响应断点- 我们不会在您的Typescript代码中保持响应断点的大小 (因为响应断点已在CSS中定义)。

We will use Angular with Bootstrap in this example, but it works for any CSS frameworks and classes. Let's start.

在此示例中,我们将Angular与Bootstrap结合使用,但它可用于任何CSS框架和类。 开始吧。

有什么计划 ( What's the Plan )

We will be using CSS Classes to determine the current responsive breakpoints. There are 5 breakpoints in Bootstrap CSS. The CSS classes to determine the visibility of each breakpoints is:

我们将使用CSS类来确定当前的响应断点。 Bootstrap CSS中有5个断点。 确定每个断点可见性CSS类是:

  • Visible only on xs: .d-block .d-sm-none

    仅在xs上可见: .d-block .d-sm-none
  • Visible only on sm: .d-none .d-sm-block .d-md-none

    仅在sm上可见: .d-none .d-sm-block .d-md-none
  • Visible only on md: .d-none .d-md-block .d-lg-none

    仅在md上可见: .d-none .d-md-block .d-lg-none
  • Visible only on lg: .d-none .d-lg-block .d-xl-none

    仅在lg上可见: .d-none .d-lg-block .d-xl-none
  • Visible only on xl: .d-none .d-xl-block

    仅在xl上可见: .d-none .d-xl-block

The CSS display property will be toggled between none or block. We will apply these classes to HTML elements.

CSS display属性将在noneblock之间切换。 我们将这些类应用于HTML元素。

Everytime when screen size changes, we will loop and find the HTML element with style display: block, this is how we will detect the current breakpoint.

每当屏幕大小改变时,我们都会循环查找具有display: block样式HTML元素,这就是我们将如何检测当前断点的方式。

Here is the code if you are too excited to see the solution: https://stackblitz.com/edit/angular-size.

如果您太兴奋看不到解决方案,请使用以下代码: https : //stackblitz.com/edit/angular-size

实现:组件 ( The Implementation: Component )

Let's create an Angular component size-detector.

让我们创建一个Angular组件size-detector

The component HTML template:

组件HTML模板:

<!-- size-detector.component.html -->
<div *ngFor="let s of sizes" class="{{s.css + ' ' + (prefix + s.id) }}">{{s.name}}</div>

The component Typescript code:

组件打字稿代码:

// size-detector.component.ts
...
export class SizeDetectorComponent implements AfterViewInit {
  prefix = 'is-';
  sizes = [
    {
      id: SCREEN_SIZE.XS, name: 'xs', css: `d-block d-sm-none`
    },
    {
      id: SCREEN_SIZE.SM, name: 'sm', css: `d-none d-sm-block d-md-none`
    },
    {
      id: SCREEN_SIZE.MD, name: 'md', css: `d-none d-md-block d-lg-none`
    },
    {
      id: SCREEN_SIZE.LG, name: 'lg', css: `d-none d-lg-block d-xl-none`
    },
    {
      id: SCREEN_SIZE.XL, name: 'xl', css: `d-none d-xl-block`
    },
  ];

  @HostListener("window:resize", [])
  private onResize() {
    this.detectScreenSize();
  }

  ngAfterViewInit() {
    this.detectScreenSize();
  }

  private detectScreenSize() {
    // we will write this logic later
  }
}

After looking at the component code, you might be wondering where is those SCREEN_SIZE.* value come from. It is an enum. Let's create the screen size enum (You may create a new file or just place the enum in same component file)

查看了组件代码之后,您可能想知道那些SCREEN_SIZE.*值从何而来。 这是一个枚举。 让我们创建screen size enum (您可以创建一个新文件,也可以将枚举放置在相同的组件文件中)

// screen-size.enum.ts

/_ An enum that define all screen sizes the application support _/
export enum SCREEN_SIZE {
  XS,
  SM,
  MD,
  LG,
  XL
}

Also, remember to add Bootstrap to your project! You may add it via npm or yarn, but in this example, we will use the easier way. Add the cdn link in index.html.

另外,请记住将Bootstrap添加到您的项目中! 您可以通过npm或yarn来添加它,但是在本示例中,我们将使用更简单的方法。 将CDN链接添加到index.html

<!-- index.html -->
<link rel="stylesheet" 
    href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">

The code is pretty expressive itself.

该代码本身具有很好的表达能力。

  1. First, we define a list of sizes that we support and the CSS classes that used to determine each breakpoints.

    首先,我们定义了我们支持的sizes列表以及用于确定每个断点CSS类。
  2. In the HTML, we loop through the size list, create div element, assign css and display it. Also note that we give each div an additional unique css class is-<SIZE_ENUM>.

    在HTML中,我们遍历大小列表,创建div元素,分配css并显示它。 还要注意,我们给每个div一个额外的唯一CSS类is-<SIZE_ENUM>
  3. We have a function detectScreenSize. This is where we will write our logic to detect the screen size changes. We will complete that later.

    我们有一个功能detectScreenSize 。 这是我们编写逻辑来检测屏幕尺寸变化的地方。 我们将在稍后完成。
  4. We need to run the logic to everytime when screen size changes. We use the HostListener decorator to listen to the window resize event.

    屏幕大小更改时,我们需要每次都运行逻辑。 我们使用HostListener装饰器来监听window resize事件。
  5. We also need to run the logic when we first initialize the application. We need to run it during the AfterViewInit component lifecycle hook.

    首次初始化应用程序时,我们还需要运行逻辑。 我们需要在AfterViewInit组件生命周期挂钩期间运行它。

实施:服务和组件 ( The Implementation: Service & Component )

Now we have the component code "almost" ready, let's start implementing our resize service.

现在我们已经准备好组件代码“差不多”了,让我们开始实现resize service

// resize.service.ts

@Injectable()
export class ResizeService {

  get onResize$(): Observable<SCREEN_SIZE> {
    return this.resizeSubject.asObservable().pipe(distinctUntilChanged());
  }

  private resizeSubject: Subject<SCREEN_SIZE>;

  constructor() {
    this.resizeSubject = new Subject();
  }

  onResize(size: SCREEN_SIZE) {
    this.resizeSubject.next(size);
  }

}

The resize service code is simple:

调整服务代码很简单:

  1. We create a rxjs subject resizeSubject.

    我们创建一个rxjs主题resizeSubject
  2. We have a public method onResize that receive size as the parameter. It will then push the value to the resize stream. (We will call this method later in our size-detector component)

    我们有一个公共方法onResize ,它接收size作为参数。 然后它将值推送到调整大小流。 (稍后我们将在size-detector组件中调用此方法)
  3. Notice that we use distinctUntilChanged operator in the resize observable. We use that to reduce unnecessary notification. For example, when your screen size change from 200px to 300px, it is still consider as xs size in bootstrap. We don't need to notify in that case. (You can remove the operator if you need)

    注意,在可调整大小的可观察值中我们使用了distinctUntilChanged操作符。 我们用它来减少不必要的通知。 例如,当屏幕大小从200px更改为300px时,在引导程序中它仍被视为xs大小。 在这种情况下,我们不需要通知。 (如果需要,可以删除运算符)
  4. We export the the resize stream as observable via onResize$. Any components, services, directives, etc can then subscribe to this stream to get notify whenever size is changed.

    我们通过onResize$导出可调整大小的流。 然后,只要更改大小,任何组件,服务,指令等都可以订阅此流以获取通知。

Next, let's go back to our size-detector component and update the detectScreenSize logic.

接下来,让我们返回到size-detector组件,并更新detectScreenSize逻辑。

// size-detector.component.ts
...

private detectScreenSize() {
    constructor(private elementRef: ElementRef, private resizeSvc: ResizeService) { }

    const currentSize = this.sizes.find(x => {
      // get the HTML element
      const el = this.elementRef.nativeElement.querySelector(`.${this.prefix}${x.id}`);

      // check its display property value
      const isVisible = window.getComputedStyle(el).display != 'none';

      return isVisible;
    });

    this.resizeSvc.onResize(currentSize.id);
}

...

Let's breakdown and go through the logic together:

让我们分解并一起研究逻辑:

  1. First, we will need to inject the ElementRef and our newly created ResizeService to our component.

    首先,我们需要将ElementRef和我们新创建的ResizeService到组件中。
  2. Base on our CSS classes, at any point of time, there will be ONLY ONE HTML element visible. We loop through our sizes array and find it.

    根据我们CSS类,在任何时间点,将只显示一个HTML元素。 我们遍历我们的sizes数组并找到它。
  3. For each size of our sizes array, we will use HTML5 element's query selector to find the element by the unique css class we defined earlier on is-<SIZE_ENUM>.

    对于我们的sizes数组的每个大小,我们将使用HTML5元素的查询选择器通过我们先前在is-<SIZE_ENUM>上定义的唯一css类来查找元素。
  4. Once we find the current visible element, we then notify our resize service by calling the onResize method.

    找到当前可见的元素后,我们将通过调用onResize方法通知调整大小服务。

使用服务和组件 ( Using the Service and Component )

You may place the size-detector component under our root component app-component. For example:

您可以将size-detector组件放置在我们的根组件app-component 。 例如:

<!-- app.component.html -->

<hello name="{{ name }}"></hello>
<!-- Your size-detector component place here -->
<app-size-detector></app-size-detector>

In this example, I have another hello-component in the app-component, but that doesn't matter.

在此示例中,我在app-component有另一个hello-component app-component ,但这没关系。

Since I place the component in app-component, means I can use the ResizeService everywhere (directives, components, services, etc).

由于我将组件放置在app-component ,这意味着我可以在ResizeService地方(指令,组件,服务等)使用ResizeService

For instance, let's say I want to detect the screen size changes in hello-component, I can do so by inject the ResizeService in constructor, then subscribe to the onSizeChange$ observable and do what I need.

例如,假设我要检测hello-component的屏幕尺寸变化,可以通过在构造函数中注入ResizeService ,然后订阅可观察的onSizeChange$并执行我需要的操作来实现。

// hello.component.ts

@Component({
  selector: 'hello',
  template: `<h1>Hello {{size}}!</h1>`,
})
export class HelloComponent  {

  size: SCREEN_SIZE;

  constructor(private resizeSvc: ResizeService) { 
    // subscribe to the size change stream
    this.resizeSvc.onResize$.subscribe(x => {
      this.size = x;
    });
  }

}

In the above code, we detect the screen size changes and simply display the current screen size value.

在上面的代码中,我们检测到屏幕尺寸的变化,仅显示当前的屏幕尺寸值。

See it in action!

看到它的行动!

One of the real life use case scenario might be you have accordion on screen. In mobile, you would like to collapse all accordion panels, show only the active one at a time. However, in desktop, you might want to expand all panel.

现实生活中的用例场景之一可能是屏幕上显示了手风琴。 在移动设备中,您希望折叠所有手风琴面板,一次只显示活动面板。 但是,在台式机中,您可能需要扩展所有面板。

摘要 ( Summary )

This is how we can detect the screen size changes without maintaining the actual breakpoint sizes in our JavaScript code. Here is the code: https://stackblitz.com/edit/angular-size.

这样,我们就可以检测屏幕尺寸的变化,而无需在JavaScript代码中保留实际的断点尺寸。 这是代码: https : //stackblitz.com/edit/angular-size

If you think of it, it is not very often that the user changes the screen size when browsing the app. You may handle the screen sizes changes application wide (like our example above) or just handle it everytime you need it (per use case / component basis).

如果考虑到这一点,用户在浏览应用程序时更改屏幕尺寸的情况并不常见。 您可以在整个应用程序范围内处理屏幕尺寸更改(例如上面的示例),也可以在每次需要时进行处理(基于用例/组件)。

Besides that, if you don't mind to duplicate and maintain the breakpoint sizes in JavaScript code, you may remove the component, move the detectScreenSize into your service and change a bit on the logic. It is not difficult to implement that. (Try it probably?)

除此之外,如果您不介意在JavaScript代码中复制和维护断点大小,则可以删除该组件,将detectScreenSize移到您的服务中并在逻辑上进行一些更改。 实现这一点并不困难。 (可能尝试一下?)

That's all. Happy coding!

就这样。 编码愉快!

翻译自: https://scotch.io/tutorials/detect-responsive-screen-sizes-in-angular

响应式屏幕

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值