先上效果图:
(1) 看起来可能有点卡顿,但是实际上页面上看起来挺顺畅的。
(2) 思路就是获取每一个列表的宽度,设置定时器移动列表,当移动的距离达到一个列表的宽度的时候,把这个距离放到数组的最后。这样就能达成无缝循环滚动了。
大致的情况就是下面这样:
接下来就是代码的实现:
index.vue 引入组件
<template>
<div>
<marqueeLeft :send-val=\'send\'></marqueeLeft >
</div>
</template>
<script> import marqueeLeft from \'../components/marquee\' export default { data:function(){ return{ send:[{place: \"来自东莞市的\", name: \"黄女士\"}, {place: \"来自太原市的\", name: \"吴先生\"}, {place: \"来自常州市的\", name: \"戚先生\"}, {place: \"来自金华市的\", name: \"尤先生\"}, {place: \"来自贵阳市的\", name: \"陈女士\"}, {place: \"来自长春市的\", name: \"魏女士\"}, {place: \"来自泉州市的\", name: \"褚先生\"}, {place: \"来自南昌市的\", name: \"蒋女士\"}, {place: \"来自南京市的\", name: \"沈先生\"}, {place: \"来自天津市的\", name: \"韩先生\"}, {place: \"来自宁波市的\", name: \"邹女士\"}, {place: \"来自嘉兴市的\", name: \"周女士\"}, {place: \"来自长沙市的\", name: \"秦先生\"}, {place: \"来自济南市的\", name: \"孙女士\"}, {place: \"来自杭州市的\", name: \"杨先生\"}] } }, components:{ marqueeLeft }, } </script>
marquee.vue 组件页面
<template>
<div class=\"my-outbox\">
<div class=\"my-inbox\" ref=\'box\'>
<div class=\"my-list\" v-for=\"(item,index) in sendVal\" :key=\'index\' ref=\'list\'> {{item.place}}<span class=\"my-uname\">{{item.name}}</span>刚刚购买了产品 </div>
</div>
</div>
</template>
<script> export default { name:\'my-marquee-left\', props:{ sendVal:Array }, data() { return { nowTime:null,//定时器标识
disArr:[],//每一个内容的宽度
} }, mounted:function(){ var that = this; var item = this.$refs.list; var len = this.sendVal.length; var arr = []; var margin = this.getMargin(item[0]) //因为设置的margin值一样,所以取第一个就行。
for(var i = 0;i < len;i++){ arr.push(item[i].clientWidth + margin)//把宽度和 margin 加起来就是每一个元素需要移动的距离
} this.disArr = arr; this.moveLeft(); }, beforeDestroy:function(){ clearInterval(this.nowTime);//页面关闭清除定时器
this.nowTime = null;//清除定时器标识
}, methods:{ //获取margin属性
getMargin:function(obj){ var marg = window.getComputedStyle(obj,null)[\'margin-right\']; marg = marg.replace(\'px\',\'\') return Number(marg) //强制转化成数字
}, //移动的方法
moveLeft:function(){ var outbox = this.$refs.box; var that=this; var startDis = 0;//初始位置
this.nowTime = setInterval(function(){ startDis -= 0.5; if(Math.abs(startDis) > Math.abs(that.disArr[0])){ that.disArr.push(that.disArr.shift())//每次移动完一个元素的距离,就把这个元素的宽度
that.sendVal.push(that.sendVal.shift())//每次移动完一个元素的距离,就把列表数据的第一项放到最后一项
startDis = 0; } outbox.style = \'transform: translateX(\'+ startDis +\'px)\'; //每次都让盒子移动指定的距离 },1000/60)
} } } </script>
<style lang=\"less\" scoped> .my-outbox{ color: #D7BC8D; overflow: hidden; height: 35px; background: #422b02; .my-inbox{ white-space: nowrap; .my-list{ margin-right: 25px; display: inline-block; font-size: 13px; height: 35px; line-height: 35px; .my-uname{ color: #FF8900;
} } } } </style>
(1) 添加一个获取margin的方法,是为了保证如果是使用 rem em 等单位时,margin值不会出现偏差的情况
(2) 如果引入组件的页面中,send-val的值是异步请求的。那么,在marquee.vue组件页面,多数情况会获取不了 refs 。这时候我自己的解决方法是:
<marqueeLeft :send-val=\'send\' v-if=\'send\'></marqueeLeft >
表示只有当 send 不为空的时候才渲染该组件,不过这种情况会导致如果 请求响应太慢,组件会一直渲染不出来,就可能会影响布局。
(3) 如果不想每次都去设置transform,那么可以把transform放到该元素上,然后修改data中的数据就行了,比如:
<div class=\"my-inbox\" :style=\'transform: translateX(\'+ cssStyle +\'px)\'></div> // 然后在 js 中把 每次移动的值,赋值给cssStyle就行了。不过我感觉这种没什么区别
如果想实现,上下无缝滚动,这种效果。思路和左右无缝滚动一样,基本上只需要把 transform 改成 Y轴移动 ,每个列表的宽度改成高度就行了。
不清楚这种方式实现是否会有什么问题,FPS一直保持在 接近60左右。暂时没发现什么缺点。。。