本文共 10223 字,大约阅读时间需要 34 分钟。
响应式屏幕
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 in this example, but it works for any CSS frameworks and classes. Let's start.
在此示例中,我们将Angular与使用,但它可用于任何CSS框架和类。 开始吧。
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 of each breakpoints is:
我们将使用CSS类来确定当前的响应断点。 Bootstrap CSS中有5个断点。 确定每个断点CSS类是:
.d-block .d-sm-none
仅在xs上可见: .d-block .d-sm-none
.d-none .d-sm-block .d-md-none
仅在sm上可见: .d-none .d-sm-block .d-md-none
.d-none .d-md-block .d-lg-none
仅在md上可见: .d-none .d-md-block .d-lg-none
.d-none .d-lg-block .d-xl-none
仅在lg上可见: .d-none .d-lg-block .d-xl-none
.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
属性将在none
或block
之间切换。 我们将这些类应用于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: .
如果您太兴奋看不到解决方案,请使用以下代码: : 。
Let's create an Angular component size-detector
.
让我们创建一个Angular组件size-detector
。
The component HTML template:
组件HTML模板:
{ {s.name}}
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
。
The code is pretty expressive itself.
该代码本身具有很好的表达能力。
sizes
that we support and the CSS classes that used to determine each breakpoints. 首先,我们定义了我们支持的sizes
列表以及用于确定每个断点CSS类。 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>
。 detectScreenSize
. This is where we will write our logic to detect the screen size changes. We will complete that later. 我们有一个功能detectScreenSize
。 这是我们编写逻辑来检测屏幕尺寸变化的地方。 我们将在稍后完成。 HostListener
decorator to listen to the window resize
event. 屏幕大小更改时,我们需要每次都运行逻辑。 我们使用HostListener
装饰器来监听window resize
事件。 AfterViewInit
component lifecycle hook. 首次初始化应用程序时,我们还需要运行逻辑。 我们需要在AfterViewInit
组件生命周期挂钩期间运行它。 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{ return this.resizeSubject.asObservable().pipe(distinctUntilChanged()); } private resizeSubject: Subject ; constructor() { this.resizeSubject = new Subject(); } onResize(size: SCREEN_SIZE) { this.resizeSubject.next(size); }}
The resize service code is simple:
调整服务代码很简单:
resizeSubject
. 我们创建一个rxjs主题resizeSubject
。 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
组件中调用此方法) 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
大小。 在这种情况下,我们不需要通知。 (如果需要,可以删除运算符) 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:
让我们分解并一起研究逻辑:
ElementRef
and our newly created ResizeService
to our component. 首先,我们需要将ElementRef
和我们新创建的ResizeService
到组件中。 sizes
array and find it. 根据我们CSS类,在任何时间点,将只显示一个HTML元素。 我们遍历我们的sizes
数组并找到它。 sizes
array, we will use to find the element by the unique css class we defined earlier on is-<SIZE_ENUM>
. 对于我们的sizes
数组的每个大小,我们将使用通过我们先前在is-<SIZE_ENUM>
上定义的唯一css类来查找元素。 onResize
method. 找到当前可见的元素后,我们将通过调用onResize
方法通知调整大小服务。 You may place the size-detector
component under our root component app-component
. For example:
您可以将size-detector
组件放置在我们的根组件app-component
。 例如:
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: `Hello { {size}}!
`,})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.
现实生活中的用例场景之一可能是屏幕上显示了手风琴。 在移动设备中,您希望折叠所有手风琴面板,一次只显示活动面板。 但是,在台式机中,您可能需要扩展所有面板。
This is how we can detect the screen size changes without maintaining the actual breakpoint sizes in our JavaScript code. Here is the code: .
这样,我们就可以检测屏幕尺寸的变化,而无需在JavaScript代码中保留实际的断点尺寸。 这是代码: : 。
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!
就这样。 编码愉快!
翻译自:
响应式屏幕
转载地址:http://jsuwd.baihongyu.com/