动手吧,vue移动端消息滚动组件

先看效果图:

1、模板部分

html 复制代码
<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部分

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部分

javascript 复制代码
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、参数和事件

可以的话,点个赞嘛

javascript 复制代码
## 属性
 
content: 内容。参数类型:string|array
 
touchStop: 触摸是否停止滚动。参数类型:boolean, 默认 false

touchStopDuration: 触摸停止滚动持续时长(ms)。参数类型:number, 默认1500

showCloseIcon:是否显示关闭按钮。参数类型:boolean, 默认 true
 
## 事件
 
close:关闭事件
相关推荐
布瑞泽的童话1 分钟前
无需切换平台?TuneFree如何搜罗所有你爱的音乐
前端·vue.js·后端·开源
白鹭凡14 分钟前
react 甘特图之旅
前端·react.js·甘特图
2401_8628867818 分钟前
蓝禾,汤臣倍健,三七互娱,得物,顺丰,快手,游卡,oppo,康冠科技,途游游戏,埃科光电25秋招内推
前端·c++·python·算法·游戏
书中自有妍如玉25 分钟前
layui时间选择器选择周 日月季度年
前端·javascript·layui
Riesenzahn26 分钟前
canvas生成图片有没有跨域问题?如果有如何解决?
前端·javascript
f89790707028 分钟前
layui 可以使点击图片放大
前端·javascript·layui
小贵子的博客29 分钟前
ElementUI 用span-method实现循环el-table组件的合并行功能
javascript·vue.js·elementui
忘不了情1 小时前
左键选择v-html绑定的文本内容,松开鼠标后出现复制弹窗
前端·javascript·html
码上飞扬1 小时前
前端框架对比选择:如何在众多技术中找到最适合你的
vue.js·前端框架·react·angular·svelte