Is there a better way to change opacity and background of button on click?

58 views Asked by At

I am trying to get a screen working in Ionic/Angular where different buttons can be pressed and change their opacity until a threshold is reached and then change the color of the button. An overview is provided with the picture below:

Calibration

My current implementation is working but I think there should be a better way to achieve this because it looks kinda bad with all the if cases and I am not sure if this is the right way to access the button and edit its opacity.

My implementation:

calibration.page.ts

const BUTTONS = [
  { id: 1, opacity: 1.0 },
  { id: 2, opacity: 1.0 },
  { id: 3, opacity: 1.0 },
  { id: 4, opacity: 1.0 },
  { id: 5, opacity: 1.0 },
  { id: 6, opacity: 1.0 },
  { id: 7, opacity: 1.0 },
  { id: 8, opacity: 1.0 },
  { id: 9, opacity: 1.0 }
]

export class CalibrationPage implements OnInit {

  buttons = BUTTONS;

  @ViewChild('Pt0') button0: any;
  @ViewChild('Pt1') button1: any;
  @ViewChild('Pt2') button2: any;
  @ViewChild('Pt3') button3: any;
  @ViewChild('Pt4') button4: any;
  @ViewChild('Pt5') button5: any;
  @ViewChild('Pt6') button6: any;
  @ViewChild('Pt7') button7: any;
  @ViewChild('CalibrationPoint') buttonCalibration: any;

buttonClicked(buttonId: any) {
    if (buttonId == 0) {
      this.buttons[buttonId].opacity -= 0.2;
      if (this.buttons[buttonId].opacity > 0.1) {
        this.button0.el.style.setProperty('--opacity', this.buttons[buttonId].opacity)
      } else {
        this.button0.el.style.setProperty('--background', 'red')
        this.button0.el.style.setProperty('--opacity', '1.0')
      }
    } else if (buttonId == 1) {
      this.buttons[buttonId].opacity -= 0.2;
      if (this.buttons[buttonId].opacity > 0.1) {
        this.button1.el.style.setProperty('--opacity', this.buttons[buttonId].opacity)
      } else {
        this.button1.el.style.setProperty('--background', 'red')
        this.button1.el.style.setProperty('--opacity', '1.0')
      }
          ... and so on for all the other buttons

calibration.page.html

<ion-grid>
  <ion-row>
    <ion-col>
      <ion-button #Pt0 id="Pt0" shape="circle" size="small" (click)="buttonClicked(0)">
        <ion-icon slot="icon-only"></ion-icon>
      </ion-button>
    </ion-col>
    <ion-col>
      <ion-button #Pt1 id="Pt1" shape="circle" size="small" (click)="buttonClicked(1)">
        <ion-icon slot="icon-only"></ion-icon>
      </ion-button>
    </ion-col>
  </ion-row>
  <ion-row>
    <ion-col>
      <ion-button #Pt2 id="Pt2" shape="circle" size="small" (click)="buttonClicked(2)">
        <ion-icon slot="icon-only"></ion-icon>
      </ion-button>
    </ion-col>
  </ion-row>
  <ion-row>
    <ion-col>
      <ion-button #Pt3 id="Pt3" shape="circle" size="small" (click)="buttonClicked(3)">
        <ion-icon slot="icon-only"></ion-icon>
      </ion-button>
    </ion-col>
    <ion-col>
      <ion-button #CalibrationPoint id="CalibrationPoint" shape="circle" size="small" disabled="true" (click)="buttonClicked(8)">
        <ion-icon slot="icon-only"></ion-icon>
      </ion-button>
    </ion-col>
    <ion-col>
      <ion-button #Pt4 id="Pt4" shape="circle" size="small" (click)="buttonClicked(4)">
        <ion-icon slot="icon-only"></ion-icon>
      </ion-button>
    </ion-col>
  </ion-row>
  <ion-row>
    <ion-col>
      <ion-button #Pt5 id="Pt5" shape="circle" size="small" (click)="buttonClicked(5)">
        <ion-icon slot="icon-only"></ion-icon>
      </ion-button>
    </ion-col>
  </ion-row>
  <ion-row>
    <ion-col>
      <ion-button #Pt6 id="Pt6" shape="circle" size="small" (click)="buttonClicked(6)">
        <ion-icon slot="icon-only"></ion-icon>
      </ion-button>
    </ion-col>
    <ion-col>
      <ion-button #Pt7 id="Pt7" shape="circle" size="small" (click)="buttonClicked(7)">
        <ion-icon slot="icon-only"></ion-icon>
      </ion-button>
    </ion-col>
  </ion-row>
</ion-grid>

Can someone help me here and/or have tips to achieve what I want?

1

There are 1 answers

3
Eliseo On BEST ANSWER

The first is use .css to animate the "dots". If, e.g. you define a .css like

.dot {
  background-color: silver;
  border-radius: 50%;
  height: 1rem;
  width:1rem;
}
.dot.animate{
  background:red;

  animation:
  3s 1 alternate fade;
}
@keyframes fade {
  0% {
    opacity: 1;
    background:silver;
  }
  99% {
    opacity: 0;
    background:silver;
  }
  100%
  {
    opacity:1;
  }
}
.dot.show{
  background-color:silver;
}

We can see that the only is replace one class by another. Imagine some simple

<div #dot class="dot show" (click)="animate(dot)"></div>

And the function animate

  animate(element:HTMLElement)
  {
    element.classList.remove("show");
    element.classList.add("animate");
  }

See that, in Angular, when we use a template reference variable and use in the .html we are pass the HTMLElement. Be careful!, if we use @ViewChild we get a ElementRef (and in elementRef.nativeElement is where we "reach" the HTLMElement)

Well, I imagine you need "some more" that "animate" the buttons. So, not use ViewChild, else ViewChildren. ViewChildren is a QueryList So if we have some like

@for(i of [1,2,3,4]; track $index)
{
<div  #dot class="dot show" [attr.data-index]="i" 
    (click)="animate($index)"></div> //<--I choose now pass the index
}

Our animate can be now

  //declare first a variable message and ViewChildren
  message:string=""
  @ViewChildren('dot') dots!:QueryList<ElementRef>

  animate(index:number)
  {
    const dot=this.dots.find((x:ElementRef,i:number)=>i==index)
    dot?.nativeElement.classList.remove("show");
    dot?.nativeElement.classList.add("animate");
    this.message="You click the "+
         dot?.nativeElement.getAttribute('data-index')+ " dot"
  }

a stackblitz (it's only angular)

Update

To position the dots, we can use css grid (see this large article to understand it)

.container {
  display: grid;
  grid-template-columns: repeat(2,1fr);
  grid-template-rows: repeat(7, 1fr);
  grid-auto-flow:column row;
}
.dot:nth-child(3n)
{
  grid-column-start:1;
  grid-column-end:3;
  justify-self:center;
}
.dot:nth-child(3n-1)
{
  justify-self:end;
}