vue3瀑布流示例,左侧菜单根据窗口滚动条进行固定和取消固定,实现瀑布流demo

瀑布流demo的实现效果:

效果说明:

1.使用vue3实现瀑布流效果;

2.瀑布流横向设置5等分,可根据个人需求调整;

3.左侧菜单可根据右侧滚动条滑动时进行固定和取消固定,实现更优的展示效果;

4.瀑布流中的图片可使用img标签,也可使用背景图片(代码中注释的部分已标名)

实现方式:

一、创建瀑布流子组件

1.新建瀑布流组件的vue文件,如命名为WaterFall.vue

2.构建瀑布流中展示的内容,可根据不同需求进行自定义

<template>
	<div class="list">
		<div
			class="item"
			v-for="(item, index) in waterList"
			:key="index"
			:style="{
				width: width + 'px',
				height: item.height + 'px',
				left: item.left + 'px',
				top: item.top + 'px',
				// 'background-image': 'url(' + item.image + ')', // 假设 item 对象中有 image 属性
				// 'background-size': 'cover', // 如果需要的话,设置背景图片大小
				// 'background-position': 'center', // 如果需要的话,设置背景图片位置
				// 'background-repeat': 'no-repeat', // 禁止背景图片重复
			}"
		>
			<img :src="item.image" :alt="item.text" />
			<p class="text-box">{{ item.text }}</p>
		</div>
	</div>
</template>

3.构建瀑布流展示的相关方法

这里需要说明的是:为增加该瀑布流的可复用性,瀑布流数据通过父组件的list进行传递,boxWidth是指盛放瀑布流的盒子宽度,也是通过父组件进行传递,也可个人自己定义。父组件在下面也会给出详细代码。

这里将每个瀑布流里面的盒子设置的间距为右边20,下边20,第一行的数据进行了特殊处理,可根据个人需求调整。

<script setup>
import { ref, reactive, onMounted } from 'vue';
const props = defineProps({
	list: {
		type: Array,
		default: () => {
			return [];
		},
	},
	boxWidth: {
		type: String,
		default: () => {
			return '';
		},
	},
});

const width = 226; // 图片宽度
const gap = 20; // 图片上下间距
const rightGap = 20; // 图片右间距
const waterList = ref([]); // 瀑布流数组
const heightList = reactive([]); // 列高度数组

// 屏幕宽度需要在 mounted 之后拿到
onBeforeUpdate(() => {
	// 计算列数
	console.log('组件里获取的宽度', props.boxWidth);
	const column = Math.floor(props.boxWidth / width); // 根据盒子的宽度确定列数
	console.log('计算的列数', column);
	// 核心内容就是维护每个图片的 left、top
	for (let i = 0; i < props.list.length; i++) {
		// 先铺上第一行(i < column 则表示是第一行)
		if (i < column) {
			props.list[i].top = 0;
			props.list[i].left = i * (width + (i > 0 ? rightGap : 0));
			// 塞进瀑布流
			waterList.value?.push(props.list[i]);
			// 高度数据更新
			heightList[i] = props.list[i].height;
		}
		// 后面的就要一张张塞进去,每次找出最低的列往里塞
		else {
			// 最低的高度,先默认为第一列高度
			let current = heightList[0];
			// 最低的列,先默认为第一个
			let col = 0;

			// 循环每一列进行比较
			heightList.forEach((h, i) => {
				if (h < current) {
					current = h;
					col = i;
				}
			});
			console.log('最低的列', col, '高度为', current);

			// 由此计算出该图片的 left、top
			props.list[i].left = col * (width + rightGap);
			props.list[i].top = current + gap;
			// 塞进瀑布流
			waterList.value.push(props.list[i]);

			// 更新列高度数组
			heightList[col] = current + gap + props.list[i].height;
		}
	}
	console.log('waterList', waterList.value);
	console.log('heightList', heightList);
});
</script>

4.设置瀑布流的相关样式

可根据个人需求进行调整,这里展示的是上方效果图的样式

<style lang="scss" scoped>
.list {
	position: relative;
	height: 100%;
	width: 100%;
	.item {
		position: absolute;
		.text-box {
			font-weight: 500;
			font-size: 18px;
			color: #000000;
		}
		img {
            width: 100%;
            height: 90%;
			object-fit: cover;
		}
	}
}
</style>

二、创建使用瀑布流的父组件

1.新建父组件,如命名为index.vue

2.创建父组件的展示内容,分为左侧的菜单和右侧的瀑布流内容

isMenuBarFixed为控制左侧菜单根据右侧滚动条进行滑动的属性,menu-bar里面的内容为左侧菜单的内容,works-box为右侧瀑布流的内容,中间的<div style="width: 160px" v-if="isMenuBarFixed"></div>为当左侧菜单脱离文档流的占位内容,关于脱离文档流,想要学习的同学可参考这篇博客:CSS标准文档流和脱离文档流_css脱离文档流-CSDN博客

<template>
	<div class="max-content">
		<div class="main-content">
			<div class="works-publicity">
				<nav class="menu-bar" ref="menuBar" :class="{ fixed: isMenuBarFixed }">
					<ul style="padding: 0">
						<li
							v-for="(item, index) in menuItems"
							:key="index"
							class="menu-item"
							@click="setActiveItem(index)"
							:class="{ active: activeIndex === index }"
						>
							<div class="menu-item-content">
								<div
									class="menu-item-border"
									:class="{ blue: activeIndex === index }"
								></div>
								<img
									:src="item.isActive ? item.activeImage : item.image"
									class="menu-icon"
									alt=""
								/>
								<div class="menu-text" :class="{ blue: activeIndex === index }">
									{{ item.text }}
								</div>
							</div>
						</li>
					</ul>
				</nav>
				<div style="width: 160px" v-if="isMenuBarFixed"></div>
				<div class="works-box" ref="myDiv">
					<water-fall :list="list" :boxWidth="boxWidth" class="water-fall" />
				</div>
			</div>
		</div>
	</div>
</template>

3.父组件的相关方法

控制左侧瀑布流菜单根据右侧滚动条固定的相关关键代码可参考本篇博客的缩略版使用vue3实现右侧瀑布流滑动时左侧菜单的固定与取消固定-CSDN博客

<script setup>
import { cloneDeep } from 'lodash-es';
const config = useRuntimeConfig();

const myDiv = ref(null);
const menuBar = ref(null);
const isMenuBarFixed = ref(false);
const boxWidth = ref(''); // 展示瀑布流的盒子的宽度
const menuItems = ref([
	{
		text: '第一个',
		image: '/images/works-publicity/industrial-design.png',
		activeImage: '/images/works-publicity/industrial-design-active.png',
		isActive: true,
	},
	{
		text: '第二个',
		image: '/images/works-publicity/handicrafts.png',
		activeImage: '/images/works-publicity/handicrafts-active.png',
		isActive: false,
	},
	{
		text: '第三个',
		image: '/images/works-publicity/painting.png',
		activeImage: '/images/works-publicity/painting-active.png',
		isActive: false,
	},
	{
		text: '第四个',
		image: '/images/works-publicity/photograph.png',
		activeImage: '/images/works-publicity/photograph-active.png',
		isActive: false,
	},
	{
		text: '第五个',
		image: '/images/works-publicity/geography.png',
		activeImage: '/images/works-publicity/geography-active.png',
		isActive: false,
	},
	{
		text: '第六个',
		image: '/images/works-publicity/tradition.png',
		activeImage: '/images/works-publicity/tradition-active.png',
		isActive: false,
	},
]);
const list = [
	{
		height: 450,
		background: 'red',
		image: '/images/works-publicity/test1.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 600,
		background: 'pink',
		text: '美术作品+沈佳宜',
		image: '/images/works-publicity/test2.png',
	},
	{
		height: 450,
		background: 'blue',
		image: '/images/works-publicity/test3.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 350,
		background: 'green',
		image: '/images/works-publicity/test4.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 500,
		background: 'gray',
		image: '/images/works-publicity/test5.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 400,
		background: '#CC00FF',
		image: '/images/works-publicity/test6.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 300,
		background: 'pink',
		image: '/images/works-publicity/test7.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 600,
		background: '#996666',
		image: '/images/works-publicity/test8.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 400,
		background: 'gray',
		image: '/images/works-publicity/test9.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 400,
		background: '#CC00FF',
		image: '/images/works-publicity/test10.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 500,
		background: 'gray',
		image: '/images/works-publicity/test11.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 300,
		background: '#996666',
		image: '/images/works-publicity/test12.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 600,
		background: 'gray',
		image: '/images/works-publicity/test13.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 400,
		background: '#CC00FF',
		image: '/images/works-publicity/test1.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 500,
		background: 'gray',
		image: '/images/works-publicity/test2.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 300,
		background: '#996666',
		image: '/images/works-publicity/test3.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 300,
		background: 'gray',
		image: '/images/works-publicity/test4.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 400,
		background: '#CC00FF',
		image: '/images/works-publicity/test5.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 500,
		background: 'gray',
		image: '/images/works-publicity/test6.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 400,
		background: '#996666',
		image: '/images/works-publicity/test7.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 350,
		background: 'gray',
		image: '/images/works-publicity/test8.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 450,
		background: '#CC00FF',
		image: '/images/works-publicity/test9.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 430,
		background: 'gray',
		image: '/images/works-publicity/test10.png',
		text: '平面作品+李宇轩',
	},
	{
		height: 600,
		background: '#996666',
		image: '/images/works-publicity/test11.png',
		text: '平面作品+李宇轩',
	},
];
const activeIndex = ref(0);
// 点击左侧菜单栏切换内容
const setActiveItem = (num) => {
	menuItems.value.forEach((item, idx) => {
		item.isActive = num === idx;
	});
	activeIndex.value = num;
};
// 监听滚动事件
const handleScroll = () => {
	const scrollTopThreshold = 428;
	const scrollTop = document.body.scrollTop; // 获取滚动位置
	if (scrollTop >= scrollTopThreshold) {
		isMenuBarFixed.value = true;
	} else {
		isMenuBarFixed.value = false;
	}
};


onMounted(async () => {
	await nextTick(); // 等待 DOM 更新
	boxWidth.value = myDiv.value.offsetWidth; // 确保 myDiv 已经被定义并且 DOM 已经渲染
	document.body.addEventListener('scroll', handleScroll); // 添加滚动事件监听器
});
// 组件卸载前
onUnmounted(() => {
	document.body.removeEventListener('scroll', handleScroll()); // 移除滚动事件监听器
});
</script>

4.父组件内容的相关样式

<style lang="scss" scoped>
.works-publicity {
	display: flex;
	width: 100%;
	padding: 20px 0;
	box-sizing: border-box;
	.menu-bar,
	.fixed {
		position: relative; /* 或者 static,取决于你的布局 */
		top: 0; /* 确保它在页面顶部开始 */
		transition: top 0.3s ease; /* 可选的过渡效果 */
		display: flex;
		justify-content: space-around; /* 根据需要调整菜单项间距 */
		list-style: none;
		padding: 0;
		.menu-item {
			display: flex;
			position: relative;
			align-items: center; /* 垂直居中图片和文字 */
			justify-content: center;
			cursor: pointer; /* 鼠标悬停时改变样式(可选) */
			width: 160px;
			height: 136px;
			text-align: center;
			border-left: 1px solid #d3dde9;
		}
		.menu-icon {
			width: 32px; /* 根据图标大小调整 */
			height: 32px; /* 根据图标大小调整 */
			margin-bottom: 20px; /* 图片和文字之间的间距 */
		}
		.menu-item-content {
			display: flex;
			align-items: center;
			flex-direction: column;
		}

		.menu-item-border {
			position: absolute;
			left: 0; /* 左边框位置 */
			top: 0; /* 可以根据需要调整 */
			bottom: 0; /* 可以根据需要调整 */
			width: 3px; /* 边框宽度 */
			background-color: transparent; /* 默认透明 */
		}

		.menu-item-border.blue {
			background-color: var(--el-color-primary); /* 激活时变为蓝色 */
		}
		.menu-text.blue {
			color: var(--el-color-primary); /* 激活时文字变为蓝色 */
		}
	}
	.fixed {
		/* 当固定时 */
		position: fixed;
		top: 0;
		width: 160px; /* 或者你需要的宽度 */
		z-index: 100; /* 确保它在其他内容之上 */
		/* 其他样式,可能需要调整以适应固定定位 */
	}
	.works-box {
		display: flex;
		flex-direction: column;
		width: 100%;
		.water-fall {
			margin-top: 30px;
		}
	}
}
</style>

这样,关于瀑布流的展示及滚动条滑动时,左侧菜单的灵活固定就完成啦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/644506.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Django 里html模板

Django 提供两种方式让程序员自定义html模板。 第一种方法 在项目文件夹里的urls.py进行添加 修改代码如下 from django.contrib import admin from django.urls import path from app01 import views # 得添加这行urlpatterns [path(xxx/, views.home), # 添加这行path(…

从0开始学统计-你能区分率和构成比吗?

1.数据的变异度如何描述&#xff1f; 数据的变异度描述了数据集中数值之间的差异或波动程度。常用的描述数据变异度的统计量包括&#xff1a; &#xff08;1&#xff09;范围&#xff08;Range&#xff09;&#xff1a;范围是数据集中最大值与最小值之间的差异&#xff0c;表…

NDIS小端口驱动(九)

PCIe设备难免会遇到一些重置设备的请求&#xff0c;例如重置总线的时候&#xff0c;但是由于NIC网卡的多样性&#xff0c;重置设备确实也有许多要注意的地方&#xff0c;另外还有一些包含WDM的NDIS驱动 微型端口驱动程序硬件重置 微型端口驱动程序必须向 NdisMRegisterMinipo…

重新思考:Netflix 的边缘负载均衡

声明 本文是对Netflix 博客的翻译 前言 ​ 在先前关于Zuul 2开源的文章中&#xff0c;我们简要概述了近期在负载均衡方面的一些工作。在这篇文章中&#xff0c;我们将更详细地介绍这项工作的原因、方法和结果。 ​ 因此&#xff0c;我们开始从Zuul和其他团队那里学习&#…

L01_JVM 高频知识图谱

这些知识点你都掌握了吗&#xff1f;大家可以对着问题看下自己掌握程度如何&#xff1f;对于没掌握的知识点&#xff0c;大家自行网上搜索&#xff0c;都会有对应答案&#xff0c;本文不做知识点详细说明&#xff0c;只做简要文字或图示引导。 类的生命周期 类加载器 JVM 的内存…

CAD二次开发(2)-将直线对象添加到CAD图形文件

1. 准备工作 创建一个类库项目&#xff0c;如下&#xff1a; 2. 分析Line对象 Line类的初始化方法和参数 using Autodesk.AutoCAD.DatabaseServices; Line line new Line();Line 继承Curve 继承Entity 继承DBObject 继承Drawable 继承RXObject 初始化方法有两个&#xf…

2024年汉字小达人活动4个多月开赛:18道历年选择题和答案、解析

根据近年的安排&#xff0c;2024年第11届汉字小达人比赛还有4个多月就启动&#xff0c;那么孩子们如何利用这段时间有条不紊地备考呢&#xff1f;我的建议是两手准备&#xff1a;①把小学1-5年级的语文课本上的知识点熟悉&#xff0c;重点是字、词、成语、古诗。②把历年真题刷…

求两个整数最大公约数的方法

可以使用递归来实现&#xff0c;编写gcd函数返回最终的结果(最大公约数)。传入两个参数&#xff0c;如果存在一个数字不大于0就返回0&#xff0c;利用上面的公式就可以得出最后的结果。

电脑远程控制另一台电脑怎么弄?

可以远程控制另一台电脑吗&#xff1f; “你好&#xff0c;我对远程访问技术不太了解。现在&#xff0c;我希望我的朋友可以远程控制我的Windows 10电脑&#xff0c;以便她能帮我解决一些问题。请问&#xff0c;有没有免费的方法可以实现这种远程控制&#xff1f;我该如何操作…

机器学习大模型驱动:未来的趋势与应用

文章目录 &#x1f4d1;前言一、什么是机器学习大模型&#xff1f;1.1 大模型的特点1.2 大模型的技术基础 二、大模型的技术实现2.1 Transformer 架构2.2 预训练和微调2.3 模型并行和数据并行 三、大模型的应用场景3.1 自然语言处理&#xff08;NLP&#xff09;3.2 计算机视觉&…

SpringMVC源码解读[1] -Spring MVC 环境搭建

源码地址: https://github.com/chen-jiacheng/springmvc-quickstart 一、使用 IDEA 创建 Spring MVC 项目 直接创建项目即可 默认项目结构: springmvc-quickstart ├── pom.xml └── src├── main│ ├── java│ │ └── com│ │ └── chenjiache…

2024Spring> HNU-计算机系统-实验4-Buflab-导引+验收

前言 称不上导引了&#xff0c;因为验收已经结束了。主要是最近比较忙&#xff0c;在准备期末考试。周五晚上才开始看实验&#xff0c;自己跟着做了一遍实验&#xff0c;感觉难度还是比bomblab要低的&#xff0c;但是如果用心做的话对于栈帧的理解确实能上几个档次。 实验参考…

(南京观海微电子)——TFT LCM的作用

VCOM介绍 VCOM是液晶分子偏转的参考电压 &#xff0c;要求要稳定&#xff0c;对液晶显示有直接影响&#xff0c;具体的屏不同的话 也是不同的。 电压的具体值是根据输入的数据以及Vcom电压大小来确定的&#xff0c;用来显示各种不同灰阶&#xff0c;也就是实现彩色显示GAMMA简…

《计算机网络微课堂》3-11 虚拟局域网 VLAN

本节课我们介绍虚拟局域网 VLAN 的基本概念。 ‍ 3.11.1 虚拟局域网 VLAN 概述 在之前课程中我们已经介绍过了以太网交换机自学习和转发帧的流程&#xff0c;‍‍以及为避免网络环路而产生的生成树协议。 以太网交换机工作在数据链路层&#xff0c;‍‍也包括物理层&#xf…

搜索自动补全-elasticsearch实现

1. elasticsearch准备 1.1 拼音分词器 github地址&#xff1a;https://github.com/infinilabs/analysis-pinyin/releases?page6 必须与elasticsearch的版本相同 第四步&#xff0c;重启es docker restart es1.2 定义索引库 PUT /app_info_article {"settings": …

vim操作手册

vim分为插入模式、命令模式、底行模式。 插入模式&#xff1a;编辑模式 命令模式&#xff1a;允许使用者通过命令&#xff0c;来进行文本的编辑控制 底行模式&#xff1a;用来进行让vim进行包括但不限于shell进行交互 w&#xff1a;保存 wq&am…

AI Agent: Agent框架+7个实例

何谓Agent Agent 作为一种新兴的人工智能技术&#xff0c;正在受到越来越多的关注。要说清楚什么是 Agent&#xff0c;先得看看人工智能的本质是什么。 人工智能这个名称来自它试图通过计算机程序或机器来模拟、扩展和增强人类智能的 一些方面。在这个定义中&#xff0c;“人…

Java进阶学习笔记20——枚举

认识枚举&#xff1a; 枚举是一种特殊的类。 枚举类的格式&#xff1a; 说明&#xff1a; 第一行是罗列枚举的对象名称。只能写合法的标识符&#xff08;名称&#xff09;&#xff0c;多个名称用逗号隔开。 这些名称本质上都是常量&#xff0c;每个变量都会记住枚举类的一个…

z3-加法器实验

补码器加减法&#xff0c;运算方法简介 我们要知道什么是补码的加法&#xff0c;我们为什么要用补码的加法&#xff1f; 补码的加法其实就是将两个补码形式的二进制数字直接相加&#xff0c;处理的时候忽略超出固定位数的进位。补码的加法运算和无符号二进制数的加法操作一样&…