一、引言
项目有文本滚动展示的需求,一开始使用marquee标签来实现需求,但是看一下MDN对该标签的描述:
这个标签已经不再推荐使用了,那就自己封装一个类似的组件来用。
二、实现思路
1.如何让文本滚动起来?
给滚动元素设置绝对定位并给他的父元素设置相对定位,定义如:left: a => left: b 的动画来实现滚动效果。
2.组件需要哪些配置?
(1)滚动的方向:上右下左
(2)滚动的速度:这里我以px/s作为单位
(3)是否强制滚动:非强制滚动时若内容未超出容器则不滚动
三、实现过程
1.html
<template>
<!-- 文本滚动 -->
<div class="text-scroll" ref="textScroll">
<div class="content" ref="content" :style="contentStyle">
<!-- 默认插槽,插入滚动内容 -->
<slot></slot>
</div>
</div>
</template>
2.css
<style scoped lang="scss">
.text-scroll {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
.content {
width: fit-content;
height: fit-content;
position: absolute;
}
}
</style>
最外层的div为滚动的可视区,里面的div为滚动区。可视区宽度高度均为100%使用时大小由外部容器决定,overflow设为hidden,防止滚动区滚动出可视区外仍可见。滚动区设置宽高都为fit-content使大小随内容自适应,内部设置插槽使用组件时插入滚动内容。
3.动画
动画设置在滚动区上
需要往上右下左四个不同方向滚动的动画,我们只需要定义上左两个方向另外两个方向直接反转即可。先来看向上滚动的动画怎么写,向上滚动的话滚动区的起点应设置在可视区外正下方,所以动画的起点应为 top: 100%,终点应设置在可视区外正上方应为top: 负的滚动区高度。
向上向左动画即为:
<style lang="scss">
.text-scroll {
.content {
@keyframes up-scroll {
0% {
top: 100%;
}
100% {
top: var(--animation-end);
}
}
@keyframes left-scroll {
0% {
left: 100%;
}
100% {
left: var(--animation-end);
}
}
}
}
</style>
--animation-end这个变量的值是负的滚动区的宽度或高度,具体是宽度还是高度由滚动的方向决定,由于滚动区的宽高不确定,所以需要通过js获取并设置这个变量。
注意:动画样式不能加上scoped,否则不生效!
4.js
(1)组件配置
props: {
/* 滚动方向
* value: up、down、left、right
*/
direction: {
default: "up",
type: String,
},
//滚动速度 单位px/s
speed: {
default: 60,
type: Number,
},
/* 是否强制滚动
* false: 内容未超出时不滚动
*/
isForceScroll: {
default: true,
type: Boolean,
},
},
通过props传入组件配置
(2)设置滚动动画
data() {
return {
contentStyle: {
//滚动动画
"--animation-end": "",
animation: "",
},
};
},
methods: {
//设置滚动动画
setScrollAnimation() {
//获取文本滚动实际显示宽度高度以及滚动内容宽度高度
let textScrollWidth = this.$refs.textScroll.offsetWidth,
textScrollHeight = this.$refs.textScroll.offsetHeight,
contentWidth = this.$refs.content.offsetWidth,
contentHeight = this.$refs.content.offsetHeight;
//不强制滚动判断内容是否超出,未超出则不滚动
if (!this.isForceScroll) {
if (this.direction === "up" || this.direction === "down") {
if (contentHeight <= textScrollHeight) {
this.contentStyle["--animation-end"] = "";
this.contentStyle.animation = "";
return;
}
} else {
if (contentWidth <= textScrollWidth) {
this.contentStyle["--animation-end"] = "";
this.contentStyle.animation = "";
return;
}
}
}
//滚动长度、时间
let scrollLength, time;
//根据滚动方向来设置不同的滚动动画
switch (this.direction) {
case "up":
this.contentStyle["--animation-end"] = `-${contentHeight}px`;
scrollLength = contentHeight + textScrollHeight;
time = scrollLength / this.speed;
this.contentStyle.animation = `up-scroll linear ${time}s infinite`;
break;
case "down":
this.contentStyle["--animation-end"] = `-${contentHeight}px`;
scrollLength = contentHeight + textScrollHeight;
time = scrollLength / this.speed;
this.contentStyle.animation = `up-scroll linear ${time}s infinite reverse`;
break;
case "left":
this.contentStyle["--animation-end"] = `-${contentWidth}px`;
scrollLength = contentWidth + textScrollWidth;
time = scrollLength / this.speed;
this.contentStyle.animation = `left-scroll linear ${time}s infinite`;
break;
case "right":
this.contentStyle["--animation-end"] = `-${contentWidth}px`;
scrollLength = contentWidth + textScrollWidth;
time = scrollLength / this.speed;
this.contentStyle.animation = `left-scroll linear ${time}s infinite reverse`;
break;
}
},
},
scrollLength为一次动画实际滚动的长度,time为一次动画的持续时间。我们可以算出scrollLength应为:可视区的宽或高加上滚动区的宽或高(根据滚动的方向来判断是宽还是高)。time应为 scrollLegnth / 组件配置的滚动速度。
(3)生命周期
在mounted里调用上面的方法初始化组件。在updated里同样调用该方法,当组件宽高改变或插槽内容更新重新设置动画。
mounted() {
this.setScrollAnimation();
},
updated() {
//当插槽内容更新重新设置滚动动画
this.setScrollAnimation();
},
四、在线演示
五、完整代码
<template>
<!-- 文本滚动 -->
<div class="text-scroll" ref="textScroll">
<div class="content" ref="content" :style="contentStyle">
<!-- 默认插槽,插入滚动内容 -->
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "TextScroll",
props: {
/* 滚动方向
* value: up、down、left、right
*/
direction: {
default: "up",
type: String,
},
//滚动速度 单位px/s
speed: {
default: 60,
type: Number,
},
/* 是否强制滚动
* false: 内容未超出时不滚动
*/
isForceScroll: {
default: true,
type: Boolean,
},
},
data() {
return {
contentStyle: {
//滚动动画
"--animation-end": "",
animation: "",
},
};
},
methods: {
//设置滚动动画
setScrollAnimation() {
//获取文本滚动实际显示宽度高度以及滚动内容宽度高度
let textScrollWidth = this.$refs.textScroll.offsetWidth,
textScrollHeight = this.$refs.textScroll.offsetHeight,
contentWidth = this.$refs.content.offsetWidth,
contentHeight = this.$refs.content.offsetHeight;
//不强制滚动判断内容是否超出,未超出则不滚动
if (!this.isForceScroll) {
if (this.direction === "up" || this.direction === "down") {
if (contentHeight <= textScrollHeight) {
this.contentStyle["--animation-end"] = "";
this.contentStyle.animation = "";
return;
}
} else {
if (contentWidth <= textScrollWidth) {
this.contentStyle["--animation-end"] = "";
this.contentStyle.animation = "";
return;
}
}
}
//滚动长度、时间
let scrollLength, time;
//根据滚动方向来设置不同的滚动动画
switch (this.direction) {
case "up":
this.contentStyle["--animation-end"] = `-${contentHeight}px`;
scrollLength = contentHeight + textScrollHeight;
time = scrollLength / this.speed;
this.contentStyle.animation = `up-scroll linear ${time}s infinite`;
break;
case "down":
this.contentStyle["--animation-end"] = `-${contentHeight}px`;
scrollLength = contentHeight + textScrollHeight;
time = scrollLength / this.speed;
this.contentStyle.animation = `up-scroll linear ${time}s infinite reverse`;
break;
case "left":
this.contentStyle["--animation-end"] = `-${contentWidth}px`;
scrollLength = contentWidth + textScrollWidth;
time = scrollLength / this.speed;
this.contentStyle.animation = `left-scroll linear ${time}s infinite`;
break;
case "right":
this.contentStyle["--animation-end"] = `-${contentWidth}px`;
scrollLength = contentWidth + textScrollWidth;
time = scrollLength / this.speed;
this.contentStyle.animation = `left-scroll linear ${time}s infinite reverse`;
break;
}
},
},
mounted() {
this.setScrollAnimation();
},
updated() {
//当插槽内容更新重新设置滚动动画
this.setScrollAnimation();
},
};
</script>
<style scoped lang="scss">
.text-scroll {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
.content {
width: fit-content;
height: fit-content;
position: absolute;
}
}
</style>
<style lang="scss">
.text-scroll {
.content {
@keyframes up-scroll {
0% {
top: 100%;
}
100% {
top: var(--animation-end);
}
}
@keyframes left-scroll {
0% {
left: 100%;
}
100% {
left: var(--animation-end);
}
}
}
}
</style>