여우처럼 주제 바꾸기🦊 환경광 기반💡 Angular 응용 프로그램에서

DISCLAIMER: This is a continuation of this article where I write about implementing a theme switch in an Angular App. If you haven't already read that, I recommend you to read this first.👇🏻




That way you'll have more context of where I'm coming from.🙂

This article is intended for an audience with mixed experience levels. I've still added a TL;DR; below 👇🏻 as a precautionary measure, in case you're just interested in a specific section.


TL;박사

  • Why Dark Theme in the Dark? 🤷🏻‍♂️
  • Determining when it's dark⚡

  • Enter: the AmbientLightSensor Web Interface 📡
  • Feature Detection 🕵🏻‍♂️
  • Handling Edge Cases
  • Using the AmbientLightSensor Web Interface
  • Trying it out 🧪
  • Next Steps 👣
  • Closing Notes 🎉
  • 우선, 우리는 우리가 구축한 the same Angular App을 계속 사용할 것이다.

    왜 어두운 주제를 선택했습니까?🤷🏻‍♂️

    So, as I mentioned in the , Dark Mode is awesome and low-lit ambiances are the best suited for #DarkMode.

    Now changing themes in our App based on user interaction is OK. But we can take it to the next level by intelligently switching themes, based on the ambient lighting conditions that the user is in.

    And that's exactly what we're going to do in this article.

    언제 날이 어두워질지 확정하다⚡

    Now you might think, how exactly do we determine that it's dark. Well, there's something called Illuminance that can help us do that.

    According to Wikipedia:

    Illuminance is a measure of the luminous flux spread over a given area.

    One can think of luminous flux (which is measured in lumens BTW) as a measure of the total "amount" of visible light present, and the illuminance as a measure of the intensity of illumination on a surface.


    따라서 간단하게 말하면 빛의 통량은 어둠도와 반비례한다.
    참고로, 우리는 이 표를 사용하여 어둠의 정도를 확정할 것이다.

    상기 표에서 판단하면 광통량이 10 또는 더 적으면 우리가 어두운 환경에 처해 있다고 안전하게 생각할 수 있다.하지만 이것은 내가 선택한 숫자일 뿐이다.당신의 취향에 따라 10-20(또는 50, 마음에 들면) 사이의 숫자를 마음대로 선택할 수 있습니다


    좋아, 우리는 광통량에 따라 환경이 밝은지 어두운지 확정할 수 있어.그러나 우리는 어떻게 광통량을 확정합니까?🤔



    환경 광센서웹 인터페이스 시작📡

    This is a cool new interface from the Sensor APIs that returns the current light level or illuminance of the ambient light around the hosting device.

    The AmbientLightSensor object has a property named illuminance on it, that returns the current light level in lux of the ambient light level around the hosting device.

    It would only work on devices that have the ambient light sensor(hardware) on them(obviously). With the help of this AmbientLightSensor interface, our browsers can access the data collected by Ambient Light Sensors on devices. Cool, isn't it? 🤩

    What does that mean for us? Well, you can think of illuminance as the luminous flux incident on a surface.

    Now we know how to get the illuminance , and from the Illuminance Table 우리는 우리가 처한 환경이 어둠인지 광명인지 확인할 수 있다


    따라서, 만약illuminance<=10 (마찬가지로, 이 숫자는 전적으로 너에게 달려 있다), 그렇지 않으면


    사용AmbientLightSensor 인터페이스 접근illuminance은 매우 간단하며, MDN 문서의 this example에서 사용법이 언급되었습니다


    하지만 이 인터페이스를 사용할 때 우리는 많은 다른 일에 주의해야 한다.하나하나 봅시다


    피쳐 체크🕵🏻‍♂️:

    This is to determine whether the browser that is running our App has the AmbientLightSensor feature on it or not. To detect this, we can simply check:

    if ("AmbientLightSensor" in window) {
        // Yay! The Browser has what it takes
    }
    

    테두리 사례 처리:

    Checking whether the browser supports a feature doesn't guarantee that everything will work as expected. There might be errors:

    • While instantiation the sensor.
    • While using it.
    • When the user's permissions might be required to use the sensor.
    • When the sensor type might not be supported by the device.

    So all these scenarios would result in an error. So while using this interface, we'll have to cater to all these edge cases as well.

    Now that we know what we're looking at, let's try to implement this in our App.

    환경 광센서웹 인터페이스 사용

    Reading the illuminance and handling all these edge cases is the major task that we should delegate to a service. So let's implement a service in Angular that will handle all these things for us.

    The only output that we're going to expect from this service is an Observable that either gives us the illuminance or an error message that we could show to the user. So let's do this. I'll name this service AmbientLightSensorService .

    Also, since this service would also rely on the window object, let's provide it as a value so that we could then inject it as a dependency in our AmbientLightSensorService .

    So in our AppModule :

    응용 프로그램.단원송전 시스템



    ...
    import { AmbientLightSensorService } from "./ambient-light-sensor.service";
    
    @NgModule({
      ...
      providers: [
        AmbientLightSensorService,
        {
          provide: Window,
          useValue: window,
        },
        ...
      ]
    })
    export class AppModule {}
    

    우리는 또한 많은 메시지, 오류 유형, 센서 정책과 센서 이름 등을 처리할 것이다.우리도 그것들을 상수로 공개합시다:


    흔했어상수송전 시스템



    export const SENSOR_NAME = "AmbientLightSensor";
    export const SENSOR_POLICY_NAME = "ambient-light-sensor";
    export const ACCESS_DENIED = "denied";
    export const THEME_OPTIONS_URL = "/assets/options.json";
    export const THEME_BASE_PATH = "node_modules/@angular/material/prebuilt-themes";
    export const STYLE_TO_SET = "theme";
    export const DARK_THEME = "pink-bluegrey";
    export const LIGHT_THEME = "deeppurple-amber";
    
    export const ERROR_TYPES = {
      SECURITY: "SecurityError",
      REFERENCE: "ReferenceError",
      NOT_ALLOWED: "NotAllowedError",
      NOT_READABLE: "NotReadableError"
    };
    
    export const ERROR_MESSAGES = {
      UNSUPPORTED_FEATURE: "Your browser doesn't support this feature",
      BLOCKED_BY_FEATURE_POLICY:
        "Sensor construction was blocked by a feature policy.",
      NOT_SUPPORTED_BY_USER_AGENT: "Sensor is not supported by the User-Agent.",
      PREMISSION_DENIED: "Permission to use the ambient light sensor is denied.",
      CANNOT_CONNECT: "Cannot connect to the sensor."
    };
    

    내가 스스로 해석하는 방식으로 이 변수들을 명명하길 바란다


    지금, 이 서비스를 실현합시다:


    환경 광센서.서비스송전 시스템



    import { ReplaySubject, Observable } from "rxjs";
    import { Injectable } from "@angular/core";
    
    import {
      SENSOR_NAME,
      SENSOR_POLICY_NAME,
      ACCESS_DENIED,
      ERROR_TYPES,
      ERROR_MESSAGES
    } from "./common.const";
    
    @Injectable()
    export class AmbientLightSensorService {
      private illuminance: ReplaySubject <number> = new ReplaySubject <number>(1);
      illuminance$: Observable<number> = this.illuminance.asObservable();
    
      constructor(private window: Window) {
        try {
          if (SENSOR_NAME in window) {
            this.startReading();
          } else {
            this.illuminance.error(ERROR_MESSAGES.UNSUPPORTED_FEATURE);
          }
        } catch (error) {
          // Handle construction errors.
          if (error.name === ERROR_TYPES.SECURITY) {
            this.illuminance.error(ERROR_MESSAGES.BLOCKED_BY_FEATURE_POLICY);
          } else if (error.name === ERROR_TYPES.REFERENCE) {
            this.illuminance.error(ERROR_MESSAGES.NOT_SUPPORTED_BY_USER_AGENT);
          } else {
            this.illuminance.error(`${error.name}: ${error.message}`);
          }
        }
      }
    
      private startReading() {
        const sensor = new AmbientLightSensor();
        sensor.onreading = () => this.illuminance.next(sensor.illuminance);
        sensor.onerror = async event => {
          // Handle runtime errors.
          if (event.error.name === ERROR_TYPES.NOT_ALLOWED) {
            // Branch to code for requesting permission.
            const result = await navigator.permissions.query({
              name: SENSOR_POLICY_NAME
            });
            if (result.state === ACCESS_DENIED) {
              this.illuminance.error(ERROR_MESSAGES.PREMISSION_DENIED);
              return;
            }
            this.startReading();
          } else if (event.error.name === ERROR_TYPES.NOT_READABLE) {
            this.illuminance.error(ERROR_MESSAGES.CANNOT_CONNECT);
          }
        };
        sensor.start();
      }
    }
    
    

    우리가 지난 절에서 토론한 모든 변두리 상황을 만족시켜야 한다


    기본적으로, 우리는illuminanceReplaySubject<number>illuminance$


    "왜 aObservable<number>입니까?"물어볼 수도 있어.우리는 초기값이 없기 때문에 그것으로 대체하는 것이 더 의미가 있다


    현재, 우리는 호출ReplaySubject<number>(1) 방법을 통해 새로운 lux값을 아래로 밀어낸다BehaviorSubject<number>(null)illuminance.오류 상황에 대해 우리는 ReplaySubject 방법으로 오류를 제거합니다


    방법 이름과 오류 메시지 이름도 쉽게 설명할 수 있습니다.만약 내가 상세하게 논술할 수 있도록 아래에 평론을 발표해 주십시오


    현재 서비스가 준비되었습니다. 이 서비스를 의존항으로 주입하여 nexterrorHeaderComponent을 이용하여 lux값(또는 오류 메시지)에 접근할 수 있습니다


    조도



    import { Component, OnDestroy, OnInit } from "@angular/core";
    import { MatSnackBar } from "@angular/material/snack-bar";
    import { Observable, Subject } from "rxjs";
    import { takeUntil } from "rxjs/operators";
    
    import { AmbientLightSensorService } from "../ambient-light-sensor.service";
    import { DARK_THEME, LIGHT_THEME } from "../common.const";
    import { Option } from "../option.model";
    import { ThemeService } from "../theme.service";
    
    @Component({
      selector: "app-header",
      templateUrl: "./header.component.html",
      styleUrls: ["./header.component.css"]
    })
    export class HeaderComponent implements OnInit, OnDestroy {
      options$: Observable<Array<Option>> = this.themeService.getThemeOptions();
      private unsubscribe$ = new Subject<void>();
    
      constructor(
        private readonly themeService: ThemeService,
        private readonly alsService: AmbientLightSensorService,
        private readonly snackBar: MatSnackBar
      ) {}
    
      ngOnInit() {
        this.themeService.setTheme(DARK_THEME);
        this.alsService.illuminance$
          .pipe(takeUntil(this.unsubscribe$))
          .subscribe(
            illuminance => {
              illuminance <= 10
                ? this.themeService.setTheme(DARK_THEME)
                : this.themeService.setTheme(LIGHT_THEME);
            },
            error => this.showMessage(error)
          );
      }
    
      themeChangeHandler(themeToSet) {
        this.themeService.setTheme(themeToSet);
      }
    
      ngOnDestroy() {
        this.unsubscribe$.next();
        this.unsubscribe$.complete();
      }
    
      private showMessage(messageToShow) {
        this.snackBar.open(messageToShow, "OK", {
          duration: 4000
        });
      }
    }
    

    보시다시피:


    • 우리는 현재 Observable 의존항으로 주입할 것이다.
    • 생명주기 연결에서 우리header.component.tsAmbientLightSensorService에 연결된다.여기서부터:
      • 사용 ngOnInit 값 호출이 성공적으로 리셋되었습니다.여기에서 우리는 subscribe 값을 검사합니다.
        • 만약 Observable 이라면 우리는 illuminance 를 설정합니다.
        • 만약 illuminance 이라면 우리는 <= 10 를 설정합니다.

      • DARK_THEME 메시지 호출 오류 리셋을 사용합니다.그곳에서 우리는 분식집을 표시하기 위해 > 10 방법을 사용하기만 하면 된다.


    그 밖에 이번에 우리는 LIGHT_THEMEerror 로 업그레이드했기 때문에, 우리는 어떤 메모리 유출도 피하기 위해 명확한 일을 해야 한다.이를 위해 showMessage 연산자를 사용하여 설명합니다


    Read more about this approach in this article on AngularInDepth by Tomas Trajan


    이렇게.우리의 subscribe 주제 전환이 이미 준비되었다.테스트를 해볼게요


    시도해 보다🧪

    Before we do that, there's a caveat. And it has something to do with browser support.





    위에서 보듯이 브라우저 지원은 아직 좋지 않습니다.하지만 우리는 적어도 세계 최고의 브라우저에서 테스트를 해야 한다


    이를 위해, 우리는 우선 하나의 표지판을 사용해야 한다:


    그래서 나는 네비게이션Observable을 하고 핸드폰에 그것을 사용할 것이다. (내 노트북에는 환경광 센서가 없다.)그리고 핸드폰에 있는 브라우저를 다시 켤게요


    이제 테스트를 해볼게요:





    마지막 코드는:




    다음 단계👣

    As of now, there's a slight issue in the App. What if the user doesn't want to change the theme automagically, based on the lighting conditions? We can add in a simple fix as a settings/preference menu asking to turn this behavior ON/OFF and switch the theme only when the behavior is turned ON.

    Give it a try and implement the Preference/Settings Menu and then only switch the theme if the user has switched this auto-theme-switch behavior on.

    폐막사🎉

    Awwww! You're still here? Thanks for sticking around. I hope you liked it.

    I’m extremely grateful to , and for taking the time to proofread it and providing all the constructive feedback in making this article better.

    I hope this article has taught you something new related to Angular and Web in general. If it did, hit that 🧡/🦄 icon, and add it to your reading list(🔖). Also, share this article with your friends who are new to Angular/Web and want to achieve something similar.

    Please stay tuned while I work on uploading a video version of this article. 📺

    Icon Courtesy: Angular MaterialAngularIO출판사|CSSNoun프로젝트의monkik|Light sensorNoun프로젝트의Adnen Kadri


    다음까지.👋🏻

    좋은 웹페이지 즐겨찾기