You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中IntersectionObserver差异化阈值设置问题

问题

我给页面段落配置了IntersectionObserver,当前设置为段落滚动到指定阈值时,新段落淡入、上方段落淡出。但段落淡出时机比预期早,我希望仅当段落完全移出视口时才触发隐藏动画,现在段落一达到阈值就消失。请问能不能为元素的显示和隐藏分别设置不同的阈值?

我的代码示例

HTML

<div class="wrapper">
      <p>Start editing to see some magic happen :-)</p>
      <p #p1>
        Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
        eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
        voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
        clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
        amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
        nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed
        diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
        clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
        amet.
      </p>
      <p #p2>
        Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
        eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
        voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
        clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
        amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
        nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed
        diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
        clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
        amet.
      </p>
      <p #p3>
        Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
        eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
        voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
        clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
        amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
        nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed
        diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
        clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
        amet.
      </p>
      <p #p4>
        Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
        eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
        voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
        clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
        amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
        nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed
        diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
        clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
        amet.
      </p>
      <p #p5>
        Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
        eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
        voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
        clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
        amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
        nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed
        diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
        clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
        amet.
      </p>
      
      <div class="sites-circle" [ngClass]="mouseMove ? 'onMove' : 'notMove'">
        hello
      </div>
    </div>

TypeScript

import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements AfterViewInit  {

  constructor(private el: ElementRef) {}

  ngAfterViewInit() {
  const threshold = 0.7; // how much % of the element is in view
  const observer = new IntersectionObserver(
      (entries) => {
          entries.forEach((entry) => {
              if (entry.isIntersecting) {
                entry.target.classList.add('activate');
              }
              else{
                entry.target.classList.remove('activate');
              }
          });
      },
      { threshold }
  );
  observer.observe(this.p1.nativeElement);
  observer.observe(this.p2.nativeElement);
  observer.observe(this.p3.nativeElement);
  observer.observe(this.p4.nativeElement);
  observer.observe(this.p5.nativeElement);

}
@ViewChild('p1') p1!: ElementRef;
@ViewChild('p2') p2!: ElementRef;
@ViewChild('p3') p3!: ElementRef;
@ViewChild('p4') p4!: ElementRef;
@ViewChild('p5') p5!: ElementRef;
}

CSS

p {
  font-family: Lato;
  font-size:35px;
  opacity:0;
  transition:.4s;
}

.wrapper {
  width: 100%;
}

p.activate{
  opacity:1;
}
解决方案

可以实现,下面提供两种可行方案:

方案一:单Observer+多阈值数组

通过设置包含显示、隐藏阈值的数组,在回调中区分元素进入/退出的阶段,分别处理逻辑:

修改后的TypeScript代码:

import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements AfterViewInit  {

  constructor(private el: ElementRef) {}

  ngAfterViewInit() {
    // 配置显示阈值(70%可见时触发)和隐藏阈值(0%可见时触发)
    const thresholds = [0, 0.7];
    const observer = new IntersectionObserver(
        (entries) => {
            entries.forEach((entry) => {
                // 元素进入视口且达到70%可见度时,添加激活类
                if (entry.isIntersecting && entry.intersectionRatio >= 0.7) {
                  entry.target.classList.add('activate');
                }
                // 元素完全移出视口(无任何部分可见)时,移除激活类
                else if (!entry.isIntersecting && entry.intersectionRatio === 0) {
                  entry.target.classList.remove('activate');
                }
            });
        },
        { threshold: thresholds }
    );
    observer.observe(this.p1.nativeElement);
    observer.observe(this.p2.nativeElement);
    observer.observe(this.p3.nativeElement);
    observer.observe(this.p4.nativeElement);
    observer.observe(this.p5.nativeElement);
  }

  @ViewChild('p1') p1!: ElementRef;
  @ViewChild('p2') p2!: ElementRef;
  @ViewChild('p3') p3!: ElementRef;
  @ViewChild('p4') p4!: ElementRef;
  @ViewChild('p5') p5!: ElementRef;
}

方案二:双独立Observer(逻辑更清晰)

分别创建两个Observer,一个负责监听元素进入视口的触发条件,另一个负责监听元素完全移出的条件:

修改后的TypeScript代码:

import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements AfterViewInit  {

  constructor(private el: ElementRef) {}

  ngAfterViewInit() {
    // 监听显示:元素70%可见时添加激活类
    const showObserver = new IntersectionObserver(
      (entries) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            entry.target.classList.add('activate');
          }
        });
      },
      { threshold: 0.7 }
    );

    // 监听隐藏:元素完全移出视口时移除激活类
    const hideObserver = new IntersectionObserver(
      (entries) => {
        entries.forEach(entry => {
          if (!entry.isIntersecting && entry.intersectionRatio === 0) {
            entry.target.classList.remove('activate');
          }
        });
      },
      { threshold: 0 }
    );

    // 批量绑定监听
    const paragraphs = [this.p1, this.p2, this.p3, this.p4, this.p5];
    paragraphs.forEach(p => {
      showObserver.observe(p.nativeElement);
      hideObserver.observe(p.nativeElement);
    });
  }

  @ViewChild('p1') p1!: ElementRef;
  @ViewChild('p2') p2!: ElementRef;
  @ViewChild('p3') p3!: ElementRef;
  @ViewChild('p4') p4!: ElementRef;
  @ViewChild('p5') p5!: ElementRef;
}

方案说明

  • 方案一通过单Observer实现,减少实例数量,适合简单场景;
  • 方案二拆分逻辑为两个独立Observer,代码可读性更强,便于后续扩展其他交互;
  • 两种方案都能实现元素70%可见时淡入,完全移出视口时淡出的效果,解决原问题中淡出过早的问题。

内容的提问来源于stack exchange,提问作者Snorlax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 18:01:24