使用RequestAnimationFrame,核心部分就是利用transformX
实现位移
Js 逻辑写的比较挫,还要想想怎么改进,或者有更好的思路。
marquee的要求是两段文字的间隔能人为的控制,所以用了两个重复的p标签。
然后就是如何计算第一个p标签和第二个p标签移出了可视区域,可视区域不一定是屏幕宽度,可能是一个div设置了width
和overflow
实现的。
另外就是从左往右以及从右往左的区别。
从右往左
一开始p标签的位置是 position:absolute; left: 0
,也就是在‘可视区域’的左边,从右往左就是移动负值。
如何判断移出‘可视区域’? 利用倍数来计算,
实际文字的宽度 / 可视区域的宽度
得到3、3.5、4之类的一个倍数,用这个倍数和目前正在变化时拿到的translateX的值 / 可视区域的宽度
假设是3倍,那么第二步计算出的值如果正好是3,说明文字的末尾已经出现在‘可视区域’,此时➕一个系数x,就可以实现两段文字的间隔(x按照实际想要的间隔自行设置)。
此时第一段文字 间隔 已经全部出现在可视区域了,接下来就要让第二段文字开始移动。第二段文字的起始位置就是‘可视区域’的宽度。
然后判断文字全部移出‘可视区域’判断 第二步骤的 倍数 - 第一步的倍数 < 一个允许范围的误差值
即可。
从左往右
同样的思路,从左往右,刚开始第一个p标签也是位于 left: 0
的位置。
从左往右比较简单,translateX 移动位置 >= '可视区域宽度'
即为移出可视区域,然后将第一个p标签的 translateX
设置为 -自身宽度
即成为了从左边出来的元素。
两个p标签间隔多少就看,第一个p标签开始移动多少距离后让第二个p标签开始移动,这个值就可以直接设定了。
代码语言:javascript复制 export default {
data() {
return {
x1: 0,
x2: 0,
textWidth: 'auto',
raf: '',
timeout: '',
currentDirection: true
}
},
watch: {
during: function () {
this.marquee();
},
content: function () {
this.marquee();
},
width: function () {
this.marquee();
},
fontsize: function () {
this.marquee();
},
direction: function (newVal) {
switch (newVal) {
case 'right-left':
this.currentDirection = true;
break;
case 'left-right':
this.currentDirection = false;
break;
}
this.marquee();
}
},
methods: {
debounce: function (func, wait, immediate) {
let timeout;
return function () {
let context = this, args = arguments;
let later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
},
marquee: function () {
this.debounce(() => {
this.$nextTick(() => {
this.animation();
})
}, 1000, true)()
},
animation: function () {
window.cancelAnimationFrame(this.raf);
this.x1 = 0;
let wrap = this.width > this.$el.clientWidth ? this.$el.clientWidth : this.$refs.wrap.clientWidth;
let textWidth = this.$refs.content.clientWidth;
this.textWidth = textWidth 'px';
if (textWidth < wrap) {
this.textWidth = wrap 50 'px';
textWidth = wrap 50;
}
let originText2 = this.currentDirection ? wrap : (textWidth) * -1 - 20;
this.x2 = originText2;
let flag1 = true;
let flag2 = false;
let distance = textWidth / wrap;
let current1 = 0;
let current2 = 0;
let test = () => {
flag1 && (this.x1 = this.currentDirection ? this.x1 - this.during : this.x1 this.during);
flag2 && (this.x2 = this.currentDirection ? this.x2 - this.during : this.x2 this.during);
if (this.currentDirection) {
current1 = Math.abs(this.x1 / wrap);
current2 = Math.abs(this.x2 / wrap);
Math.abs(current1 - distance 0.9) <= 0.05 && (flag2 = true); //启动第二个
Math.abs(current2 - distance 0.9) <= 0.05 && (flag1 = true);//启动第1个
if (Math.abs(current1 - distance) <= 0.02) { //第一个超出屏幕
this.x1 = wrap; //回到起始位置
flag1 = false;
}
if (Math.abs(current2 - distance) <= 0.02) { //第2个超出屏幕
this.x2 = wrap;
flag2 = false;
}
} else {
(this.x1 > 0 && this.x1 <= this.during) && (flag2 = true);
(this.x2 > 0 && this.x2 <= this.during) && (flag1 = true);
if (this.x1 >= wrap) {
this.x1 = originText2;
flag1 = false;
}
if (this.x2 >= wrap) {
this.x2 = originText2;
flag2 = false
}
}
this.raf = window.requestAnimationFrame(test);
};
this.raf = window.requestAnimationFrame(test)
}
},
mounted() {
this.marquee();
}
}
代码语言:javascript复制<div class="scrolling-text">
<a class="scrolling-text-wrap" v-href="href" ref="wrap" :style="{
'font-size':FONTSIZE 'rem',
color:color,
width:WIDTH 'rem',
margin:TOP 'rem 0 0 ' LEFT 'rem'
}">
<p :style="{
transform:'translateX(' x1 'px)',
'width': textWidth
}">
<span class="text-content" v-text="content" ref="content"></span>
</p>
<p :style="{
transform:'translateX(' x2 'px)',
'width': textWidth
}">
<span class="text-content" v-text="content"></span>
</p>
</a>
</div>
代码语言:javascript复制.scrolling-text {
position: relative;
width: 100%;
height: 100%;
text-align: center;
overflow: hidden;
font-size: 0;
z-index: 5;
.scrolling-text-wrap {
position: relative;
display: inline-block;
white-space: nowrap;
overflow: hidden;
&:before {
content: 'M';
visibility: hidden;
}
p {
position: absolute;
left: 0;
top: 0;
display: inline-block;
font-size: inherit;
color: inherit;
white-space: nowrap;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-direction: initial;
animation-fill-mode: backwards;
animation-play-state: initial;
span {
display: inline-block;
}
}
}
}