vue3使用插槽写一个自定义瀑布列表
效果如下:
组件
<!-- 注意:如果要使用这个的话就需要把 父组件里面的高度去掉,就是img标签里的高度: <img :src="item.imgUrl" :alt="item.text" class="item-img" :style="{height:item.height+'px'}"/> 把style去掉<template><div class="waterfall-container"><divv-for="(item, index) in props.items":key="index"class="waterfall-item"><slot :item="item"></slot></div></div>
</template><script setup>
import { ref } from "vue";
const props = defineProps({items: {type: Array,default: [],},
});
</script>
<style scoped>
.waterfall-container {display: grid;grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); /* 自动填充列 */gap: 10px; /* 项目间隔 */padding: 10px;
}.waterfall-item {background-color: #fff;border-radius: 8px;overflow: hidden;box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);display: flex;flex-direction: column;
}
</style> --><template><div class="waterfall-container"><divv-for="(item, index) in props.items":key="index"class="waterfall-item":style="getRandomShadow()"><div class="border-container"><slot :item="item"></slot></div></div></div>
</template><script setup>
import { ref } from "vue";
const props = defineProps({items: {type: Array,default: [],},
});function getRandomShadow() {const hue = Math.floor(Math.random() * 360);const saturation = Math.floor(Math.random() * 30) + 70;const lightness = Math.floor(Math.random() * 20) + 60;const alpha = 0.3 + Math.random() * 0.3;const color = `hsla(${hue}, ${saturation}%, ${lightness}%, ${alpha})`;return {boxShadow: `0 4px 6px ${color}`,};
}
</script><style scoped>
.waterfall-container {column-count: auto;column-width: 250px;gap: 10px;padding: 10px;
}.waterfall-item {background-color: #fff;border-radius: 10px;overflow: hidden;margin-bottom: 15px;break-inside: avoid;display: inline-block;width: 100%;transition: transform 0.3s ease, box-shadow 0.3s ease; /*定义了变换和阴影的过渡动画*/transform: translateY(0); /*设置初始位置*/will-change: transform, box-shadow; /*提示浏览器这些属性可能会变化,优化性能*/position: relative;/* 为边框留出空间 */padding: 2px;
}/* 边框容器样式 */
.border-container {position: relative;z-index: 1;padding: 16px;border-radius: 8px;background: #fff;height: 100%;
}/* 边框流动效果 - 修改实现方式以确保兼容性 */
.waterfall-item::after {content: "";position: absolute;top: 0;left: 0;right: 0;bottom: 0;border-radius: 10px;background: linear-gradient(90deg,#4facfe 0%,#00f2fe 25%,#84fab0 50%,#8fd3f4 75%,#4facfe 100%);background-size: 400% 100%;z-index: 0;opacity: 0;transition: opacity 0.3s ease;animation: borderFlow 4s linear infinite;
}/* 鼠标悬停时的效果 */
.waterfall-item:hover {/* 轻微上浮 */transform: translateY(-5px);/* 增强阴影效果 */box-shadow: 0 3px 6px #b3f5c3c0 !important;/* 提升层级,确保凸起效果明显 */z-index: 10;
}/* 鼠标悬停时显示边框动画 */
.waterfall-item:hover::after {opacity: 1;
}/* 边框流动动画 */
@keyframes borderFlow {0% {background-position: 0% 0%;}100% {background-position: 400% 0%;}
}
</style>
组件使用
<template><div class="homes GunDongTiao"><div class="GunDongTiao" style="width: 100%; height: 700px; overflow: auto"><pubu :items="lists" ><template #default="{ item }"><div class="hoverdiv"><img:src="item.imgUrl":alt="item.text"class="item-img":style="{ height: item.height + 'px' }"/><p class="item-text">{{ item.text }}--高度:{{ item.height }}</p></div></template></pubu></div><!-- <div class="GunDongTiao display_width"><div class="display_ziyuansu" v-for="item in 100" :key="item">{{ item }}爱上了和的那份拉萨三大</div></div> --></div>
</template><script setup>
import { ref, onMounted } from "vue";
import pubu from "./pubu.vue";const lists = ref([]);
function getRandomFromArray(...args) {// 检查是否有传入参数if (args.length === 0) {return undefined; // 或抛出错误:throw new Error("请至少传入一个参数");}// 生成随机索引(0 到 参数数量-1 之间)const randomIndex = Math.floor(Math.random() * args.length);// 返回随机选中的参数return args[randomIndex];
}function FN_lists() {let list = [];for (let i = 0; i < 1000; i++) {// https://img2.baidu.com/it/u=1083342850,1962599537&fm=253&app=138&f=JPEG?w=800&h=1129// https://i.bobopic.com/small/85034916.jpg// ttps://b0.bdstatic.com/1fa501a96cecc59b336e0a208a82cc95.jpglist.push({height: getRandomFromArray(100, 150, 195, 200, 350, 400, 630),imgUrl: getRandomFromArray(`https://i.bobopic.com/small/85034916.jpg`,`https://b0.bdstatic.com/1fa501a96cecc59b336e0a208a82cc95.jpg`,`https://img2.baidu.com/it/u=1083342850,1962599537&fm=253&app=138&f=JPEG?w=800&h=1129`,"https://img0.baidu.com/it/u=1098659514,4222641721&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=800",'https://img1.baidu.com/it/u=1363165673,1495198767&fm=253&app=138&f=JPEG?w=800&h=1684','https://pic.rmb.bdstatic.com/bjh/241202/dump/3a3c22dfa42dc54549afe91070056862.png','https://img0.baidu.com/it/u=3135630413,3847935609&fm=253&app=138&f=JPEG?w=800&h=1200','https://img1.baidu.com/it/u=889768745,998862614&fm=253&app=138&f=JPEG?w=800&h=1372','https://img2.baidu.com/it/u=1622597608,1377744426&fm=253&app=138&f=JPEG?w=800&h=1422','https://img1.baidu.com/it/u=1791741031,1284315034&fm=253&app=138&f=JPEG?w=800&h=1199','https://pic.rmb.bdstatic.com/bjh/video/efaacfa961497e635fad1cf9bffabfe3.jpeg?for=bg','https://qcloud.dpfile.com/pc/JYEc5AfZmYfDw2k2rh2SOmEF4OvL5kiNnyVtnsI-p46wy43j2YldXkWVhVrI7Ign.jpg','https://inews.gtimg.com/om_bt/OL3l3PFwZsaPuLsg2GWtQW-Uslu8epudPAYI5WLP5uzFsAA/641','https://p3-pc-sign.douyinpic.com/tos-cn-i-0813c001/ooDrAMEfQIsVoIEc7f2bDflCse1YtAQ0AGAAFi~tplv-dy-aweme-images:q75.webp?biz_tag=aweme_images&from=327834062&lk3s=138a59ce&s=PackSourceEnum_PUBLISH&sc=image&se=false&x-expires=1757570400&x-signature=yiT92FEoALP7a6FinmkvyCGDm2Q%3D'),height: getRandomFromArray(100, 150, 195, 200, 350, 400, 630),text: `Item ${i + 1}`,maxWidth: getRandomFromArray(180, 220, 250, 280),});}return list;
}
onMounted(() => {lists.value = FN_lists();
});
</script><style scoped>
.homes {width: 100vw;height: 90vh;overflow: auto;
}.item-img {width: 100%;height: auto;object-fit: cover;
}.item-text {padding: 10px;font-size: 14px;color: #333;
}/* 鼠标移入时的效果 */
.hoverdiv:hover .item-img {transform: scale(1.1);
}/* 初始状态样式 */
.hoverdiv .item-text {color: #333;font-weight: 400;transition: none; /* 避免过渡与动画冲突 */
}/* 鼠标移入时,为目标子元素应用动画 */
.hoverdiv:hover .item-text {animation: hoverIn 0.9s ease forwards;
}/* 鼠标移出时,为目标子元素应用反向动画 */
.hoverdiv:not(:hover) .item-text {animation: hoverOut 0.9s ease forwards;
}/* 移入动画:先移动 → 再变粗 → 最后变色 */
@keyframes hoverIn {0% {font-weight: 400;color: #333;}33% {font-weight: 400;color: #4eeaf5;}66% {font-weight: 600; /* 完成变粗 */color: #f54e4e;}100% {font-weight: 600;color: #f88bb5; /* 完成变色 */text-align: center;}
}/* 移出动画:先恢复颜色 → 再恢复字重 → 最后恢复位置 */
@keyframes hoverOut {0% {font-weight: 600;color: #f88bb5;}33% {font-weight: 600;color: #f54e4e; /* 完成颜色恢复 */}66% {font-weight: 400; /* 完成字重恢复 */color: #4eeaf5;}100% {font-weight: 400;color: #333;text-align: left;}
}/* display布局的时候子元素没有固定的宽高的情况下居中 */
.display_width {width: 100%;height: 100px;overflow: auto;border: 1px solid #36d;box-sizing: border-box;padding: 0 10px;column-count: auto;column-width: 150px;display: flex;flex-flow: row;flex-direction: row;align-items: center;flex-wrap: nowrap;gap: 10px;
}
.display_ziyuansu {height: 60px;border: 1px solid #36d;box-sizing: border-box;padding: 10px 20px;flex-grow: 0;flex-shrink: 0;line-height: 40px;
}/* 滚动条样式 */
.GunDongTiao::-webkit-scrollbar {width: 5px;height: 5px;
}.GunDongTiao::-webkit-scrollbar-track {background: #f1f1f1;border-radius: 10px;
}.GunDongTiao::-webkit-scrollbar-thumb {background: #888;border-radius: 10px;
}.GunDongTiao::-webkit-scrollbar-thumb:hover {background: #8ac4e6;
}
</style>
这个是添加了一些其它效果的基本样式
子组件
<template><div class="waterfall-container"><divv-for="(item, index) in props.items":key="index"class="waterfall-item":style="getRandomShadow()"><div class="border-container"><slot :item="item"></slot></div></div></div>
</template><script setup>
import { ref } from "vue";
const props = defineProps({items: {type: Array,default: [],},
});function getRandomShadow() {const hue = Math.floor(Math.random() * 360);const saturation = Math.floor(Math.random() * 30) + 70;const lightness = Math.floor(Math.random() * 20) + 60;const alpha = 0.3 + Math.random() * 0.3;const color = `hsla(${hue}, ${saturation}%, ${lightness}%, ${alpha})`;return {boxShadow: `0 4px 6px ${color}`,};
}
</script><style scoped>
.waterfall-container {column-count: auto;column-width: 250px;gap: 10px;padding: 10px;
}.waterfall-item {background-color: #fff;border-radius: 10px;overflow: hidden;margin-bottom: 15px;break-inside: avoid;display: inline-block;width: 100%;transition: transform 0.3s ease, box-shadow 0.3s ease; /*定义了变换和阴影的过渡动画*/transform: translateY(0); /*设置初始位置*/will-change: transform, box-shadow; /*提示浏览器这些属性可能会变化,优化性能*/position: relative;/* 为边框留出空间 */padding: 2px;
}/* 边框容器样式 */
.border-container {position: relative;z-index: 1;padding: 6px;border-radius: 8px;background: #fff;height: 100%;
}/* 边框流动效果 - 修改实现方式以确保兼容性 */
.waterfall-item::after {content: "";position: absolute;top: 0;left: 0;right: 0;bottom: 0;border-radius: 10px;background: linear-gradient(90deg,#4facfe 0%,#00f2fe 25%,#84fab0 50%,#8fd3f4 75%,#4facfe 100%);background-size: 400% 100%;z-index: 0;opacity: 0;transition: opacity 0.3s ease;animation: borderFlow 4s linear infinite;
}/* 鼠标悬停时的效果 */
.waterfall-item:hover {/* 轻微上浮 */transform: translateY(-5px);/* 增强阴影效果 */box-shadow: 0 3px 6px #b3f5c3c0 !important;/* 提升层级,确保凸起效果明显 */z-index: 10;
}/* 鼠标悬停时显示边框动画 */
.waterfall-item:hover::after {opacity: 1;
}/* 边框流动动画 */
@keyframes borderFlow {0% {background-position: 0% 0%;}100% {background-position: 400% 0%;}
}
</style>
两个不同的应用
<template><div class="homes GunDongTiao"><pubu :items="lists"><template #default="{ item }"><div class="hoverdiv"><!-- 占位容器 --><div class="placeholder-container" :style="{ height: item.height + 'px' }"><!-- 蒙版 --><div class="item-mask"></div><!-- 标题文字 --><div class="item-text"><span class="text-inner">{{ item.text }}</span></div></div></div></template></pubu></div>
</template><script setup>
import { ref, onMounted } from "vue";
import pubu from "./pubu.vue";const lists = ref([]);function getRandomFromArray(...args) {if (args.length === 0) return undefined;const randomIndex = Math.floor(Math.random() * args.length);return args[randomIndex];
}function generateLists() {let list = [];// 不再需要图片地址数组for (let i = 0; i < 14; i++) {list.push({height: getRandomFromArray(100, 150, 195, 200, 350, 400, 630),text: `内容块 ${i + 1}`,maxWidth: getRandomFromArray(180, 220, 250, 280),});}return list;
}onMounted(() => {lists.value = generateLists();
});
</script><style scoped>
.homes {width: 100vw;min-height: 100vh;padding: 20px;overflow: auto;background: #0f172a;box-sizing: border-box;
}.hoverdiv {position: relative;border-radius: 16px;overflow: hidden;cursor: pointer;
}/* 占位容器样式 */
.placeholder-container {width: 100%;background: #1e293b; /* 占位背景色 */border-radius: 16px;position: relative;
}/* 蒙版样式 */
.item-mask {position: absolute;inset: 0;background: linear-gradient(to top,rgba(15, 23, 42, 0.95),rgba(139, 92, 246, 0.7));opacity: 0;transition: opacity 0.4s ease;z-index: 1;
}/* 标题文字样式 */
.item-text {position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);padding: 15px;font-size: 18px;color: #ffffff;text-shadow: 0 0 10px rgba(255, 255, 255, 0.9),0 0 20px rgba(139, 92, 246, 0.8);white-space: nowrap;opacity: 0;z-index: 2;transition: opacity 0.3s ease 0.05s;
}
.text-inner::after {content: "";position: absolute;bottom: -5px;left: 0;width: 0;height: 2px;background: linear-gradient(90deg, #ff00ff, #00ffff);transition: width 0.3s ease 0.1s;
}/* 鼠标悬停效果 - 只保留蒙版和文字显示 */
.hoverdiv:hover .item-mask {opacity: 1;
}.hoverdiv:hover .item-text {opacity: 1;
}
.hoverdiv:hover .text-inner::after {width: 100%;
}
/* 滚动条样式保留 */
.GunDongTiao::-webkit-scrollbar {width: 6px;height: 6px;
}.GunDongTiao::-webkit-scrollbar-track {background: rgba(30, 41, 59, 0.5);border-radius: 10px;
}.GunDongTiao::-webkit-scrollbar-thumb {background: linear-gradient(180deg, #8b5cf6, #4f46e5);border-radius: 10px;transition: all 0.3s ease;
}.GunDongTiao::-webkit-scrollbar-thumb:hover {background: linear-gradient(180deg, #a78bfa, #6366f1);
}
</style>
<template><div class="homes GunDongTiao"><pubu :items="lists"><template #default="{ item }"><div class="hoverdiv"><img:src="item.imgUrl":alt="item.text"class="item-img":style="{ height: item.height + 'px' }"/><!-- 蒙版 --><div class="item-mask"></div><!-- 标题文字 --><div class="item-text">{{ item.text }}</div></div></template></pubu><!-- <div class="GunDongTiao display_width"><div class="display_ziyuansu" v-for="item in 100" :key="item">{{ item }}爱上了和的那份拉萨三大</div></div> --></div>
</template><script setup>
import { ref, onMounted } from "vue";
import pubu from "./pubu.vue";const lists = ref([]);
function getRandomFromArray(...args) {// 检查是否有传入参数if (args.length === 0) {return undefined; // 或抛出错误:throw new Error("请至少传入一个参数");}// 生成随机索引(0 到 参数数量-1 之间)const randomIndex = Math.floor(Math.random() * args.length);// 返回随机选中的参数return args[randomIndex];
}function FN_lists() {let list = [];const imgs = [`https://i.bobopic.com/small/85034916.jpg`,`https://b0.bdstatic.com/1fa501a96cecc59b336e0a208a82cc95.jpg`,`https://img2.baidu.com/it/u=1083342850,1962599537&fm=253&app=138&f=JPEG?w=800&h=1129`,"https://img0.baidu.com/it/u=1098659514,4222641721&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=800","https://img1.baidu.com/it/u=1363165673,1495198767&fm=253&app=138&f=JPEG?w=800&h=1684","https://pic.rmb.bdstatic.com/bjh/241202/dump/3a3c22dfa42dc54549afe91070056862.png","https://img0.baidu.com/it/u=3135630413,3847935609&fm=253&app=138&f=JPEG?w=800&h=1200","https://img1.baidu.com/it/u=889768745,998862614&fm=253&app=138&f=JPEG?w=800&h=1372","https://img2.baidu.com/it/u=1622597608,1377744426&fm=253&app=138&f=JPEG?w=800&h=1422","https://img1.baidu.com/it/u=1791741031,1284315034&fm=253&app=138&f=JPEG?w=800&h=1199","https://pic.rmb.bdstatic.com/bjh/video/efaacfa961497e635fad1cf9bffabfe3.jpeg?for=bg","https://qcloud.dpfile.com/pc/JYEc5AfZmYfDw2k2rh2SOmEF4OvL5kiNnyVtnsI-p46wy43j2YldXkWVhVrI7Ign.jpg","https://inews.gtimg.com/om_bt/OL3l3PFwZsaPuLsg2GWtQW-Uslu8epudPAYI5WLP5uzFsAA/641","https://p3-pc-sign.douyinpic.com/tos-cn-i-0813c001/ooDrAMEfQIsVoIEc7f2bDflCse1YtAQ0AGAAFi~tplv-dy-aweme-images:q75.webp?biz_tag=aweme_images&from=327834062&lk3s=138a59ce&s=PackSourceEnum_PUBLISH&sc=image&se=false&x-expires=1757570400&x-signature=yiT92FEoALP7a6FinmkvyCGDm2Q%3D"];for (let i = 0; i < 1000; i++) {// https://img2.baidu.com/it/u=1083342850,1962599537&fm=253&app=138&f=JPEG?w=800&h=1129// https://i.bobopic.com/small/85034916.jpg// ttps://b0.bdstatic.com/1fa501a96cecc59b336e0a208a82cc95.jpglist.push({height: getRandomFromArray(100, 150, 195, 200, 350, 400, 630),imgUrl: getRandomFromArray(`https://i.bobopic.com/small/85034916.jpg`,`https://b0.bdstatic.com/1fa501a96cecc59b336e0a208a82cc95.jpg`,`https://img2.baidu.com/it/u=1083342850,1962599537&fm=253&app=138&f=JPEG?w=800&h=1129`,"https://img0.baidu.com/it/u=1098659514,4222641721&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=800","https://img1.baidu.com/it/u=1363165673,1495198767&fm=253&app=138&f=JPEG?w=800&h=1684","https://pic.rmb.bdstatic.com/bjh/241202/dump/3a3c22dfa42dc54549afe91070056862.png","https://img0.baidu.com/it/u=3135630413,3847935609&fm=253&app=138&f=JPEG?w=800&h=1200","https://img1.baidu.com/it/u=889768745,998862614&fm=253&app=138&f=JPEG?w=800&h=1372","https://img2.baidu.com/it/u=1622597608,1377744426&fm=253&app=138&f=JPEG?w=800&h=1422","https://img1.baidu.com/it/u=1791741031,1284315034&fm=253&app=138&f=JPEG?w=800&h=1199","https://pic.rmb.bdstatic.com/bjh/video/efaacfa961497e635fad1cf9bffabfe3.jpeg?for=bg","https://qcloud.dpfile.com/pc/JYEc5AfZmYfDw2k2rh2SOmEF4OvL5kiNnyVtnsI-p46wy43j2YldXkWVhVrI7Ign.jpg","https://inews.gtimg.com/om_bt/OL3l3PFwZsaPuLsg2GWtQW-Uslu8epudPAYI5WLP5uzFsAA/641","https://p3-pc-sign.douyinpic.com/tos-cn-i-0813c001/ooDrAMEfQIsVoIEc7f2bDflCse1YtAQ0AGAAFi~tplv-dy-aweme-images:q75.webp?biz_tag=aweme_images&from=327834062&lk3s=138a59ce&s=PackSourceEnum_PUBLISH&sc=image&se=false&x-expires=1757570400&x-signature=yiT92FEoALP7a6FinmkvyCGDm2Q%3D"),height: getRandomFromArray(100, 150, 195, 200, 350, 400, 630),text: `图片 ${i + 1}`,maxWidth: getRandomFromArray(180, 220, 250, 280),});}return list;
}
onMounted(() => {lists.value = FN_lists();
});
</script><style scoped>
.homes {width: 100vw;min-height: 100vh;padding: 20px;overflow: auto;background-color: #f8f9fa;box-sizing: border-box;
}
/* 卡片容器样式 */
.hoverdiv {position: relative;border-radius: 12px;overflow: hidden;box-shadow: 0 4px 12px #00000014;transition: transform 0.4s ease, box-shadow 0.4s ease;cursor: pointer;
}/* 图片样式与动画 */
.item-img {width: 100%;height: 100%;object-fit: cover;transition: transform 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}/* 蒙版样式与动画 */
.item-mask {position: absolute;inset: 0;background: linear-gradient(to top, #000000b3 0%, #ff79a366 100%);opacity: 0;transform: translateY(20px);transition: opacity 0.5s ease, transform 0.5s ease;
}/* 文本样式与动画 */
.item-text {position: absolute;left: 50%;top: 50%;transform: translate(-50%, 100px);padding: 12px;font-size: 16px;color: #ffffff;text-shadow: 0 2px 4px #00000033;white-space: nowrap;opacity: 0;transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);transition-delay: 0.1s;
}/* 鼠标悬停效果 */
.hoverdiv:hover {transform: translateY(-6px);box-shadow: 0 12px 20px #0000001f;
}.hoverdiv:hover .item-img {transform: scale(1.12);
}.hoverdiv:hover .item-mask {opacity: 1;transform: translateY(0);
}.hoverdiv:hover .item-text {opacity: 1;transform: translate(-50%, -50%);animation: textPulse 1.5s ease-in-out infinite alternate;
}/* 文本脉动动画 */
@keyframes textPulse {from {letter-spacing: 0;font-weight: 400;}to {letter-spacing: 0.5px;font-weight: 500;}
}/* display布局的时候子元素没有固定的宽高的情况下居中 */
.display_width {width: 100%;height: 100px;overflow: auto;border: 1px solid #36d;box-sizing: border-box;padding: 0 10px;column-count: auto;column-width: 150px;display: flex;flex-flow: row;flex-direction: row;align-items: center;flex-wrap: nowrap;gap: 10px;
}
.display_ziyuansu {height: 60px;border: 1px solid #36d;box-sizing: border-box;padding: 10px 20px;flex-grow: 0;flex-shrink: 0;line-height: 40px;
}/* 滚动条样式 */
.GunDongTiao::-webkit-scrollbar {width: 5px;height: 5px;
}.GunDongTiao::-webkit-scrollbar-track {background: #f1f1f1;border-radius: 10px;
}.GunDongTiao::-webkit-scrollbar-thumb {background: #888;border-radius: 10px;
}.GunDongTiao::-webkit-scrollbar-thumb:hover {background: #8ac4e6;
}
</style>