评论
【继续作死】 20行代码 uni-app 小程序选项卡重复点击 执行列表左侧滑入动画

【继续作死】 20行代码 uni-app 小程序选项卡重复点击 执行列表左侧滑入动画

为了保护这些未成年的学生妹,校长裸身冲向了扫黄的警察

【继续作死】 20行代码 uni-app 小程序选项卡重复点击 执行列表左侧滑入动画

前面我们介绍了《前端噩梦,当选项卡在页面中间如何处理点击后闪屏》今天想在它 的基础上点击后有一个从左侧滑入的动画;滑入动画难度不大,我认为这个效果的难点在于点击不同选项卡后都要有一个滑入的动画,这才是难点;20行代码 uni-app 小程序选项卡重复点击 执行列表左侧滑入动画

预览

【继续作死】 20行代码 uni-app 小程序选项卡重复点击 执行列表左侧滑入动画

核心

为了适应大众和在任何地方都可以用,采用在列表外层加一个view来实现动画;isSlide控制动画是否显示

<!-- 列表2 -->

<view class="slide-container"  :class="{ 'active': isSlide }" >

    <list-grid :listDa="tabDas" :urls="urls"></list-grid>

</view>
/* 选项卡动画 */

.slide-container {

position: relative;

left: -400px;

opacity: 0;

transition: all 0.4s ease;

will-change: transform, opacity;/* 核心:优化和提示浏览器准备动画 */

}



.slide-container.active {

left: 0;

opacity: 1;/*核心*/

}



.slide-container:not(.active) {

transition: none;/*核心: 移除时没有动画,立即消失 */

}

案例

<template>

<view class="container">

<!-- 顶部红色背景容器 -->

<view class="top-container">

<text class="top-title">测试案例</text>

</view>



<!-- 选项卡 -->

<view class="tabs">

<view :class="currentTab == 0 ? 'active tab-item' : 'tab-item'" @click="switchTab(0)">

				文章

</view>

<view :class="currentTab == 1 ? 'active tab-item' : 'tab-item'" @click="switchTab(1)">

				新闻

</view>

</view>



<!-- 内容区域 -->

<view class="content">

<!-- slide-container 动画层 -->

<view class="slide-container" :class="{ 'active': isSlide }">

<view class="news-list">

<view class="news-item" v-for="(item, index) in newsList" :key="index">

<image class="news-img" :src="item.imgUrl"></image>

<view class="news-info">

<text class="news-title">{{item.title}}</text>

<text class="news-desc">{{item.desc}}</text>

<text class="news-time">{{item.time}}</text>

</view>

</view>

</view>

</view>



<!-- 占位符 -->

<view v-if="tapHeight" style="height: 800px;"></view>

</view>

</view>

</template>



<script>

export default {

data() {

return {

isSlide: false, // 滑动动画



tapHeight: false,

currentTab: 0, // 当前选中的选项卡

newsList: [], // 新闻列表数据

page: 1, // 当前页码

pageSize: 10, // 每页数量

// 模拟数据

mockData: [{

title: "这是一篇文章标题",

desc: "这是文章的简要描述内容,一般会显示前几句...",

time: "2025-05-10",

imgUrl: "https://picsum.photos/200/150?random=1"

					},

					{

title: "科技创新引领未来发展",

desc: "随着科技的不断进步,人工智能、大数据等新技术正在改变我们的生活...",

time: "2025-05-09",

imgUrl: "https://picsum.photos/200/150?random=2"

					},

					{

title: "健康生活小常识",

desc: "合理饮食、适量运动是保持健康的关键,这里有一些实用的健康小知识...",

time: "2025-05-08",

imgUrl: "https://picsum.photos/200/150?random=3"

					},

					{

title: "全球经济形势分析",

desc: "当前全球经济面临诸多挑战,贸易摩擦、通货膨胀等问题影响着各国经济...",

time: "2025-05-07",

imgUrl: "https://picsum.photos/200/150?random=4"

					},

					{

title: "教育改革新动态",

desc: "教育部门近日发布了一系列教育改革措施,旨在提高教育质量...",

time: "2025-05-06",

imgUrl: "https://picsum.photos/200/150?random=5"

					},

					{

title: "环保行动,从我做起",

desc: "环境污染问题日益严重,每个人都应该为环保出一份力...",

time: "2025-05-05",

imgUrl: "https://picsum.photos/200/150?random=6"

					}

				]

			}

		},

onLoad() {

// 页面加载时初始化数据

this.loadData();

		},

methods: {

// 切换选项卡

switchTab(index) {

this.tapHeight = true; //显示占位符

this.isSlide = false;// 滑动动画

this.currentTab = index;

this.newsList = [];

this.loadData();

			},



// 加载数据

loadData() {

// 模拟网络请求延迟

setTimeout(() => {

// 生成30条模拟数据

let mockType = this.currentTab === 0 ? '文章' : '新闻';

let newData = [];



for (let i = 0; i < this.pageSize; i++) {

// 随机选择一条基础数据进行修改

let randomIndex = Math.floor(Math.random() * this.mockData.length);

let item = JSON.parse(JSON.stringify(this.mockData[randomIndex]));



// 修改标题以区分文章和新闻

						item.title = `${mockType} ${this.page * this.pageSize + i + 1}: ${item.title}`;

						newData.push(item);



this.tapHeight = false; //关闭占位符

					}



this.newsList = [...this.newsList, ...newData];

this.page++;



this.isSlide = true;// 滑动动画



// 判断是否还有更多数据

if (this.newsList.length >= 30) {

this.hasMore = false;

					}

				}, 500);

			},





		}

	}

</script>



<style>

/* 选项卡动画 */

.slide-container {

position: relative;

left: -400px;

opacity: 0;

transition: all 0.4s ease;

will-change: transform, opacity;

/* 核心:优化和提示浏览器准备动画 */



	}



.slide-container.active {

left: 0;

opacity: 1;

/*核心*/

	}



.slide-container:not(.active) {

transition: none;

/*核心: 移除时没有动画,立即消失 */

	}



/* 其他样式 */

.container {

margin: 28rpx;

	}



.top-container {

height: 400rpx;

background-color: #ff0000;

display: flex;

align-items: center;

justify-content: center;

margin-bottom: 28rpx;

	}



.top-title {

color: #ffffff;

font-size: 48rpx;

font-weight: bold;

	}



.tabs {

display: flex;

height: 88rpx;

line-height: 88rpx;

background-color: #ffffff;

border-bottom: 1rpx solid #eeeeee;

	}



.tab-item {

flex: 1;

text-align: center;

font-size: 32rpx;

color: #333333;

	}



.active {

color: #ffffff;

background-color: #000000;

font-weight: bold;

	}



.content {

padding: 20rpx;

	}



.loading {

height: 100rpx;

display: flex;

align-items: center;

justify-content: center;

color: #999999;

font-size: 28rpx;

	}



.news-list {

background-color: #ffffff;

border-radius: 12rpx;

	}



.news-item {

display: flex;

padding: 24rpx;

border-bottom: 1rpx solid #f5f5f5;

	}



.news-img {

width: 200rpx;

height: 150rpx;

border-radius: 8rpx;

object-fit: cover;

	}



.news-info {

flex: 1;

margin-left: 20rpx;

display: flex;

flex-direction: column;

justify-content: space-between;

	}



.news-title {

font-size: 32rpx;

color: #333333;

font-weight: 500;

line-height: 40rpx;

display: -webkit-box;

		-webkit-box-orient: vertical;

		-webkit-line-clamp: 2;

overflow: hidden;

	}



.news-desc {

font-size: 28rpx;

color: #666666;

line-height: 36rpx;

display: -webkit-box;

		-webkit-box-orient: vertical;

		-webkit-line-clamp: 1;

overflow: hidden;

margin-top: 8rpx;

	}



.news-time {

font-size: 24rpx;

color: #999999;

margin-top: 8rpx;

	}



.load-more {

height: 88rpx;

line-height: 88rpx;

text-align: center;

color: #666666;

font-size: 28rpx;

	}



	page {

background-color: #f8f8f8;

	}

</style>