列表元素滚动动画

发布于:2025-07-02 ⋅ 阅读:(27) ⋅ 点赞:(0)

在这里插入图片描述
基于Vue2以及指令的列表滚动显示动画

<template>
 <div>
  <h2>Scroll Animation Demo</h2>
  <div class="list-container">
    <div class="spacer">向下滚动查看动画效果...</div>
    <div v-for="item in 10" :key="item" v-slide-in class="list-item">
      Item {{ item }}
    </div>
    <div class="spacer">向上滚动不会触发新的动画</div>
  </div>
 </div>
</template>

<script>
const OFFSET = 200;
const DURATION = 800;
const EASING = 'cubic-bezier(0.25, 0.46, 0.45, 0.94)';
const FILL = 'forwards';

// 使用全局 WeakMap 和 IntersectionObserver
const animationMap = new WeakMap();
const animatedElements = new WeakSet(); // 记录已经动画过的元素
let lastScrollY = 0; // 记录上次滚动位置

// 滚动事件处理函数
const handleScroll = () => {
  lastScrollY = window.scrollY;
  console.log('Scroll position updated:', lastScrollY);
};

const intersectionObserver = new IntersectionObserver((entries) => {
  const currentScrollY = window.scrollY;
  const isScrollingDown = currentScrollY >= lastScrollY;
  
  console.log('Observer triggered:', { 
    currentScrollY, 
    lastScrollY, 
    isScrollingDown,
    entriesCount: entries.length
  });
  
  entries.forEach((entry) => {
    console.log('Entry:', { 
      isIntersecting: entry.isIntersecting,
      alreadyAnimated: animatedElements.has(entry.target)
    });
    
    if (entry.isIntersecting && !animatedElements.has(entry.target)) {
      const animation = animationMap.get(entry.target);
      if (animation) {
        console.log('Playing animation for element');
        animation.play();
        animatedElements.add(entry.target); // 标记为已动画
      }
    }
  });
}, {
  threshold: 0.1,
  rootMargin: '0px 0px -10px 0px' // 调整为更容易触发
});

export default {
  name: 'TestContent11',
  directives: {
    slideIn: {
      inserted(el) {
        // 检查浏览器是否支持 Web Animations API
        if (!el.animate) {
          console.warn('Web Animations API not supported');
          return;
        }
        
        // 设置初始样式,确保元素在动画前是隐藏的
        el.style.opacity = '0';
        
        // 创建动画
        const animation = el.animate([
          {
            transform: `translateY(${OFFSET}px)`,
            opacity: 0,
          },
          {
            transform: 'translateY(0)',
            opacity: 1,
          }
        ], {
          duration: DURATION,
          easing: EASING,
          fill: FILL,
        });
        
        // 暂停动画
        animation.pause();
        console.log('Animation created and paused for element');
        
        // 开始观察元素
        intersectionObserver.observe(el);
        console.log('Element is being observed');
        
        // 存储动画引用
        animationMap.set(el, animation);
      },
      
      unbind(el) {
        // 停止观察元素
        intersectionObserver.unobserve(el);
        
        // 清理动画引用
        if (animationMap.has(el)) {
          const animation = animationMap.get(el);
          animation.cancel();
          animationMap.delete(el);
        }
        
        // 清理已动画标记
        animatedElements.delete(el);
      }
    }
  },
  data() {
    return {};
  },
  mounted() {
    // 初始化滚动位置
    lastScrollY = window.scrollY;
    console.log('Initial scroll position:', lastScrollY);
    
    // 监听滚动事件更新位置
    window.addEventListener('scroll', handleScroll, { passive: true });
  },
  beforeDestroy() {
    // 移除滚动事件监听
    window.removeEventListener('scroll', handleScroll);
  }
};
</script>

<style scoped>
.list-container {
  max-width: 600px;
  margin: 0 auto;
  padding-bottom: 500px; /* 添加足够的空间以便滚动 */
}

.list-item {
  padding: 30px;
  margin: 20px 0;
  background-color: white;
  border-radius: 15px;
  box-shadow: 0 8px 25px rgba(0,0,0,0.1);
  border-left: 5px solid #667eea;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
  font-size: 1.5em;
  font-weight: bold;
  text-align: center;
  color: #667eea;
}

.list-item:hover {
  transform: translateY(-5px);
  box-shadow: 0 15px 35px rgba(0,0,0,0.15);
}

.spacer {
  height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #667eea;
  font-size: 1.2em;
  opacity: 0.8;
}
</style>

网站公告

今日签到

点亮在社区的每一天
去签到