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:
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?
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;
}