By Noxxxx from https://www.noxxxx.com/?post_type=post&p=682
欢迎分享与聚合,尊重版权,可以联系授权
使用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标签开始移动,这个值就可以直接设定了。
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(); } }
<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>
.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; } } } }