尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

vue3实现图片瀑布流(基于Masonry实现)

vue3实现图片瀑布流(基于Masonry实现)
📅 发布时间:2026/6/23 13:04:20

前言

需要实现一个图片+视频的瀑布流效果,使用了vue3+Masonry库进行实现,这是一个关于调用AI提供接口(聚合起来)再实现一个二次AI的平台

效果

模板一

模板二

开始

1.安装

npm install masonry-layout

2.代码1实现

<script setup lang="ts"> import { reactive, ref, watch, onMounted, nextTick } from "vue"; import Masonry from "masonry-layout"; /** * 图片瀑布流 */ const gridRef = ref(null); let msnry = null; // 初始化 Masonry const initMasonry = async () => { console.log("====initMasonry======"); await nextTick(); if (!gridRef.value) return; msnry = new Masonry(gridRef.value, { itemSelector: ".i-c-card", columnWidth: 224, gutter: 5, fitWidth: false, // 水平排序 horizontalOrder: true, // 取消掉左上角动画 transitionDuration: 0, }); }; // 监听数据变化[更新布局] watch( () => dataImgArr.value, async () => { await nextTick(); if (msnry) { msnry.reloadItems(); msnry.layout(); } }, { deep: true }, ); // 图片|视频加载好调用msnry.layout const relayout = () => { if (msnry) { msnry.layout(); } }; // 页面渲染完执行initMasonry onMounted(() => { initMasonry(); }); </script> <template> <div class="my-container"> <!-- 图片瀑布流 --> <div class="img-container" @scroll="handleScroll"> <div class="img-content" ref="gridRef"> <template v-for="(item, key) in dataArr" :key="item.id"> <div class="i-c-card" ref="cardRef"> <div style="position: relative"> <img v-if="item.chat_type == 'image'" :src="item.chat_message[0]" alt="Image" @load="relayout" /> <video v-if="item.chat_type == 'video'" :src="item.chat_message[0]" @loadedmetadata="relayout" ></video> <div class="i-c-nav-content"> <div style=" display: flex; justify-content: center; align-items: center; " > <div class="logo"> <img :src="item.userinfo.avatar" alt="" /> </div> <div class="title"> <span>{{ item.userinfo.nickname }}</span> </div> </div> <div v-if="item.glsquare" style=" display: flex; justify-content: center; align-items: center; " > <div class="btn"> <div class="content"> <img src="@assets/icons/white-watch.svg" alt="" /> <span>{{ item.glsquare.views }}</span> </div> </div> <div class="btn" style="margin-left: 10px"> <div class="content"> <img src="@assets/icons/collect.svg" alt="" /> <span>{{ item.glsquare.likes }}</span> </div> </div> </div> </div> </div> <div class="i-c-nan-title-content" v-if="false"> <div class="title"> <span>{{ item.glsquare.title }}</span> </div> </div> </div> </template> </div> </div> <!-- end 图片瀑布流 --> </div> </template> <style scoped lang="scss"> .img-container { height: calc(100vh - 60px); overflow-y: auto; margin-left: 20px; .img-content { width: 100%; display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 2px; padding: 0px 0px; padding-bottom: 100px; // 滚动条 .ready { opacity: 1; } .i-c-card::after { content: ""; position: absolute; inset: 0; background: rgba(0, 0, 0, 0); transition: all 0.25s ease; pointer-events: none; } .i-c-card:hover::after { background: rgba(0, 0, 0, 0.4); } .i-c-card:hover { // transform: translateY(-6px); box-shadow: 0 12px 30px rgba(0, 0, 0, 0.18), 0 4px 10px rgba(0, 0, 0, 0.12); } .i-c-card:hover .i-c-nav-content { z-index: 9; } .i-c-card:hover .i-c-nan-title-content { z-index: 9; } .i-c-card { overflow: hidden; display: flex; flex-direction: column; //width: 100%; width: 224px; // width: calc((100vw - 280px) / 8); border-radius: 8px; margin-bottom: 10px; //background-color: red; //height: auto; img { cursor: pointer; width: 100%; height: auto; display: block; object-fit: cover; } video { cursor: pointer; width: 100%; height: auto; display: block; object-fit: cover; } .i-c-nan-title-content { padding: 10px 20px; display: flex; align-items: center; border: 1px solid rgba(38, 38, 38, 1); border-top: 0; background-color: rgba(23, 23, 23, 1); color: rgba(196, 199, 200, 1); font-size: 12px; .title { width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } } .i-c-nav-content { width: 100%; position: absolute; bottom: 5px; padding: 0px 20px; display: flex; align-items: center; justify-content: space-between; .i-c-n-li { flex: 1; } .logo { width: 24px; img { width: 24px; height: 24px; border-radius: 100%; } } .title { width: 80px; margin-left: 4px; margin-right: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 10px; color: rgba(255, 253, 253, 1); } .btn { //background-color: rgba(217, 217, 217, 0.8); border-radius: 10px; padding: 5px 5px; img { width: 12px; height: 12px; margin-right: 2px; } .content { display: flex; justify-content: center; align-items: center; span { margin-left: 5px; font-size: 14px; } } } } } } @media (max-width: 1600px) { .img-content { grid-template-columns: repeat(5, 1fr); } } @media (max-width: 1400px) { .img-content { grid-template-columns: repeat(4, 1fr); } } @media (max-width: 1100px) { .img-content { grid-template-columns: repeat(3, 1fr); } } @media (max-width: 768px) { .img-content { grid-template-columns: repeat(2, 1fr); } } @media (max-width: 480px) { .img-content { grid-template-columns: repeat(1, 1fr); } } } </style>

代码2实现

<script setup lang="ts"> import { ref, watch, onMounted, nextTick } from "vue"; const list = ref([ { id: 0, chat_type: "image", chat_message: ["https://picsum.photos/id/10/1200/675"], orientation: "横", }, { id: 1, chat_type: "image", chat_message: ["https://picsum.photos/id/11/675/1200"], orientation: "竖", }, { id: 2, chat_type: "image", chat_message: ["https://picsum.photos/id/12/1920/1080"], orientation: "横", }, { id: 3, chat_type: "image", chat_message: ["https://picsum.photos/id/13/1080/1920"], orientation: "竖", }, { id: 4, chat_type: "image", chat_message: ["https://picsum.photos/id/14/1440/900"], orientation: "横", }, { id: 5, chat_type: "image", chat_message: ["https://picsum.photos/id/15/900/1440"], orientation: "竖", }, { id: 6, chat_type: "image", chat_message: ["https://picsum.photos/id/16/1600/900"], orientation: "横", }, { id: 7, chat_type: "image", chat_message: ["https://picsum.photos/id/17/900/1600"], orientation: "竖", }, { id: 8, chat_type: "image", chat_message: ["https://picsum.photos/id/18/1280/720"], orientation: "横", }, { id: 9, chat_type: "image", chat_message: ["https://picsum.photos/id/19/720/1280"], orientation: "竖", }, { id: 10, chat_type: "image", chat_message: ["https://picsum.photos/id/20/1920/1080"], orientation: "横", }, { id: 11, chat_type: "image", chat_message: ["https://picsum.photos/id/21/1080/1920"], orientation: "竖", }, { id: 12, chat_type: "image", chat_message: ["https://picsum.photos/id/22/1440/900"], orientation: "横", }, { id: 13, chat_type: "image", chat_message: ["https://picsum.photos/id/23/900/1440"], orientation: "竖", }, { id: 14, chat_type: "image", chat_message: ["https://picsum.photos/id/24/1600/900"], orientation: "横", }, { id: 15, chat_type: "image", chat_message: ["https://picsum.photos/id/25/900/1600"], orientation: "竖", }, { id: 16, chat_type: "image", chat_message: ["https://picsum.photos/id/26/1280/720"], orientation: "横", }, { id: 17, chat_type: "image", chat_message: ["https://picsum.photos/id/27/720/1280"], orientation: "竖", }, { id: 18, chat_type: "image", chat_message: ["https://picsum.photos/id/28/1920/1080"], orientation: "横", }, { id: 19, chat_type: "image", chat_message: ["https://picsum.photos/id/29/1080/1920"], orientation: "竖", }, { id: 20, chat_type: "image", chat_message: ["https://picsum.photos/id/30/1440/900"], orientation: "横", }, { id: 21, chat_type: "image", chat_message: ["https://picsum.photos/id/31/900/1440"], orientation: "竖", }, { id: 22, chat_type: "image", chat_message: ["https://picsum.photos/id/32/1600/900"], orientation: "横", }, { id: 23, chat_type: "image", chat_message: ["https://picsum.photos/id/33/900/1600"], orientation: "竖", }, { id: 24, chat_type: "image", chat_message: ["https://picsum.photos/id/34/1280/720"], orientation: "横", }, { id: 25, chat_type: "image", chat_message: ["https://picsum.photos/id/35/720/1280"], orientation: "竖", }, { id: 26, chat_type: "image", chat_message: ["https://picsum.photos/id/36/1920/1080"], orientation: "横", }, { id: 27, chat_type: "image", chat_message: ["https://picsum.photos/id/37/1080/1920"], orientation: "竖", }, { id: 28, chat_type: "image", chat_message: ["https://picsum.photos/id/38/1440/900"], orientation: "横", }, { id: 29, chat_type: "image", chat_message: ["https://picsum.photos/id/39/900/1440"], orientation: "竖", }, ]); const gridRef = ref<HTMLElement | null>(null); let msnry: Masonry | null = null; import Masonry from "masonry-layout"; /** 初始化 Masonry */ const initMasonry = async () => { await nextTick(); if (!gridRef.value) return; msnry = new Masonry(gridRef.value, { itemSelector: ".i-c-card", gutter: 5, fitWidth: false, horizontalOrder: true, transitionDuration: 0, }); }; /** 重新布局(图片/视频加载后调用) */ const relayout = () => { msnry?.layout(); }; onMounted(initMasonry); // 数据变化后重新计算布局 watch( () => list.value, async () => { await nextTick(); if (msnry) { msnry.reloadItems(); msnry.layout(); } }, { deep: true }, ); </script> <template> <div class="img-container"> <div class="img-content" ref="gridRef"> <template v-for="item in list" :key="item.id"> <div class="i-c-card"> <div> <img v-if="item.chat_type == 'image'" :src="item.chat_message[0]" alt="Image" @load="relayout" /> </div> <div> <!--其他元素--> </div> </div> </template> </div> </div> </template> <style lang="scss" scoped> .img-container { height: calc(100vh - 60px); overflow-y: auto; margin-left: 20px; .img-content { width: 100%; display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 2px; padding: 0px 0px; padding-bottom: 200px; // 滚动条 .i-c-card::after { content: ""; position: absolute; inset: 0; background: rgba(0, 0, 0, 0); transition: all 0.25s ease; pointer-events: none; } .i-c-card:hover::after { background: rgba(0, 0, 0, 0.4); } .i-c-card:hover { transform: translateY(-3px); box-shadow: 0 12px 30px rgba(0, 0, 0, 0.18), 0 4px 10px rgba(0, 0, 0, 0.12); } .i-c-card { overflow: hidden; display: flex; flex-direction: column; width: 224px; border-radius: 2px; margin-bottom: 6px; transition: transform 0.3s ease; img { cursor: pointer; width: 100%; height: auto; display: block; object-fit: cover; } } } } </style>

只能提供大概得代码模板,仅供参考

总结

1.先安装npm install masonry-layout

2.查看它的配置参数

3.代码实现

相关新闻

  • 02-数字孪生三大厂商2026最新技术布局深度解析
  • 如何用WeChatMsg将微信聊天记忆变成永久数字财富?
  • foobar2000终极美化指南:5分钟打造专业音乐播放界面

最新新闻

  • 六自由度自平衡稳定平台:动态工况下的高精度稳姿技术与行业应用
  • 如何选择一家专业的兰州软件开发服务商?
  • 为什么我的 Radeon 显卡没出力,检查这几点立刻解决
  • 长上下文推理不再难,Strix Halo 轻松拿捏十万字小说分析
  • 挺进沙漠腹地:全国单体最大沙漠光伏项目通信网络选型与部署实践
  • Sunshine游戏串流完整指南:5步打造你的私人游戏云

日新闻

  • Arduino-ESP32项目深度解析:解锁隐藏芯片支持与架构演进
  • 2026年 系统窗厂家/品牌推荐榜单:隔音系统窗+高端系统门窗的核心优势与选购指南 - 品牌发掘
  • NVBench:首个双语非言语发声语音合成评测基准详解与实践

周新闻

  • Visual C++运行库修复终极指南:5分钟快速解决Windows软件启动错误
  • 手把手教你构建统计局地区经济数据爬虫:从环境搭建到数据持久化全指南
  • 2026多Agent深度解析:用AI团队替代单一模型,四种架构实战落地

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号