先看效果图:
1、模板部分
<transition name="fade-sport">
<div class="v-message-roll" v-show="visible">
<svg class="v-icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7405">
<path
d="M594.917478 144.398738c0-43.803645-37.123502-79.478146-82.916455-79.478146-45.699832 0-82.918501 35.585473-82.918501 79.478146 0 4.472871 0.38681 8.860808 1.12973 13.133112-114.497731 38.254256-208.430076 155.73083-208.430076 294.718325l0 109.423155c0 0 0 157.590178-40.387849 158.963455-24.082488 0-42.528606 17.792225-42.528606 39.74112 0 22.098297 18.557658 39.737026 41.452087 39.737026l663.36643 0c23.004947 0 41.451064-17.791202 41.451064-39.737026 0-22.103414-18.557658-39.74112-41.451064-39.74112-41.46539 0-41.46539-157.854191-41.46539-157.854191L802.218848 452.263477c0-139.166573-87.828324-256.691243-208.408587-294.828842C594.538855 153.190985 594.917478 148.838863 594.917478 144.398738zM636.377752 839.789535c-0.12382 65.903989-55.286164 119.28885-124.377752 119.28885-68.616774 0-124.254955-53.163827-124.378775-119.28885L636.377752 839.789535z"
fill="#f9a60a"
p-id="7406"
></path>
</svg>
<div class="v-content">
<ul class="v-content-list" ref="listRef" @touchstart="touchstart" @touchend="touchend">
<li class="v-content-item" ref="itemsRef" v-for="(item, index) in contentComputed" :key="index">
{{ item }}
</li>
</ul>
</div>
<svg
v-if="showCloseIcon"
@click="close"
class="v-icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="8743"
>
<path
d="M576 512l277.333333 277.333333-64 64-277.333333-277.333333L234.666667 853.333333 170.666667 789.333333l277.333333-277.333333L170.666667 234.666667 234.666667 170.666667l277.333333 277.333333L789.333333 170.666667 853.333333 234.666667 576 512z"
fill="#f9a60a"
p-id="8744"
></path>
</svg>
</div>
</transition>
2、css部分
.v-message-roll {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
transition: opacity 0.2s;
.v-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
vertical-align: middle;
fill: currentColor;
overflow: hidden;
}
.v-content {
flex: 1;
width: 0;
.v-content-list {
width: 100%;
overflow-x: auto;
white-space: nowrap;
&::-webkit-scrollbar {
display: none;
}
.v-content-item {
display: inline-block;
color: #db8412;
}
}
}
}
.fade-sport-enter,
.fade-sport-leave {
opacity: 0;
}
3、js部分
const SPEED = {
FAST: 0.6,
MIDDLE: 0.4,
SLOW: 0.3,
};
export default {
data() {
return {
visible: true,
animationFrameTimer: null,
touchTimeout: null,
refreshRateTimer: null,
};
},
props: {
content: [String, Array],
touchStop: {
type: Boolean,
default: false,
},
touchStopDuration: {
type: Number,
default: 1500,
},
showCloseIcon: {
type: Boolean,
default: true,
},
},
watch: {
content: {
handler() {
this.reset();
this.$nextTick(() => {
this.init();
});
},
deep: true,
},
},
computed: {
contentComputed() {
if (Object.prototype.toString.call(this.content) === "[object Array]") {
return this.content;
}
return [this.content];
},
},
mounted() {
this.calcScreenRefreshRate().then((rate) => {
this.screenRefreshRate = rate;
this.init();
});
},
methods: {
init() {
const itemsRef = this.$refs.itemsRef;
const scrollW = this.$refs.listRef.scrollWidth;
this.scrollW = scrollW;
if (itemsRef.length) {
this.itemsRef = this.$refs.itemsRef;
this.setInterval();
}
},
reset() {
this.$refs.listRef.scrollLeft = 0;
this.cancelAnimationFrame();
},
calcScreenRefreshRate() {
return new Promise((resolve) => {
let screenRefreshRate = 0;
const animationFrameFunc = () => {
screenRefreshRate += 2;
this.refreshRateTimer = window.requestAnimationFrame(animationFrameFunc);
};
setTimeout(() => {
window.cancelAnimationFrame(animationFrameFunc);
resolve(screenRefreshRate);
}, 500);
animationFrameFunc();
});
},
setInterval(num) {
this.animationFrameTimer = window.requestAnimationFrame(() => this.roll(num));
},
roll(num = 0) {
const step = this.calcStep();
this.itemsRef.forEach((ele) => {
ele.style.transform = `translateX(-${num}px)`;
});
this.animationFrameTimer = window.requestAnimationFrame(() =>
this.roll(num < this.scrollW ? num + step : 0)
);
},
calcStep() {
const screenRefreshRate = this.screenRefreshRate;
let step = 0;
if (screenRefreshRate < 90) {
step = SPEED["FAST"];
} else if (screenRefreshRate < 120) {
step = SPEED["MIDDLE"];
} else {
step = SPEED["SLOW"];
}
return step;
},
touchstart() {
if (!this.touchStop) {
this.$refs.listRef.style.overflow = "hidden";
} else {
this.itemsRef.forEach((ele) => {
ele.style.transform = `translateX(0)`;
});
this.cancelAnimationFrame();
window.clearTimeout(this.touchTimeout);
}
},
touchend() {
if (this.touchStop) {
const scrollLeft = this.$refs.listRef.scrollLeft;
this.touchTimeout = setTimeout(() => {
this.$refs.listRef.scrollLeft = 0;
this.setInterval(scrollLeft);
}, this.touchStopDuration);
}
},
close() {
this.visible = false;
this.$emit("close");
this.cancelAnimationFrame();
},
cancelAnimationFrame() {
window.cancelAnimationFrame(this.animationFrameTimer);
window.cancelAnimationFrame(this.refreshRateTimer);
},
},
deactivated() {
this.cancelAnimationFrame();
},
beforeDestroy() {
this.cancelAnimationFrame();
},
};
4、参数和事件
可以的话,点个赞嘛
## 属性
content: 内容。参数类型:string|array
touchStop: 触摸是否停止滚动。参数类型:boolean, 默认 false
touchStopDuration: 触摸停止滚动持续时长(ms)。参数类型:number, 默认1500
showCloseIcon:是否显示关闭按钮。参数类型:boolean, 默认 true
## 事件
close:关闭事件