今天介紹一下,上下,左右無縫滾動的公告欄信息組件的開發。
首先上效果:
看起來有點卡頓,實際上還是挺順暢的。。。
代碼:
左右滾動的組件:marqueeX
<template> <div class="my-outbox"> <div class="my-inbox" ref='box'> <div class="my-list" v-for="(item,index) in sendVal" :key='index'> {{item.place}}<span class="my-uname">{{item.name}}</span>剛剛購買了產品 </div> <div class="my-list" v-for="(item,index) in sendVal" :key='(index+1)*100'> {{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 {} }, mounted:function(){ var that = this; var target = this.$refs.box; var initLeft = 0; setInterval(function(){ initLeft ++; if(initLeft >= target.offsetWidth / 2 ){ initLeft = 0; } target.style = 'transform: translateX(-'+ initLeft +'px)'; },16) } } </script> <style lang="less" scoped> .my-outbox{ color: #D7BC8D; overflow: hidden; height: 35px; background: #422b02; position: relative; .my-inbox{ white-space: nowrap; position: absolute; font-size: 0; .my-list{ margin-right: 25px; display: inline-block; font-size: 13px; height: 35px; line-height: 35px; .my-uname{ color: #FF8900; } } } } </style>
上線滾動的組件:marqueeY
<template> <div class="my-outbox" ref='outbox'> <div class="my-inbox" ref='movebox'> <div class="my-listbox" v-for="(item,index) in sendVal" :key='index'> <div class="my-title"> {{item.name}}<text class="my-utel">{{item.mobile}}</text> <text class="my-addr">{{item.place}}</text> </div> <div class="my-content">{{item.content}}</div> </div> <div class="my-listbox" v-for="(item,index) in sendVal" :key='(index+1)*100' v-if='isShow'> <div class="my-title"> {{item.name}}<text class="my-utel">{{item.mobile}}</text> <text class="my-addr">{{item.place}}</text> </div> <div class="my-content">{{item.content}}</div> </div> </div> </div> </template> <script> export default { name:'my-marquee-top', props:{ sendVal:Array }, data() { return { isShow:true } }, mounted:function(){ var moveTarget = this.$refs.movebox; var outbox = this.$refs.outbox; if(outbox.offsetHeight > (moveTarget.offsetHeight /2)){ this.isShow = false; return } var initTop = 0; setInterval(function(){ initTop ++; if(initTop >= moveTarget.offsetHeight / 2 ){ initTop = 0; } moveTarget.style = 'transform: translateY(-'+ initTop +'px)'; },16) }, } </script> <style lang="less" scoped> .my-outbox{ color: #FEE7B1; height:300px; margin: 20px; background: #FEE7B1; overflow: hidden; border: 2px solid #C46302; .my-inbox{ margin: 0 45px; .my-listbox{ padding: 20px 0; font-size: 18px; border-bottom: 1px solid #C7BEB1; .my-title{ color: #DB7000; margin-bottom: 10px; clear: both; overflow:hidden; .my-utel{ font-size: 16px; margin-left: 20px; } .my-addr{ font-size: 16px; float: right; } } .my-content{ text-align: justify; word-break: break-all; font-size: 14px; color: #53565D; } } } } </style>
使用組件:
<marqueeX send-val='data1' v-if='data1' /> <marqueeY send-val='data2' v-if='data2' />
測試數據:

data: {
"msg": [{
"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": "黃女士"
}, {
"place": "來自天津市的",
"name": "金先生"
}, {
"place": "來自紹興市的",
"name": "韓先生"
}, {
"place": "來自西安市的",
"name": "褚先生"
}, {
"place": "來自蘇州市的",
"name": "姜先生"
}, {
"place": "來自南寧市的",
"name": "陳女士"
}, {
"place": "來自沈陽市的",
"name": "華先生"
}],
"comment": [{
"name": "馮先生",
"mobile": "170****8441",
"place": "重慶",
"content": "從小跟着奶奶長大,她信了一輩子的佛了,我從以前的出了事都會拜佛到今天的每日一拜,已經是一種信仰了,只要多積德行善,下一世咱還是條好漢。"
}, {
"name": "姜女士",
"mobile": "180****2717",
"place": "包頭",
"content": "第三世的我是只萌萌的小白兔,果然兔兔那么可愛,千萬不能吃兔兔。╮(๑•́ ₃•̀๑)╭"
}, {
"name": "曹女士",
"mobile": "171****5347",
"place": "遼陽",
"content": "稀里糊塗點進來,還以為跟三生三世十里桃花一個性質的電視劇呢0.0,不過這個真的好准,我小時候和青年時代算的就跟結果說的一模一樣。"
}, {
"name": "魏先生",
"mobile": "151****3555",
"place": "無錫",
"content": "以前總聽說因果循環報應不爽,但一直不明白是啥意思,看了我前四世的身份后我終於明白了,真實,簡直太真實了。"
}]
}
(1) 上下滾動和左右滾動的 js 代碼 基本沒什么區別,只是我在上下滾動的代碼中加入了判斷如果數據太短就不顯示克隆的內容和不執行滾動事件
(2) 大致的思路就是讓裝內容的盒子一直滾動至內容底部,然后在大於或者等於這個位置的時候,就把當前滾動的位置 置為0,相當於初始化。
(3) 引用組件的時候,使用 v-if 是因為我用setTimeout模擬后台延時返回數據,然后在數據沒拿到的時候就不顯示該組件。
(4) 因為有克隆的內容部分,所以那部分也需要設置key,一定要注意同一組件中,key不要搞成一樣。
更新:
使用 requestAnimationFrame 來實現一幀一幀的繪制移動的距離:
只更新 marqueeX 的實現方法,marqueeY實現方式是一樣的:
export default { name:'my-marquee-left', props:{ sendVal:Array }, data() { return { initLeft:0, animateTarget:null } }, mounted:function(){ this.animateTarget = this.$refs.box; requestAnimationFrame(this.animateFn); }, methods:{ animateFn(){ this.initLeft++; if (this.initLeft >= this.animateTarget.offsetWidth / 2) { this.initLeft = 0; } this.animateTarget.style = "transform: translateX(-" + this.initLeft + "px)"; requestAnimationFrame(this.animateFn) } } }