news 2026/6/23 12:50:19

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vue3实现图片瀑布流(基于Masonry实现)

前言

需要实现一个图片+视频的瀑布流效果,使用了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.代码实现

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/23 12:47:47

Mermaid Live Editor:5分钟掌握零代码图表制作的神器

Mermaid Live Editor&#xff1a;5分钟掌握零代码图表制作的神器 【免费下载链接】mermaid-live-editor Edit, preview and share mermaid charts/diagrams. New implementation of the live editor. 项目地址: https://gitcode.com/GitHub_Trending/me/mermaid-live-editor …

作者头像 李华
网站建设 2026/6/23 12:43:22

从零开始掌握SiYuan笔记:5个实用技巧让你的知识管理更高效

从零开始掌握SiYuan笔记&#xff1a;5个实用技巧让你的知识管理更高效 【免费下载链接】siyuan A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. 项目地址: https://gitcode.com/GitHub_Trendi…

作者头像 李华
网站建设 2026/6/23 12:20:25

LeetCode 189数组轮转问题详解:辅助数组法与三次翻转法

LeetCode 189数组轮转问题详解&#xff1a;辅助数组法与三次翻转法 前言 在数组相关面试题中&#xff0c;LeetCode 189《轮转数组》是一道非常经典的题目。 题目要求将数组中的元素整体向右移动 k 个位置&#xff0c;并且直接修改原数组。看似只是简单的数据移动&#xff0c;但…

作者头像 李华