先上效果图:css
(1) 看起来可能有点卡顿,可是实际上页面上看起来挺顺畅的。vue
(2) 思路就是获取每个列表的宽度,设置定时器移动列表,当移动的距离达到一个列表的宽度的时候,把这个距离放到数组的最后。这样就能达成无缝循环滚动了。css3
大体的状况就是下面这样:小程序
接下来就是代码的实现:数组
index.vue 引入组件app
<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 组件页面less
<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值不会出现误差的状况dom
(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左右。暂时没发现什么缺点。。。
更新一下: 作了一个uniapp版本(小程序端也支持)的无缝滚动,内容和上面这个差很少,可是比上面这个更好一点,不用 16.6ms 左右的时间去更新 data里面的数据
<!-- 从右往左滚动的信息栏 -->
<!-- <myMarqueeLeft :send-val="marqueeLeft(须要动态传入的数据)" v-if='marqueeLeft(须要动态传入的数据'></myMarqueeLeft> -->
<template>
<view class="my-outbox">
<view class="my-inbox" :style="cssStyle">
<view class="my-list" v-for="(item,index) in sendVal" :key='index'> {{item.place}}<text class="my-uname">{{item.name}}</text>刚刚购买了产品 </view>
</view>
</view>
</template>
<script> export default { name:'my-marquee-left', props:{ sendVal:Array }, data() { return { nowTime:null,//定时器标识
disArr:[],//每个内容的宽度
cssDis:0,//当前须要移动的距离
cssStyle:''//当前须要设置的css样式
} }, mounted:function(){ var that = this; //兼容小程序获取不了内容的状况,须要用setTimeout延时
setTimeout(function(){ var target = uni.createSelectorQuery(); target.selectAll('.my-list').boundingClientRect() target.exec(function(msg){ var tdata = msg[0]; var len = tdata.length; for(var i = 0;i < len;i++){ that.disArr.push(tdata[i].width + 25)//把宽度和 margin 加起来就是每个元素须要移动的距离
} that.moveLeft(); }) },200) }, beforeDestroy:function(){ clearInterval(this.nowTime);//页面关闭清除定时器
this.nowTime = null;//清除定时器标识
}, methods:{ //移动的方法
moveLeft:function(){ var that=this; that.cssDis = that.disArr[0]; that.cssStyle = 'transform: translateX(-'+ that.cssDis +'px);transition: all 10s linear;'; that.nowTime = setInterval(function(){ that.cssStyle = 'transform: translateX(0);'; that.disArr.push(that.disArr.shift()) that.sendVal.push(that.sendVal.shift()) that.cssDis = that.disArr[0]; //必须等到dom更新完毕才设置内容
// #ifdef H5
that.$nextTick(function(){ that.cssStyle = 'transform: translateX(-'+ that.cssDis +'px);transition: all 10s linear;'; }) // #endif
// #ifdef MP-BAIDU
swan.nextTick(function(){ that.cssStyle = 'transform: translateX(-'+ that.cssDis +'px);transition: all 10s linear;'; }) // #endif
// #ifdef MP-WEIXIN
wx.nextTick(function(){ that.cssStyle = 'transform: translateX(-'+ that.cssDis +'px);transition: all 10s linear;'; }) // #endif
},10000) } } } </script>
<style lang="scss" scoped>
/** * @include的公共样式都在根目录下的uni.scss中 */ .my-outbox{ color: #D7BC8D; overflow: hidden; height: 70upx; .my-inbox{ white-space: nowrap; .my-list{ margin-right: 25px;//不要转换成upx display: inline-block; font-size: 26upx; @include lineH(70upx); .my-uname{ color: #FF8900;
} } } } </style>
(1) 换了种思路,这样就是每隔 10s 才去更新 data里面的数据,并且移动的过程也是使用 css3 transition 进行过分,保证了流畅性。若是想移动快一点,就修改transition的时间,不过也不要忘了修改对应settinterval的时间哦。
(2) 若是用 更新前的那种方法,在小程序端会有明显的卡顿,而且内存占用比较高。
(3) 样式无变化,主要就是 js 进行了改变
(3) @include lineH(70upx) 是 height:70upx;line-height:70upx;
(4) 上下滚动的改变也是同样的思路。