Change div opacity in Angular

69 Views Asked by At

So I'm trying to do an animation that moves the divs and fades them based on the scroll distance. I've got the transform working,but the opacity change only appears to work on desktop. When viewing it from my mobile device (iOS safari, and iOS firefox) the opacity doesn't change, and I cannot figure out why. These animations also appear choppy when scrolling on mobile, but not on desktop.

@HostListener('window:scroll', ['$event'])
@HostListener('window:touchmove', ['$event'])
onScroll(): void {
  if (this.isEffectActive) {
    /* get position of the bottom of the component */
    const introductionContainerBottomPos =
      this.elementRef.nativeElement.offsetTop +
      this.elementRef.nativeElement.offsetHeight;
    const scrollPos = window.scrollY;
    const distance =
      introductionContainerBottomPos -
      this.clamp(scrollPos, 0, introductionContainerBottomPos);
    const val = this.lerp(0, 1, distance / introductionContainerBottomPos);

    this.fadeValue = Math.round(val * 100) / 100;

    window.requestAnimationFrame(() => {
      this.container.nativeElement.style.opacity = this.fadeValue;
      this.container.nativeElement.style.transform = `translateY(${this.getTranslation()}px)`;

      this.container2.nativeElement.style.opacity = this.fadeValue;
      this.container2.nativeElement.style.transform = `translateY(${this.getTranslation()}px)`;
    });
  }
}

Anyone know a better solution?

I've tried changing the filter: alpha but that didn't work. I tried inlining the opacity, but that doesn't work either.

EDIT: I've refactored it to add some of the suggestions in the comments:

  @HostListener('window:scroll', ['$event'])
  @HostListener('window:touchmove', ['$event'])
  onScroll(): void {
    /* get position of the bottom of the component */
    const introductionContainerBottomPos =
      this.container.nativeElement.offsetTop +
      this.container.nativeElement.offsetHeight;
    const scrollPos = window.scrollY;
    const distance =
      introductionContainerBottomPos -
      this.clamp(scrollPos, 0, introductionContainerBottomPos);
    const val = this.lerp(0, 1, distance / introductionContainerBottomPos);

    this.fadeValue = Math.round(val * 100) / 100;

    window.requestAnimationFrame(() => {
      this.title.nativeElement.style.opacity =
        this.fadeValue > 0 ? this.fadeValue : 0;

      this.body.nativeElement.style.opacity =
        this.fadeValue > 0 ? this.fadeValue : 0;
    });

    window.requestAnimationFrame(() => {
      this.title.nativeElement.style.transform =
        this.fadeValue > 0
          ? `translateY(${this.getTranslation()}px)`
          : this.container.nativeElement.style.transform;
      this.body.nativeElement.style.transform =
        this.fadeValue > 0
          ? `translateY(${this.getTranslation()}px)`
          : this.container.nativeElement.style.transform;
    });
  }

Looks like this.elementRef is Nan on mobile, so using a ViewChild fixes that, which fixed the fading value not being updated on mobile. I've fixed half my issues, but now I've noticed the touchmove event runs the animation much smoother than scroll does (I think it gets updated more frequently thus allowing a smoother looking animation. Is there a better even to use than scroll? Or maybe a life cycle hook that could help me?

1

There are 1 best solutions below

1
Abru007 On
  1. Hardware Acceleration: In your component's CSS file, add the will-change property to the elements you're animating.

.animated-element { will-change: transform; }

2.Throttling: Implement a debounce function to throttle the scroll event handler. You can use a library like lodash or implement a simple debounce function yourself.

    import { Component, HostListener } from '@angular/core';
import { debounce } from 'lodash';

@Component({
  selector: 'app-your-component',
  templateUrl: './your-component.component.html',
  styleUrls: ['./your-component.component.css']
})
export class YourComponent {
  isEffectActive: boolean = true;

  @HostListener('window:scroll', ['$event'])
  onScroll = debounce(() => {
    if (this.isEffectActive) {
      // Your scroll handling logic here
    }
  }, 50); // Adjust the debounce delay as needed
}

3:Optimize Opacity Changes: Use Angular's animation module to handle opacity changes. Define animations in your component's TypeScript file using Angular's animation DSL.

    import { Component, HostBinding } from '@angular/core';
import { trigger, transition, style, animate } from '@angular/animations';

@Component({
  selector: 'app-your-component',
  templateUrl: './your-component.component.html',
  styleUrls: ['./your-component.component.css'],
  animations: [
    trigger('fadeAnimation', [
      transition(':enter', [
        style({ opacity: 0 }),
        animate('300ms', style({ opacity: 1 })),
      ]),
      transition(':leave', [
        animate('300ms', style({ opacity: 0 })),
      ]),
    ]),
  ],
})
export class YourComponent {
  @HostBinding('@fadeAnimation') fadeAnimation = true;

  // Your other component logic
}
  1. Reducing Paint Area:Target only the necessary elements for opacity and transform changes in your scroll event handler.

code

@HostListener('window:scroll', ['$event'])
onScroll() {
  if (this.isEffectActive) {
    const scrollPos = window.scrollY;
    // Only update properties for the necessary elements
    this.container.nativeElement.style.transform = translateY(${this.getTranslation()}px);
    this.container2.nativeElement.style.transform = translateY(${this.getTranslation()}px);
  }
}

These approaches should help optimize your Angular code for better performance and compatibility across different devices and browsers. Adjust the values and settings as needed for your specific use case.