首页 > 解决方案 > 如何从垫选择下拉输入中删除动画

问题描述

我想对 mat-select 输入进行风格化。默认情况下 mat-select 有一些动画。如果我单击箭头并展开列表,则会弹出列表。我想删除这个动画表单 mat-select 输入。我想用与原生 html 选择相同的动画弹出效果来实现 mat-select 输入。我不能使用本机 html 选择。

<mat-form-field>
  <mat-select>
    <mat-option>option1</mat-option>
    <mat-option>option2</mat-option>
    <mat-option>option3</mat-option>
  </mat-select>
</mat-form-field>

标签: cssangularangular-material

解决方案


我解决了我的问题:

  1. 可以从 mat-select 中删除动画。我通过这种方式实现它:
.mat-select-panel {
  &.ng-animating {
    visibility: hidden;
  }
  1. Angular Material 还支持在 . 在我的情况下是不可接受的,但你也可以试试这个:
<mat-form-field appearance="fill">
  <select matNativeControl>
    <option value="option1">option1</option>
    <option value="option2">option2</option>
    <option value="option3">option3</option>
  </select>
</mat-form-field>
  1. 我的最终解决方案是创建一个模拟原生下拉动画的指令。这是此解决方案的代码:
<mat-form-field
    appearance="outline"
    class="select-form"
    appDropDown
>
    <mat-label>Label</mat-label>
    <mat-select
        panelClass="select-panel"
        placeholder="placeholder"
    >
      <mat-option value="option1">option1</mat-option>
      <mat-option value="option2">option2</mat-option>
      <mat-option value="option3">option3</mat-option>
    </mat-select>
</mat-form-field>
import {
  Directive,
  HostListener,
  ElementRef,
  AfterViewInit,
  ContentChild,
  Renderer2,
  OnDestroy,
} from '@angular/core';
import {
  Overlay,
  ConnectedPositionStrategy,
  OverlaySizeConfig,
} from '@angular/cdk/overlay';
import {
  MatSelect,
  MatOption,
  MatOptionSelectionChange,
} from '@angular/material';
import { Subscription } from 'rxjs';

@Directive({
  selector: '[appDropDown]',
})
export class DropDownDirective implements AfterViewInit, OnDestroy {
  @ContentChild(MatSelect) select: MatSelect;

  private overlaySubscription: Subscription;
  private openPanelSubscription: Subscription;
  private closePanelSubscription: Subscription;
  private optionChangeSubscription: Subscription;
  private stateChangeSubscription: Subscription;

  private triggerValue: HTMLDivElement;
  private triggerLabelClass = 'trigger-label';
  private triggerOptionClass = 'trigger-option';
  private openPanelClass = 'active';

  constructor(
    private el: ElementRef,
    private overlayService: Overlay,
    private renderer: Renderer2
  ) {}

  @HostListener('window:resize', [])
  onWindowResize(): void {
    if (this.select.panelOpen) {
      this.updateOverlaySize();
    }
  }

  ngAfterViewInit(): void {
    this.configTrigger();
    this.configOverlay();
  }

  private configOverlay(): void {
    this.overlaySubscription = this.select.overlayDir.attach.subscribe(() => {
      this.setOverlayPositionStrategy();
      this.updateOverlaySize();
    });
  }

  private configTrigger(): void {
    this.triggerValue = this.select.trigger.nativeElement.firstChild;

    this.addClosePanelSubscription();
    this.addOpenPanelSubscription();
    this.addOptionChangeSubscription();
    this.addStateChangeSubscription();
  }

  private addClosePanelSubscription(): void {
    this.closePanelSubscription = this.select._closedStream.subscribe(() => {
      this.renderer.removeClass(this.el.nativeElement, this.openPanelClass);
      if (!this.select.empty) {
        this.setTriggerStyleOption();
        const option = this.select.selected as MatOption;
        this.triggerValue.textContent = option.viewValue;
      }
    });
  }

  private addOpenPanelSubscription(): void {
    this.openPanelSubscription = this.select._openedStream.subscribe(() => {
      this.setTriggerStyleLabel();
      this.renderer.addClass(this.el.nativeElement, this.openPanelClass);
      this.triggerValue.textContent = this.select.placeholder;
    });
  }

  private addOptionChangeSubscription(): void {
    this.optionChangeSubscription = this.select.optionSelectionChanges.subscribe(
      (option: MatOptionSelectionChange) => {
        this.setTriggerStyleOption();
        this.renderer.removeClass(this.el.nativeElement, this.openPanelClass);
        this.triggerValue.textContent = option.source.viewValue;
      }
    );
  }

  private addStateChangeSubscription(): void {
    this.stateChangeSubscription = this.select.stateChanges.subscribe(() => {
      if (this.select.empty) {
        this.setTriggerStyleLabel();
        this.triggerValue.textContent = this.select.placeholder;
      } else {
        this.setTriggerStyleOption();
        const option = this.select.selected as MatOption;
        this.triggerValue.textContent = option.viewValue;
      }
    });
  }

  private setOverlayPositionStrategy(): void {
    const positionConfig: ConnectedPositionStrategy = this.overlayService
      .position()
      .connectedTo(
        this.el,
        { originX: 'start', originY: 'center' },
        { overlayX: 'start', overlayY: 'top' }
      );

    this.select.overlayDir.overlayRef.updatePositionStrategy(positionConfig);
  }

  private updateOverlaySize(): void {
    const nativeFormField = this.el.nativeElement;
    const sizeConfig: OverlaySizeConfig = {
      width: nativeFormField.offsetWidth,
      height: nativeFormField.offsetHeight,
      minWidth: nativeFormField.minWidth,
      minHeight: nativeFormField.minHeight,
    };

    this.select.overlayDir.overlayRef.updateSize(sizeConfig);
  }

  private setTriggerStyleLabel(): void {
    this.renderer.removeClass(this.triggerValue, this.triggerOptionClass);
    this.renderer.addClass(this.triggerValue, this.triggerLabelClass);
  }

  private setTriggerStyleOption(): void {
    this.renderer.removeClass(this.triggerValue, this.triggerLabelClass);
    this.renderer.addClass(this.triggerValue, this.triggerOptionClass);
  }

  ngOnDestroy(): void {
    this.overlaySubscription.unsubscribe();
    this.openPanelSubscription.unsubscribe();
    this.closePanelSubscription.unsubscribe();
    this.optionChangeSubscription.unsubscribe();
    this.stateChangeSubscription.unsubscribe();
  }
}
.select-form {
  .mat-select {
    position: absolute;
    bottom: 30%;
    text-align: left;
  }

  .mat-select-trigger {
    position: relative;
    left: 3px;
    bottom: 2px;
    font-size: 14px;
    font-weight: 500;

    .mat-select-placeholder {
      color: #4b0082;
    }

    .mat-select-value,
    .trigger-option {
      color: #000;
    }

    .trigger-label {
      color: #4b0082;
    }
  }
}

.mat-form-field-appearance-outline.active .mat-form-field-outline {
  .mat-form-field-outline-start {
    border-bottom-left-radius: 0px;
  }

  .mat-form-field-outline-end {
    border-bottom-right-radius: 0px;
  }
}

.select-panel.mat-select-panel {
  &.ng-animating {
    visibility: hidden;
  }

  position: absolute;
  border: 1px solid #4b0082;
  top: calc(12% - 1px);
  box-shadow: none;
  min-width: 100% !important;
  border-top: 1px solid #bfbfbf;
  border-radius: 0px 0px 5px 5px;
  box-shadow: 0 3px 4px 0 transparentize(#000, 0.85) !important;
  border-top-style: none;

  animation-name: opening-panel;
  animation-duration: 0.3s;

  @keyframes opening-panel {
    0% {
      transform: scaleY(0.8);
    }

    100% {
      transform: scaleY(1);
    }
  }

  .mat-option.mat-selected:not(.mat-option-multiple) {
    background-color: #c27fc1 !important;
  }

  .mat-option:hover:not(.mat-option-disabled) {
    background-color: #c27fc1 !important;
  }

  .mat-option.mat-active {
    background-color: transparent !important;
  }

  &::-webkit-scrollbar {
    width: 5px;

    &-track {
      background: #bfbfbf;
      border-radius: 6.5px;
      margin-bottom: 10px;
    }

    &-thumb {
      background: #bfbfbf;
      border-radius: 6.5px;
    }
  }
}

.mat-select-panel {
  .mat-option.mat-option.mat-selected:not(.mat-option-multiple) {
    background: #c27fc1;
    color: $text-color !important;
  }

  .mat-option {
    font-weight: 600;
  }
}

.mat-option.mat-option-disabled {
  color: #4b0082;
  font-weight: 500;
  font-size: inherit;

  .select-arrow {
    position: absolute;
    right: 0px;
    top: 10px;
  }
}

.table-filter-panel.mat-select-panel {
  position: absolute;
  left: -5px;
  top: 27px;
  box-shadow: none;
  min-width: calc(100% + 32px);
  border: 1px solid #4b0082;
  border-radius: 0 0 1px 1px;
  box-shadow: 0 3px 4px 0 transparentize(#000, 0.85) !important;
  border-top-color: #c27fc1;

  .mat-option {
    color: #bfbfbf;

    &.mat-selected:not(.mat-option-multiple) {
      background-color: #c27fc1 !important;
    }

    &:hover:not(.mat-option-disabled) {
      background-color: #c27fc1 !important;
    }

    &.mat-active {
      background-color: transparent !important;
    }
  }

  &::-webkit-scrollbar {
    width: 5px;

    &-track {
      background: #bfbfbf;
      border-radius: 6.5px;
      margin-bottom: 10px;
    }

    &-thumb {
      background: #bfbfbf;
      border-radius: 6.5px;
    }
  }
}

推荐阅读