svg loading 图标使用 animation 抖动解决方案

原创
前端路迹
2024-7-17 08:00
编辑于 2024-7-19 09:01

最近发现了一个 loading 动画的抖动现象,如下图所示,可以明显看到鼠标移到按钮上后,loading动画没有沿着中心点旋转

动画采用了标准的animation方案,图标是用的 svg。

.ts-button-spinner {
    transform-origin: center center;
    animation: ts-button-spin 1s infinite linear;
}

@keyframes ts-button-spin {
    from {
        transform: rotate(0);
    }
    to {
        transform: rotate(360deg);
    }
}

排查过程

第一想法是在项目里面建一个空页面测试,结果发现是正常的,那说明可能是由项目的其他代码影响了,只能在原抖动的页面去一个一个排除,看是哪个代码影响了,最终发现竟然是由引入的第三方 sortablejs 库引起的。

import Sortable from "sortablejs"

发现 sortablejs 引入页面,即使不去使用它也会触发抖动,那说明这个库一定是做了全局初始化之类的动作,我第一直觉是去去调试工具看下这个svg元素的事件绑定,结果真的发现 sortablejs 有给 document 绑定事件。

于是在它的源码中挨着查找,定位到这个绑定事件的代码。

去掉这段代码loading就不会抖动了,给document绑定一个事件就会导致动画抖动也是非常奇怪,仔细再探究是哪个点影响了,发现是这个绑定事件的配置。

于是我在页面去掉 sortablejs 的引入,自己写一个document的事件绑定来验证,结果发现真的也能导致动画出现抖动。

document.addEventListener('touchmove',()=>{},{
    passive:false,
    capture:false
})

再接着排查是那个参数导致的,最终发现只有 passive 设为false 时会触发动画的抖动。
由于整个文件在Vue工程中,为了排除其它因素,将上述动画放到一个html文件中来验证。

<body>
    <style>
        .ts-button-spinner {
            user-select: none;
            transform-origin: center center;
            animation: ts-button-spin 1s infinite linear;
        }

        @keyframes ts-button-spin {
            from {
                transform: rotate(0);
            }
            to {
                transform: rotate(360deg);
            }
        }

        button{
            display: inline-flex;
            align-items: center;
            color: #000;
            width: 200px;
            font-size: 32px;
        }
    </style>
    <div>
        <button>
            <svg 
                viewBox="0 0 48 48"
                draggable="false"
                xmlns="http://www.w3.org/2000/svg"
                class="ts-button-spinner "
                width="1em"
                height="1em"
            >
                <path
                    d="M24 4a20 20 0 1 1-20 20"
                    stroke-width="3"
                    fill="none"
                    stroke="currentColor"
                    stroke-linecap="round"
                >
                </path>
            </svg>
            按钮
        </button>
    </div>

    <script>
        document.addEventListener("touchmove", () => {}, {
            passive: false,
            capture: true,
        })
    </script>
</body>

放到html中,基本上排除了其它因素的影响。测试发现鼠标移到按钮上很容易就触发抖动,说明这个问题可能真的是存在的。

那么如何解决这个问题呢?只能接着继续在这个html排查,发现将button换成div就不会出现抖动,如果不想将button换成div,经过测试将动画换成 svg 的 animateTransform 也不会触发抖动。

<svg 
                viewBox="0 0 48 48"
                draggable="false"
                xmlns="http://www.w3.org/2000/svg"
                width="1em"
                height="1em"
            >
                <path
                    d="M24 4a20 20 0 1 1-20 20"
                    stroke-width="3"
                    fill="none"
                    stroke="currentColor"
                    stroke-linecap="round"
                >
                <animateTransform
                        attributeName="transform"
                        type="rotate"
                        from="0 24 24"
                        to="360 24 24"
                        dur="1s"
                        repeatCount="indefinite"
                    /> 
                </path>
            </svg>

以上是这个CSS动画抖动的排查过程,前端开发经常会碰到类似问题,有很多问题大概率是不能直接搜索到答案的,只能自己去一步一步排查,排查的过程要学会利用方法,特别是影响因素非常多的时候,要学会用排除法,只有找到问题的根源,才能对症下药。

上述问题目前也没找到真正的原因,目前也只是找到了2个解决方案,后续还会继续探究。

转载请注明出处。本文地址: https://www.qinshenxue.com/article/svg-loading-uses-the-animation-jitter-solution.html
关注我的公众号