我能否检测任意CSS过渡是否已开始

19

在我的关闭函数中,我希望在CSS过渡效果完成后执行所有DOM清理工作。但可能没有任何过渡正在运行/可能是多阶段的(维护样式表不在我的控制范围内)。

我该如何编写类似以下函数的代码:

function close () {
  myEl.removeClass('open');
  if (animation is running/about to be run) {
    // wait for transition to end, then recursively check to see if another 
    // one has started, wait for that ...
    // then
    cleanUpDOM(); 
  } else {
    cleanUpDOM(); 
  }
}

目前我的想法是将初始检查包装在一个timeout/requestAnimationFrame中,以便给动画一个开始的机会,然后检查它是否正在运行。不幸的是,没有transitionstart事件,我不知道如何检查过渡是否已经开始。

编辑 推荐使用jquery的答案与此无关,因为jquery动画是javascript动画,而不是CSS过渡


如果您知道正在进行动画的属性,可以通过将元素的当前属性值与其CSS样式值(也称为目标)进行比较来确定动画是否正在进行中吗? - milks
@milks 通常情况下,我不知道正在过渡的是什么,甚至不知道是否有任何东西正在过渡。 (实际上,最糟糕的情况更加棘手 - 我甚至可能不知道过渡是否在外部元素上进行,例如在叠加层中,内部内容区域可能会缩小到一点,但外部叠加层仍然保持黑色...但现在这种情况并不重要) - wheresrhys
我认为这是 https://dev59.com/lnI95IYBdhLWcg3w7SpZ 的重复。 - Rob M.
这可能会有所帮助:http://blog.teamtreehouse.com/using-jquery-to-detect-when-css3-animations-and-transitions-end - koosa
@milks - 经过思考,你的想法可能可以被改编成为一些东西。我考虑从 getComputedStyle 读取 transition-property,然后获取列出的所有属性的值,然后应用类,然后再次使用 getComputedStyle 并阅读所有属性,以查看它们是否已更改(我甚至不确定规范是否表示如果有转换定义,它们应立即更改,还是在重绘之前?)我还需要考虑当一个错误意味着事件没有触发时的回退。考虑到供应商前缀,这将成为一个庞大的函数。 - wheresrhys
显示剩余3条评论
9个回答

1

我不知道有什么方法可以在不知道正在转换的元素的情况下检测转换是否正在后台工作。

然而,如果你能从转换到关键帧动画上,那么你将会拥有所需的事件-动画开始和动画结束,这样就很容易确定是否有正在运行的动画。


1

关于 transitionStart 和 transitionEnd 事件:

转换不能从无处开始。通常情况下,转换在某些事件之后开始,您通过更改样式或类等方式来更改DOM元素的状态。因此,您知道何时开始转换,因为您在代码中启动它。

在转换期间,用户I / O不会被阻止,因此转换是异步的,当转换结束时,您不知道它是否已经结束。因此,您需要使用transitionEnd事件在JavaScript中执行一些操作。

关于 transitionEnd 事件: 只需查看jsfiddle


5
我在我的 js 代码中没有明确开始它。我在 js 中添加一个类,该类应用于 css 中的某些样式,可能涉及过渡效果,因此我的 js 不知道是否会启动过渡效果。 - wheresrhys
您可以通过Modernizr检查过渡支持(带前缀或不带前缀)。如果支持过渡,您可以检查样式对象的transitionDelaytransitionDurationtransitionProperty,并获取它们的值来确定过渡是否开始。 - Alex
1
你无法确定过渡何时开始,因为它是浏览器执行的异步操作,你不知道何时会发生。请参考:https://dev59.com/N4Xca4cB1Zd3GeqPMrtc - demian85

1

这是我目前的解决方案 - 有点hacky,只有在知道可能转换的元素时才起作用,并且不适用于 transition-property: all...但这是一个有前途的开始

function toCamelStyleProp (str) {
    return str.replace(/(?:\-)([a-z])/gi, function ($0, $1) {
        return $1.toUpperCase();
    });
}

function toHyphenatedStyleProp (str) {
    return str.replace(/([A-Z])/g, function (str,m1) {
        return '-' + m1.toLowerCase();
    }).replace(/^ms-/,'-ms-');
}

function getPrefixedStyleProp (prop) {
    prop = toCamelStyleProp(prop);
    prop = Modernizr.prefixed(prop);
    return toHyphenatedStyleProp(prop);
}

function getStyleProperty (el, prop) {
    return getComputedStyle(el,null).getPropertyValue(getPrefixedStyleProp(prop));
}

function doAfterTransition ($wrapper, cssClass, mode, $transitioningEl, callback) {
    $transitioningEl = $transitioningEl || $wrapper;

    var transitioningEl = $transitioningEl[0],
        duration = +getStyleProperty(transitioningEl, 'transition-duration').replace(/[^\.\d]/g, ''),
        transitioners = getStyleProperty(transitioningEl, 'transition-property').split(' '),
        initialState = [],
        changedState = [],
        i,
        callbackHasRun = false,

        //makes sure callback doesn't get called twice by accident
        singletonCallback = function () {
            if (!callbackHasRun) {
                callbackHasRun = true;
                callback();
            }
        };

    // if no transition defined just call the callback
    if (duration === 0) {
        $wrapper[mode + 'Class'](cssClass);
        callback();
        return;
    }

    for (i = transitioners.length - 1;i>=0;i--) {
        initialState.unshift(getStyleProperty(transitioningEl, transitioners[i]));
    }

    $wrapper[mode + 'Class'](cssClass);

    setTimeout(function () {
        for (i = transitioners.length - 1;i>=0;i--) {
            changedState.unshift(getStyleProperty(transitioningEl, transitioners[i]));
        }

        for (i = transitioners.length - 1;i>=0;i--) {
            if (changedState[i] !== initialState[i]) {
                $transitioningEl.transitionEnd(singletonCallback);

                // failsafe in case the transitionEnd event doesn't fire
                setTimeout(singletonCallback, duration * 1000);
                return;
            }
        }
        singletonCallback();
    }, 20);
}

0

0
如果你计划做CSS过渡效果,可以试试jQuery Transit插件http://ricostacruz.com/jquery.transit/
非常强大和有用的工具,例如,你可以使用.css('x')来获取变换后的x坐标值。

0

2
这只能与jQuery的JavaScript动画一起使用 https://github.com/jquery/jquery/blob/master/src/effects/animatedSelector.js - 请看它如何期望在jQuery中有一个计时器。 - wheresrhys

0
这里有一个函数,等待页面 HTML 变得稳定。即当所有动画都完成时。在下面的示例中,它将等待 HTML 在 200 毫秒内保持不变,并设置最长超时时间为 2 秒。
使用以下方式调用该函数...
waitUntilHtmlStable(yourCallback, 200, 2000);

该函数...

waitUntilHtmlStable = function (callback, unchangedDuration, timeout, unchangedElapsed, html) {
    var sleep = 50;
    window.setTimeout(function () {
        var newHtml = document.documentElement.innerHTML;
        if (html != newHtml) unchangedElapsed = 0;
        if (unchangedElapsed < unchangedDuration && timeout > 0)
            waitUntilHtmlStable(callback, unchangedDuration, timeout - interval, unchangedElapsed + interval, newHtml);
        else
            callback();
    }, sleep);
};

在我的情况下,我想确保新元素存在。如果您想跟踪动画移动,请将document.documentElement.innerHTML更改为
JSON.stringify(Array.prototype.slice.call(document.documentElement.getElementsByTagName("*"), 0)
.map(function(e) {
        var x = e;
        var r = x.getBoundingClientRect();
        while (r.width == 0 || r.height == 0) {
            x = x.parentNode;
            r = x.getBoundingClientRect();
        }
        return r;
    }));

你应该使用MutationObserver而不是轮询innerHTML。 - EoghanM

-2

你可以使用Jquery,这会更容易。例如,你可以像这样使用.animate

(function(){
            var box = $('div.box')
            $('button').on('click', function(){
                box.animate({ 'font-size' : '40px'})
                .animate({'color': 'red'});
            })
        })();

或者只需执行回调函数


-2

animation.css 上,我找到了这个。

你也可以检测动画何时结束:

$('#yourElement').one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', doSomething);

点击此处阅读完整文档


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接