zoukankan      html  css  js  c++  java
  • 上滑跑马灯

    前言

    产品说,我们做一个转盘活动吧,需要轮播中奖信息。 当然这需求完全没有问题。
    产品说,你听我说完。

    1. 是从下往上轮播
    2. 如何数据没有更新,就反复轮播。
    3. 如果数据有更新,要无缝更新。
    4. 进入时间1s,暂停1S,出去时间1s.

    没问题吧。
    额, 等等,没多大问题。 那个谁,这个任务教你啦。

    方案

    然后,我的同事开始搜罗实现方案,很多都是匀速走的。
    同事甚至和产品讨论要不要换成跑马灯,嘻嘻, 开玩笑。
    说个笑话,csdn上有不少的这样代码,但是下载要积分,我可以说,日了*狗么!。

    一个下午天气晴,有凉风,心情还好。 于是花了一点时间思考了一种方案。

    关于移动端动画,无非是纯js控制,js Animation API(兼容不理想), css动画,canvas, webgl以及杂交方案。 关于本需求,前两种应该比较适合,成本低,容易实现。

    纯js实现控制比较传统的方案,要启用定时器setTimeout/setInterval/requestAnimation等等,我很烦这。

    采用css3 + js杂交方案,有戏靠谱。 既然有三个阶段,那么我就把你拆成三段动画, 随你去配置,随你去high。 当然你也可以用一段动画,通过设置来控制距离。

    整体的思路

    1. 我把每个需要滚动的每个元素独立起来,每个元素有三段动画, in , pause , out. 怎么衔接, 通过animationend事件。
    2. 那么不同元素又怎么衔接,通过animationDelay来延时启动动画, 启动后依旧是走上面的三段动画。
    3. 怎么轮回播放,当然你可以利用原来的节点,重新修改属性,设置延时启动。 我这里采用比较简单的,直接删除了原有,然后重新创建。 当然重新创建是有讲究的,你有很多选择,只要控制好衔接的事件,我这里是在最后一个节点开始第一阶段运动的时候,重新创建新节点,最后节点第三阶段运动结束,清除之前运动完毕的节点
    4. 关于无缝更新,当然要让最后一个运动的元素运动完, 所以我在第二个阶段 pause阶段执行新的节点创建,并设置好相关的延时。
    5. 关于暂停能力, animationPlayState提供这个能力,paused和running。 暂停的手,animationDelay也会停止计时,非常的棒。 因为懒,只实现了PC的暂停能力。移动端嘛,添加touch,tap,pointer事件就好。 因为懒,所以。

    改进

    1. 每个元素三个动画,是有点消耗。可以用一个大的容器放好所有的元素,然后animationPlayState来控制,蛮不错的。 如何控制每个阶段的计时呢,当然可以用js,我有个想法,就是起一个三段动画,在这个事件里面去控制animationPlayState。
    2. 节点也没回收利用,是有点浪费。
    3. 移动端的支持呢?
    4. 不太重要的其他......

    效果呢?

    怎么使用呢?

            const contents = [
                "队列1:春天 - first",
                "队列1:夏天",
                "队列1:秋天",
                "队列1:冬天",
                "队列1:夏夏湉",
                "队列1:求七天",
                "队列1:Who are You - last"
            ];
            const contents2 = [
                "队列2:这是怎么回事 - first",
                "队列2:谁是最可赖的人",
                "队列2:壮士一去不复返",
                "队列2:谁来拯救你",
                "队列2:家福乐团购有没有 - last"
            ]
    
            const el = document.querySelector("#box");
    
            let upSlide = new UpSlide({
                el
            });
            upSlide.start(contents);
    
            document.getElementById('btnChange').addEventListener("click", () => {
                upSlide.start(contents);
            })
    
            document.getElementById('btnChange2').addEventListener("click", () => {
                upSlide.start(contents2);
            })
    

    源码呢?

    等等这个有点用, 源码呢
    上滑跑马灯源码

    再贴出源码,这样文章长一点

    const DEFAULT_OPTION = {
        inTime: 1000,
        pauseTime: 1500,
        outTime: 1000,
        className: "upslide-item",
        animationClass: "upslide-item-animation",
        animationInClass: "slideup-animation-in",
        animationPauseClass: "slideup-animation-pause",
        animationOutClass: "slideup-animation-out",
        pauseOnFocus: false
    };
    
    const DELETING_CLASS_NAME = "__deleting__";
    
    function clearSiblings(el) {
        const parent = el.parentElement;
        // 移除前面节点
        while (el.previousElementSibling) {
            parent.removeChild(el.previousElementSibling);
        }
        // 移除后面的节点
        while (el.nextElementSibling) {
            parent.removeChild(el.nextElementSibling);
        }
    }
    
    class UpSlide {
        constructor(options) {
            this.el = options.el;
            this.options = Object.assign({}, DEFAULT_OPTION, options);
            this.changeStatus = 0;
            this.currentContents = null;
    
            const { inTime, pauseTime, outTime } = this.options;
            this.totalTime = inTime + pauseTime + outTime;
            this.inPausePercent = (inTime + pauseTime) / this.totalTime;
            this.animationstartEvent = this.animationstartEvent.bind(this);
            this.animationendEvent = this.animationendEvent.bind(this);
            this.mouseenterEvent = this.mouseenterEvent.bind(this);
            this.mouseleaveEvent = this.mouseleaveEvent.bind(this);
            this.init();
        }
    
        createItems(datas, baseDelay = 0) {
            const { className, animationInClass, animationClass, inTime } = this.options;
            const { totalTime, inPausePercent } = this;
            const fragment = document.createDocumentFragment();
            datas.forEach((c, i) => {
                const newEl = document.createElement("div");
                newEl.dataset.isLast = i === datas.length - 1 ? 1 : 0;
                newEl.innerText = c;
                newEl.className = className + " " + animationClass;
                newEl.style.animationName = animationInClass;
                newEl.style.animationDelay = baseDelay + i * totalTime * inPausePercent + "ms";
                newEl.style.animationDuration = inTime + "ms";
                fragment.appendChild(newEl);
            });
            return fragment;
        }
    
        animationstartEvent(e) {
            const { totalTime, inPausePercent } = this;
            const { animationInClass } = this.options;
            // 开启新的轮回
            if (e.animationName === animationInClass && e.target.dataset.isLast == 1) {
                this.innerStart(this.currentContents, totalTime * inPausePercent);
            }
        }
    
        animationendEvent(e) {
            const {
                animationInClass,
                animationPauseClass,
                animationOutClass,
                className,
                animationClass,
                pauseTime,
                outTime
            } = this.options;
    
            const { changeStatus } = this;
            const el = e.target;
            const parent = el.parentElement;
            const animationName = e.animationName;
    
            switch (animationName) {
                case animationInClass:
                    el.style.animationName = animationPauseClass;
                    el.style.animationDuration = pauseTime + "ms";
                    el.style.animationDelay = "0ms";
                    break;
                case animationPauseClass:
                    el.style.animationName = animationOutClass;
                    el.style.animationDuration = outTime + "ms";
                    el.style.animationDelay = "0ms";
    
                    // 切换
                    if (changeStatus === 1) {
                        clearSiblings(el);
                        // 标记
                        el.classList.add(DELETING_CLASS_NAME);
                        // 切换
                        this.innerStart(this.currentContents, 0);
                        this.changeStatus = 0;
                    }
                    break;
                case animationOutClass:
                    e.target.classList.remove(animationClass);
                    e.target.style.animationDelay = "";
    
                    if (el.classList.contains(DELETING_CLASS_NAME)) {
                        parent.removeChild(el);
                    }
                    // 轮回结束-清除节点
                    if (e.target.dataset.isLast == 1) {
                        const parent = e.target.parentElement;
                        const delItems = parent.querySelectorAll(
                            `.${className}:not(.${animationClass})`
                        );
                        if (delItems.length > 0) {
                            for (let i = delItems.length - 1; i >= 0; i--) {
                                parent.removeChild(delItems[i]);
                            }
                        }
                    }
                    break;
                default:
                    break;
            }
        }
    
        mouseenterEvent() {
            const { className } = this.options;
            this.el.querySelectorAll("." + className).forEach(el => {
                el.style.animationPlayState = "paused";
            });
        }
    
        mouseleaveEvent() {
            const { className } = this.options;
            this.el.querySelectorAll("." + className).forEach(el => {
                el.style.animationPlayState = "running";
            });
        }
    
        init() {
            const { el } = this;
            el.addEventListener("animationstart", this.animationstartEvent);
            el.addEventListener("animationend", this.animationendEvent);
            const { pauseOnFocus } = this.options;
            if (pauseOnFocus === true) {
                el.addEventListener("mouseenter", this.mouseenterEvent);
                el.addEventListener("mouseleave", this.mouseleaveEvent);
            }
        }
    
        innerStart(content, delay = 0) {
            this.currentContents = content;
            const c = this.createItems(content, delay);
            this.el.appendChild(c);
        }
    
        start(content, delay = 0) {
            if (this.currentContents != null) {
                this.changeStatus = 1;
                this.currentContents = content;
                return;
            }
            this.innerStart(content, delay);
        }
    
        destroy() {
            this.el.removeEventListener("animationstart", this.animationstartEvent);
            this.el.removeEventListener("animationend", this.animationendEvent);
            const { pauseOnFocus } = this.options;
            if (pauseOnFocus === true) {
                el.removeEventListener("mouseenter", this.mouseoverEvent);
                el.removeEventListener("mouseleave", this.mouseleaveEvent);
            }
            this.el.innerHTML = null;
            this.el = null;
            this.options = null;
        }
    }
    
    
  • 相关阅读:
    漫画 | 一台Linux服务器最多能支撑多少个TCP连接?
    frida hook pthread_create
    mac搜索so中的字符串
    字节字符串和数字互转
    twsited使用延迟项,并发量限制
    常见汇编指令和EFLAGS寄存器对应位的含义
    scrapy设置cookie的三种方式
    G1GC 读书笔记
    SLF4J 适配图
    支付宝和微信提现有办法免费了!
  • 原文地址:https://www.cnblogs.com/cloud-/p/11402598.html
Copyright © 2011-2022 走看看