当前位置: 首页 > ops >正文

lenis选项卡举例

<div class="tabs"><!-- 选项卡导航 --><nav class="tab-nav"><button data-tab="tab1" class="active">Tab 1</button><button data-tab="tab2">Tab 2</button><button data-tab="tab3">Tab 3</button></nav><!-- 选项卡内容容器 --><div class="tab-content active" id="tab1"><div class="scroll-content"> <!-- 滚动内容区1 --> </div></div><div class="tab-content" id="tab2"><div class="scroll-content"> <!-- 滚动内容区2 --> </div></div><div class="tab-content" id="tab3"><div class="scroll-content"> <!-- 滚动内容区3 --> </div></div>
</div>

CSS 关键样式

.tabs {height: 100vh;display: flex;flex-direction: column;
}.tab-content {flex: 1;overflow: hidden; /* 隐藏原生滚动条 */position: relative;
}.scroll-content {height: 100%; /* 关键:使内容区可滚动 */padding: 20px;
}

 JavaScript 核心逻辑

初始化 Lenis 管理器
class TabLenisManager {constructor() {this.currentLenis = nullthis.tabs = {}}// 创建选项卡实例create(tabId) {if (this.tabs[tabId]) returnconst wrapper = document.getElementById(tabId)const content = wrapper.querySelector('.scroll-content')this.tabs[tabId] = new Lenis({wrapper: wrapper,       // 容器元素content: content,       // 内容元素duration: 1.2,smoothWheel: true})// 保存滚动位置this.tabs[tabId].scroll = 0}// 切换选项卡switch(tabId) {// 停止当前实例if (this.currentLenis) {this.currentLenis.stop()this.currentLenis.scroll = this.currentLenis.animatedScroll}// 初始化新实例if (!this.tabs[tabId]) {this.create(tabId)}this.currentLenis = this.tabs[tabId]this.currentLenis.start()this.currentLenis.scrollTo(this.currentLenis.scroll)}
}

 初始化与事件绑定

const tabManager = new TabLenisManager()// 初始化第一个选项卡
tabManager.create('tab1')
tabManager.switch('tab1')// 选项卡切换事件
document.querySelectorAll('[data-tab]').forEach(btn => {btn.addEventListener('click', () => {const tabId = btn.dataset.tabtabManager.switch(tabId)})
})

 功能增强建议

动态内容处理

当选项卡内容变化时,调用:

tabManager.currentLenis.resize()
保存滚动位置

在切换时记录位置:

class TabLenisManager {constructor() {this.scrollPositions = {}}switch(tabId) {// 保存旧位置if (this.currentLenis) {this.scrollPositions[this.currentTabId] = this.currentLenis.scroll}// 恢复新位置if (this.scrollPositions[tabId]) {this.currentLenis.scrollTo(this.scrollPositions[tabId])}}
}

 响应式高度调整

function setTabHeight() {const navHeight = document.querySelector('.tab-nav').offsetHeightdocument.querySelectorAll('.tab-content').forEach(tab => {tab.style.height = `calc(100vh - ${navHeight}px)`})
}window.addEventListener('resize', setTabHeight)
setTabHeight()

 

性能优化

1. 实例销毁策略
// 最多保留2个非活动实例
const MAX_CACHED = 2class TabLenisManager {switch(tabId) {// 清除多余实例const keys = Object.keys(this.tabs)if (keys.length > MAX_CACHED) {const toRemove = keys.filter(k => k !== tabId).pop()this.tabs[toRemove].destroy()delete this.tabs[toRemove]}}
}
滚动事件代理
const handleScroll = (e) => {if (tabManager.currentLenis) {// 阻止原生滚动e.preventDefault()tabManager.currentLenis.scrollTo(tabManager.currentLenis.scroll + e.deltaY)}
}document.querySelectorAll('.tab-content').forEach(tab => {tab.addEventListener('wheel', handleScroll, { passive: false })
})

 完整 React 示例

import { useEffect, useRef } from 'react'
import Lenis from '@studio-freight/lenis'function Tabs() {const lenisRefs = useRef({})const activeTab = useRef('tab1')// 初始化实例const initLenis = (tabId) => {const wrapper = document.getElementById(tabId)const content = wrapper.querySelector('.scroll-content')lenisRefs.current[tabId] = new Lenis({wrapper,content,smoothWheel: true})}// 切换处理const switchTab = (tabId) => {// 保存旧位置const prevLenis = lenisRefs.current[activeTab.current]prevLenis.stop()// 初始化新实例if (!lenisRefs.current[tabId]) {initLenis(tabId)}const newLenis = lenisRefs.current[tabId]newLenis.start()activeTab.current = tabId}// 清理useEffect(() => {initLenis('tab1')return () => {Object.values(lenisRefs.current).forEach(lenis => lenis.destroy())}}, [])return (<div className="tabs">{/* 导航与内容结构同上 */}</div>)
}

 

通过这种实现方式,每个选项卡将拥有:
✅ 独立的滚动实例
✅ 平滑滚动效果
✅ 滚动位置记忆
✅ 自动高度适配
✅ 资源自动回收

可根据实际需求调整 MAX_CACHED 参数平衡内存占用与切换流畅度。

http://www.xdnf.cn/news/5610.html

相关文章:

  • LeetCode 373 查找和最小的 K 对数字题解
  • Git安装教程及常用命令
  • 【DeepSeek问答记录】请结合实例,讲解一下pytorch的DataLoader的使用方法
  • 11 配置Hadoop集群-免密登录
  • 一文读懂如何使用MCP创建服务器
  • ARMV8 RK3399 u-boot TPL启动流程分析 --crt0.S
  • 恰到好处TDR
  • SID310S/D/Q-10MHz, 低噪声, 轨至轨, CMOS 运算放大器
  • 二叉树路径总和
  • 10:00开始面试,10:08就出来了,问的问题有点变态。。。
  • wordcount在mapreduce的例子
  • 解读RTOS:第二篇 · 线程/任务管理与调度策略
  • WebGIS开发新突破:揭秘未来地理信息系统的神秘面纱
  • 回答 | 图形数据库neo4j社区版可以应用小型企业嘛?
  • 宇树科技安全漏洞揭示智能机器人行业隐忧
  • 视频翻译软件有哪些?推荐5款视频翻译工具[特殊字符][特殊字符]
  • 树莓派4 yolo 11l.pt性能优化后的版本
  • 摆脱拖延症的详细计划示例
  • Java根据文件名前缀自动分组图片文件
  • 社交APP如何借助游戏盾守护业务稳定
  • 配置Hadoop集群环境-使用脚本命令实现集群文件同步
  • React Native踩坑实录:解决NativeBase Radio组件在Android上的兼容性问题
  • Babel进阶:如何自定义插件?
  • 如何使用Launch4J将我们jar包变成一个可执行文件exe(依赖解压的jdk)
  • 常用的设计模式详解
  • BUUCTF 大流量分析(二) 1
  • Pycharm中No Conda enviroment selected
  • o.redisson.client.handler.CommandsQueue : Exception occured. Channel
  • 判断一个数是不是素数的最高效的算法
  • 在Fortran中输出类似Markdown的表格