nuxt.js框架使用vue-waterfall-easy插件如何引入--语法引用
2020-07-15 06:03
1696 查看
实话:我之前完全不知道vue-waterfall-easy这个插件,当我看到需求瀑布流+无限滚动的时候,我脑子里想的还是flex布局。。。
我们大佬说“你自己写的瀑布流+无限滚动肯定会出问题的”,然后我就果断放弃了(毕竟我是一个还没有入门的菜鸡),大佬给我普及了这个插件以及在vue中如何使用,让我自己找一找在nuxt里面的引入方法(这个任务,我也很果断,20分钟没有头绪,直接回头找大佬,哈哈哈哈哈哈)
所以在这里再总结一下引入的方法(使用方法我也是一知半解,探索中,所以不敢胡乱BB)。
这次在nuxt的引入,一开始也是使用了
npm install,按照了官网的格式去引入了一下(其实就是觉得引入配置应该不会有那么多特殊的吧)然后,我真的服了!
常规方法失效,回头找大佬。
大佬跟我说:
在
components文件夹下 创建
vue-waterfall-easy.vue文件 将
vue-waterfall-easy的代码复制-粘贴(我放到这了,如果真的有人看,我也是为了自己以后有用的时候可以不用再去找,哈哈哈哈哈)
<!-- —————————————↓SCSS———————分界线————————————————————————— --> <style lang="scss" scoped> .vue-waterfall-easy-container { width: 100%; height: 100%; position: relative; .vue-waterfall-easy-scroll { position: relative; width: 100%; height: 100%; overflow-x: hidden; overflow-y: scroll; -webkit-overflow-scrolling: touch; } .vue-waterfall-easy { position: absolute; width: 100%; // 移动端生效 @keyframes show-card { 0% { transform: scale(0.5); } 100% { transform: scale(1); } } & > .img-box { position: absolute; box-sizing: border-box; width: 50%; // 移动端生效 } & > .img-box.default-card-animation { animation: show-card 0.4s; transition: left 0.6s, top 0.6s; transition-delay: 0.1s; } a { display: block; } a.img-inner-box { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); border-radius: 4px; } .__err__ .img-wraper { background-image: url(data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAeAAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjk1M0JCM0QwNkVFNDExRThCNTJCQUQ2RDFGQzg0NzIxIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjk1M0JCM0NGNkVFNDExRThCNTJCQUQ2RDFGQzg0NzIxIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTYwRUMyMDE2RUUzMTFFOEJCRTU5RTFDODg1ODgwMjYiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTYwRUMyMDI2RUUzMTFFOEJCRTU5RTFDODg1ODgwMjYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAQCwsLDAsQDAwQFw8NDxcbFBAQFBsfFxcXFxcfHhcaGhoaFx4eIyUnJSMeLy8zMy8vQEBAQEBAQEBAQEBAQEBAAREPDxETERUSEhUUERQRFBoUFhYUGiYaGhwaGiYwIx4eHh4jMCsuJycnLis1NTAwNTVAQD9AQEBAQEBAQEBAQED/wAARCACRAJEDASIAAhEBAxEB/8QAZQAAAwEBAQAAAAAAAAAAAAAAAAIDAQQHAQEAAAAAAAAAAAAAAAAAAAAAEAACAQMDBAEFAAMBAAAAAAAAAQIRMQMhQRJRYYEycZHBIkITsdFSYhEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A9AAAAAMFnNQWt9kAwkssVbV9CTnKb10XQVtLSyAd5ZuzURW27yfhmX9RlDI+wD4Vf/ZVi41SKCdeNI3YEnOXJtOiBZZr/wBGcMi2Ft7AXjli76PoOcqael0Mpyg9NV0A6QEhNTWl90MBoAAAAAAGGiTlxjXfZAZkycdEqyI3q26sOrd92CTm6bbsA1boh1i3lqPGKiqIZAYklYHY0x6tIDY2B3NdjEBgNJ3NACTxbx0E1TozoYsoqSowI2o06MtjyctGqSItODptsw6NX2YHSaJCXKNd90OAAAAYznnLnJvZWK5pUjRXloiNdwCjk0l9S0YqKohcSpGrvLUcDUBLK23x23FWNtVSAuHch/KXQP5PoBdqq77GJ1XfczFVKjVBcuPk6rXqBQCH8n0D+UugFwZD+bV1oNif5OOzQDyipKjI0cW0/qXYmVVjVXjqAkZcHXZ3OhHNXcthlWNHeOjAoAABDLKs6bISlWl1Busm+42Jfm30At2BmI1gRy+3gpD1XwTy+3gpH1QDASyt8uKdFuJRw1iwOjdBKy+TIutGbK3kAAxtJNuwiywdmA7s/glj9/BV04unQli9l8AWDsBjAhSja6D4pUnTZmZV+afUVOkk+4HUBgAc0bD4v2+fsJGw+L9vn7AVQMEDAjl9vBSHqvgnl9vBSHqvgDJwbfJC8JPSlEO5wTo3qMnUDEqNGz0jXoD08BRSXyBB/m6u2xvFPQ1qjoDAVNxqv1ZuJUnT5BGw9/AFQYAwJZf1+fsJKw+X9fn7CSsB0AAAc7VG13GxP82uoZFSbezFWkkwOgGCBgRy15adDZTaioq7QZPfwZQDFFfPc2MnB0vE1AwCc+WituPjaS4kzU6agPkS9hEVeqJJU0AAh7+ACHv4AqAAwI5X+aXQVKrS7g3WTY2ONZp7IC9AAAJ5lWNf+dSV1XqdL1VGc8lxk47bAUxyqqO60GIpuL5LyuxZNNVVtmAmVfkpbbi1RYAI1QVRYAI1QJ1aRYzdAbJ8Y1I1RZggI1SNxL8uW1NCoAYxckqKiu9Bm0lV23ZFtyfJ+F2Ayyr0K4VSNf8ArUnFcpKO250LRUQABoAYLkhzXdWHMA5u26uNGXF9tx8uOusfYlbTcC6aaqrdTTnTlHVfQrHJF6PRsBwAAC5i9vg0xbgaAIAAxtJVduosskVotWiTcpav6ANKXJ9the27sF9NyuLHTWXsA2OHBd3cYDQAAAAAAAwSeNS1syhgHNRxdJfUK10ujoaTuJLCrp0Amm1Ztdhv6z3Sfkxwmu4leqoBT+1P1f8AkZy4469daEaopllRqPRAH9ZOyp5Fbbu2+xmuyGUJvsAtaaWQUcnSP1Kxwq7dR0krALDGo63Y4GgAAAAAAAAAAAAAAAAAshJ7AAGK6B3YABsNx4gADAAAAAAAAAAAAAf/2Q==); background-repeat: no-repeat; background-position: center; background-size: 50% 50%; img { display: none; } } a.img-wraper { & > img { width: 100%; display: block; border: none; } } .over { position: absolute; width: 100%; text-align: center; font-size: 12px; line-height: 1.6; color: #aaa; } } & > .loading.first { bottom: 50%; transform: translate(-50%, 50%); } & > .loading { position: absolute; left: 50%; transform: translateX(-50%); bottom: 6px; z-index: 999; @keyframes ball-beat { 50% { opacity: 0.2; transform: scale(0.75); } 100% { opacity: 1; transform: scale(1); } } &.ball-beat > .dot { vertical-align: bottom; background-color: #4b15ab; width: 12px; height: 12px; border-radius: 50%; margin: 3px; animation-fill-mode: both; display: inline-block; animation: ball-beat 0.7s 0s infinite linear; } &.ball-beat > .dot:nth-child(2n-1) { animation-delay: 0.35s; } } } </style> <!-- —————————————↓HTML————————分界线———————————————————————— --> <template lang="pug"> .vue-waterfall-easy-container(:style="{width: width&&!isMobile ? width+'px' : '', height: parseFloat(height)==height ? height+'px': height }") .loading.ball-beat(v-show="isPreloading_c", :class="{first:isFirstLoad}") slot(name="loading", :isFirstLoad="isFirstLoad") .dot(v-if="!hasLoadingSlot", v-for="n in loadingDotCount",:style="loadingDotStyle") //- 为了防止loading 跟随滚动 .vue-waterfall-easy-scroll(ref="scrollEl") slot(name="waterfall-head") .vue-waterfall-easy(:style="isMobile? '' :{width: colWidth*cols+'px',left:'50%', marginLeft: -1*colWidth*cols/2 +'px'}") .img-box( v-for="(v,i) in imgsArr_c", :class="[cardAnimationClass, {__err__: v._error}]" :style="{padding: (isMobile ? mobileGap : gap)/2+'px', width: isMobile ? '' : colWidth+'px'}" ) component.img-inner-box( :is="isRouterLink && linkRange=='card' ? 'router-link' : 'alink'", :data-index="i", :to="linkRange=='card' ? v[hrefKey] : false") component.img-wraper( v-if="v[srcKey]", :is="isRouterLink && linkRange=='img' ? 'router-link' :'alink'", :to="linkRange=='img' ? v[hrefKey] : false ", :style="{width:imgWidth_c + 'px',height:v._height ? v._height+'px':false}") img(:src="v[srcKey]") slot(:index="i",:value="v") .over(v-if="over",ref="over") slot(name="waterfall-over") 被你看光了 </template> <!-- ——————————————↓JS—————————分界线———————————————————————— --> <script> import alink from './components/alink.vue' export default { name: 'vue-waterfall-easy', components: { alink }, props: { width: { // 容器宽度 type: Number }, height: { // 容器高度 type: [Number, String] }, reachBottomDistance: { // 滚动触底距离,触发加载新图片 type: Number, // selector default: 20 // 默认在最低那一列到底时触发 }, loadingDotCount: { // loading 点数 type: Number, default: 3 }, loadingDotStyle: { type: Object, }, gap: { // .img-box 间距 type: Number, default: 20 }, mobileGap: { type: Number, default: 8 }, maxCols: { type: Number, default: 5 }, imgsArr: { type: Array, required: true, }, srcKey: { type: String, default: 'src' }, hrefKey: { type: String, default: 'href' }, imgWidth: { type: Number, default: 240 }, isRouterLink: { type: Boolean, default: false }, linkRange: { // card | img | custom 自定义通过slot自定义链接范围 type: String, default: 'card' }, loadingTimeOut: { // 预加载事件小于500毫秒就不显示加载动画,增加用户体验 type: Number, default: 500 }, cardAnimationClass: { type: [String], default: 'default-card-animation' }, enablePullDownEvent: { type: Boolean, default: false } }, data() { return { msg: 'this is from vue-waterfall-easy.vue', isMobile: !!navigator.userAgent.match(/(iPhone|iPod|Android|ios)/i), // 初始化移动端 isPreloading: true, // 正在预加载中,显示加载动画 isPreloading_c: true, imgsArr_c: [], // 待图片预加载imgsArr完成,插入新的字段height之后,才会生成imgsArr_c,这时才开始渲染 loadedCount: 0, cols: NaN, // 需要根据窗口宽度初始化 imgBoxEls: null, // 所有的.img-box元素 beginIndex: 0, // 开始要排列的图片索引,首次为第二列的第一张图片,后续加载则为已经排列图片的下一个索引 colsHeightArr: [], // 自定义loading LoadingTimer: null, isFirstLoad: true, // 首次加载 over: false, // 结束waterfall加载 } }, computed: { colWidth() { // 每一列的宽度 return this.imgWidth + this.gap }, imgWidth_c() { // 对于移动端重新计算图片宽度` return this.isMobile ? window.innerWidth / 2 - this.mobileGap : this.imgWidth }, hasLoadingSlot() { return !!this.$scopedSlots.loading } }, mounted() { this.bindClickEvent() this.loadingMiddle() this.preload() this.cols = this.calcuCols() this.$on('preloaded', () => { this.isFirstLoad = false this.imgsArr_c = this.imgsArr.concat([]) // 预加载完成,这时才开始渲染 this.$nextTick(() => { this.isPreloading = false this.imgBoxEls = this.$el.getElementsByClassName('img-box') // console.log('图片总数', this.imgBoxEls.length) this.waterfall() }) }) if (!this.isMobile && !this.width) window.addEventListener('resize', this.response) if (this.isMobile && this.enablePullDownEvent) this.pullDown() this.scroll() }, beforeDestroy() { window.removeEventListener('resize', this.response) }, watch: { isPreloading(newV, oldV) { if (newV) { setTimeout(() => { if (!this.isPreloading) return // 500毫秒内预加载完图片则不显示加载动画 this.isPreloading_c = true }, this.loadingTimeOut) } else { this.isPreloading_c = false } }, imgsArr(newV, oldV) { if (this.imgsArr_c.length > newV.length || (this.imgsArr_c.length > 0 && newV[0] && !newV[0]._height)) { // console.log('reset') this.reset() } this.preload() }, }, methods: { // ==1== 预加载 preload(src, imgIndex) { this.imgsArr.forEach((imgItem, imgIndex) => { if (imgIndex < this.loadedCount) return // 只对新加载图片进行预加载 // 无图时 if (!imgItem[this.srcKey]) { this.imgsArr[imgIndex]._height = '0' this.loadedCount++ // 支持无图模式 if (this.loadedCount == this.imgsArr.length) { this.$emit('preloaded') } return } var oImg = new Image() oImg.src = imgItem[this.srcKey] oImg.onload = oImg.onerror = (e) => { this.loadedCount++ // 预加载图片,计算图片容器的高 this.imgsArr[imgIndex]._height = e.type == 'load' ? Math.round(this.imgWidth_c / (oImg.width / oImg.height)) : (this.isMobile ? this.imgWidth_c : this.imgWidth) if (e.type == 'error') { this.imgsArr[imgIndex]._error = true this.$emit('imgError', this.imgsArr[imgIndex]) } if (this.loadedCount == this.imgsArr.length) { this.$emit('preloaded') } } }) }, // ==2== 计算cols calcuCols() { // 列数初始化 var waterfallWidth = this.width ? this.width : window.innerWidth var cols = parseInt(waterfallWidth / this.colWidth) cols = cols === 0 ? 1 : cols return this.isMobile ? 2 : (cols > this.maxCols ? this.maxCols : cols) }, // ==3== waterfall布局 waterfall() { if (!this.imgBoxEls) return // console.log('waterfall') var top, left, height, colWidth = this.isMobile ? this.imgBoxEls[0].offsetWidth : this.colWidth if (this.beginIndex == 0) this.colsHeightArr = [] for (var i = this.beginIndex; i < this.imgsArr.length; i++) { if (!this.imgBoxEls[i]) return height = this.imgBoxEls[i].offsetHeight if (i < this.cols) { this.colsHeightArr.push(height) top = 0 left = i * colWidth } else { var minHeight = Math.min.apply(null, this.colsHeightArr) // 最低高低 var minIndex = this.colsHeightArr.indexOf(minHeight) // 最低高度的索引 top = minHeight left = minIndex * colWidth // 设置元素定位的位置 // 更新colsHeightArr this.colsHeightArr[minIndex] = minHeight + height } this.imgBoxEls[i].style.left = left + 'px' this.imgBoxEls[i].style.top = top + 'px' } this.beginIndex = this.imgsArr.length // 排列完之后,新增图片从这个索引开始预加载图片和排列 }, // ==4== resize 响应式 response() { var old = this.cols this.cols = this.calcuCols() if (old === this.cols) return // 列数不变直接退出 this.beginIndex = 0 // 开始排列的元素索引 this.waterfall() if (this.over) this.setOverTipPos() }, // ==5== 滚动触底事件 scrollFn() { var scrollEl = this.$refs.scrollEl if (this.isPreloading) return var minHeight = Math.min.apply(null, this.colsHeightArr) if (scrollEl.scrollTop + scrollEl.offsetHeight > minHeight - this.reachBottomDistance) { this.isPreloading = true // console.log('scrollReachBottom') this.$emit('scrollReachBottom') // 滚动触底 } }, scroll() { this.$refs.scrollEl.addEventListener('scroll', this.scrollFn) }, waterfallOver() { this.$refs.scrollEl.removeEventListener('scroll', this.scrollFn) this.isPreloading = false this.over = true this.setOverTipPos() }, setOverTipPos() { var maxHeight = Math.max.apply(null, this.colsHeightArr) this.$nextTick(() => { this.$refs.over.style.top = maxHeight + 'px' }) }, // ==6== 点击事件绑定 bindClickEvent() { this.$el.querySelector(".vue-waterfall-easy") .addEventListener('click', e => { var targetEl = e.target; if (e.target.className.indexOf('over') !== -1) return if (targetEl.className.indexOf("img-box") != -1) return while (targetEl.className.indexOf("img-inner-box") == -1) { targetEl = targetEl.parentNode; } var index = targetEl.getAttribute("data-index"); this.$emit('click', e, { index, value: this.imgsArr_c[index], }) }) }, // ==7== 下拉事件 pullDown() { var scrollEl = this.$el.querySelector('.vue-waterfall-easy-scroll') var startY scrollEl.addEventListener('touchmove', (e) => { if (scrollEl.scrollTop === 0) { var t = e.changedTouches[0] if (!startY) startY = t.pageY var pullDownDistance = t.pageY - startY if (pullDownDistance > 0) { e.preventDefault() } this.$emit('pullDownMove', pullDownDistance) } }) scrollEl.addEventListener('touchend', (e) => { if (scrollEl.scrollTop === 0) { startY = NaN this.$emit('pullDownEnd') } }) }, // other loadingMiddle() { // 对滚动条宽度造成的不居中进行校正 var scrollEl = this.$el.querySelector('.vue-waterfall-easy-scroll') var scrollbarWidth = scrollEl.offsetWidth - scrollEl.clientWidth this.$el.querySelector('.loading').style.marginLeft = -scrollbarWidth / 2 + 'px' }, reset() { this.imgsArr_c = [] this.beginIndex = 0 this.loadedCount = 0 this.isFirstLoad = true this.isPreloading = true this.scroll() this.over = false } } } </script>
如果有兴趣也可以去GitHub看下源文档的说明,代码也是从那里复制回来的。
在
plugins文件夹下创建
vue-waterfall-easy.js文件并进行配置:
import Vue from 'vue' import VueWaterfallEasy from 'vue-waterfall-easy' Vue.use(VueWaterfallEasy) export default { name: 'vue-waterfall-easy' }
同样的,在
plugins文件夹下创建
alink.vue文件并进行配置:
<!-- —————————————↓SCSS———————分界线————————————————————————— --> <style lang="scss"> .alink { } </style> <!-- —————————————↓HTML————————分界线———————————————————————— --> <template> <a class="alink" :href="to" target="_blank"> <slot></slot> </a> </template> <!-- ——————————————↓JS—————————分界线———————————————————————— --> <script> //import XXX from './components/XXX' export default { name: 'alink', props: ['to'], data() { return { msg: 'this is from alink.vue' } }, methods: {} } </script>
注:在这次的引入中并没有配置
nuxt.config.js文件,而是直接把
vue-waterfall-easy.vue作为一个组件使用的,所以其实并没有使用到
npm install的安装,至于大佬为什么这样操作,你问我我也不知道。同理
alink.vue在这里的作用。。。。哈哈哈哈,我只想着怎么顺利使用
vue-waterfall-easy了,如果有大佬可以解惑,感激不尽!!!
既然作为一个组件使用的话,必然需要在使用的页面中引入:
import vueWaterfallEasy from "~/components/vue-waterfall-easy.vue";
顺带注册一下:
components:{ vueWaterfallEasy }
至此
vue-waterfall-easy引入结束,顺便贴上我试验的代码吧(URL是肯定给不起的):
<template> <div style="height:800px"> <vue-waterfall-easy :imgsArr="sourceMaterialList" srcKey="image" @scrollReachBottom="getData" ></vue-waterfall-easy> </div> </template> <script> import vueWaterfallEasy from "~/components/vue-waterfall-easy.vue";import axios from "axios"; export default { components: { vueWaterfallEasy }, data() { return { sourceMaterialList: [], total: 1 }; }, methods: { getData() { axios .get("URL") .then(res => { console.log(res); this.sourceMaterialList = this.sourceMaterialList.concat( res.data.rows ); console.log(this.sourceMaterialList); this.total = res.total; }); } }, created() { this.getData(); } }; </script> <style> #canvas { background-color: gray; } </style>
自我总结。
相关文章推荐
- 如何在VUE框架中使用ztree插件
- 如何在vue.js中使用jQuery 插件
- 在不使用全部vuejs框架,只在html中引用vue.js时,在main.js中调用其他js方法
- [js框架]Vue框架的基础学习 一、模板语法的使用
- 微信小程序如何引入使用js插件?
- 如何在vue项目中引入JSMpeg插件
- Vue的框架Nuxt.js中引入Sass
- vue2.0 使用vue-cli搭建的项目如何在index.html里引入静态css和js
- [原创]用户控件中使用Js,如何在客户端引用用户控件中某个控件的ID
- Vue项目中如何引用外部js
- easy ui 如何单个引用其中某个插件?
- 如何使用JS开发游戏,游戏框架LayaAir介绍
- 如何使用webpack在vue项目中写jsx语法
- vue轮播图插件vue-awesome-swiper的引入及使用
- 如何使用php脚本给html中引用的js和css路径打上版本号
- 如何使用Vuex+Vue.js构建单页应用
- Vue中如何引入远程JS文件?
- Vue.js 使用cordova camera插件调取相机
- vue.js如何将echarts封装为组件一键使用详解
- Vue.js 使用cordova camera插件调取相机