简体   繁体   English

为什么 CSS 关键帧动画在具有作用域样式的 Vue 组件中被破坏?

[英]Why are CSS keyframe animations broken in Vue components with scoped styling?

I'm trying to implement a CSS typing indicator in Vue.我正在尝试在 Vue 中实现一个 CSS 类型指示器。 Without Vue, it looks like this:没有 Vue,它看起来像这样:

 .typing-indicator { background-color: #E6E7ED; width: auto; border-radius: 50px; padding: 20px; display: table; margin: 0 auto; position: relative; -webkit-animation: 2s bulge infinite ease-out; animation: 2s bulge infinite ease-out; } .typing-indicator:before, .typing-indicator:after { content: ''; position: absolute; bottom: -2px; left: -2px; height: 20px; width: 20px; border-radius: 50%; background-color: #E6E7ED; } .typing-indicator:after { height: 10px; width: 10px; left: -10px; bottom: -10px; } .typing-indicator span { height: 15px; width: 15px; float: left; margin: 0 1px; background-color: #9E9EA1; display: block; border-radius: 50%; opacity: 0.4; } .typing-indicator span:nth-of-type(1) { -webkit-animation: 1s blink infinite 0.3333s; animation: 1s blink infinite 0.3333s; } .typing-indicator span:nth-of-type(2) { -webkit-animation: 1s blink infinite 0.6666s; animation: 1s blink infinite 0.6666s; } .typing-indicator span:nth-of-type(3) { -webkit-animation: 1s blink infinite 0.9999s; animation: 1s blink infinite 0.9999s; } @-webkit-keyframes blink { 50% { opacity: 1; } } @keyframes blink { 50% { opacity: 1; } } @-webkit-keyframes bulge { 50% { -webkit-transform: scale(1.05); transform: scale(1.05); } } @keyframes bulge { 50% { -webkit-transform: scale(1.05); transform: scale(1.05); } } html { display: table; height: 100%; width: 100%; } body { display: table-cell; vertical-align: middle; }
 <div class="typing-indicator"> <span></span> <span></span> <span></span> </div>

– source: http://jsfiddle.net/Arlina/gtttgo93/ – 来源: http : //jsfiddle.net/Arlina/gtttgo93/

The problem is that the animation does not work when adding the scoped attribute to the component's style definition ( <style lang="scss" scoped> ).问题是在将scoped属性添加到组件的样式定义 ( <style lang="scss" scoped> ) 时,动画不起作用。 I believe it may be related to keyframes that should be declared globally.我相信它可能与应该全局声明的关键帧有关。

The element with .typing-indicator is in the template of the component with scoped styling.带有.typing-indicator的元素位于具有作用域样式的组件的模板中。

Does anyone have an idea of how I can allow my component to have scoped styling while making the keyframe animations work?有没有人知道如何在使关键帧动画工作的同时允许我的组件具有范围样式?

Problem问题

The problem is down to how the Webpack loader for Vue ( vue-loader ), incorrectly, parses animation names when adding IDs to scoped selectors and other identifiers.问题归结为 Vue 的 Webpack 加载器 ( vue-loader ) 在向作用域选择器和其他标识符添加 ID 时如何错误地解析动画名称。 This is important because vue-loader's CSS scoping uses unique attributes added to elements to replicate the behaviour of CSS scoping.这很重要,因为 vue-loader 的 CSS 范围使用添加到元素的独特属性来复制 CSS 范围的行为。 While your keyframe names get IDs appended, references to keyframes in animation rules in scoped styles do not.虽然您的关键帧名称会附加 ID,但在作用域样式的动画规则中对关键帧的引用不会。

Your CSS:你的 CSS:

@-webkit-keyframes blink {
  50% {
    opacity: 1;
  }
}

@keyframes blink {
  50% {
    opacity: 1;
  }
}
@-webkit-keyframes bulge {
  50% {
    -webkit-transform: scale(1.05);
            transform: scale(1.05);
  }
}
@keyframes bulge {
  50% {
    -webkit-transform: scale(1.05);
            transform: scale(1.05);
  }
}

.typing-indicator {
  ...
  -webkit-animation: 2s bulge infinite ease-out;
          animation: 2s bulge infinite ease-out;
}

.typing-indicator span:nth-of-type(1) {
  -webkit-animation: 1s blink infinite 0.3333s;
          animation: 1s blink infinite 0.3333s;
}
.typing-indicator span:nth-of-type(2) {
  -webkit-animation: 1s blink infinite 0.6666s;
          animation: 1s blink infinite 0.6666s;
}
.typing-indicator span:nth-of-type(3) {
  -webkit-animation: 1s blink infinite 0.9999s;
          animation: 1s blink infinite 0.9999s;
}

Should get transformed to:应该变成:

@-webkit-keyframes blink-data-v-xxxxxxxx {
  50% {
    opacity: 1;
  }
}

@keyframes blink-data-v-xxxxxxxx {
  50% {
    opacity: 1;
  }
}
@-webkit-keyframes bulge-data-v-xxxxxxxx {
  50% {
    -webkit-transform: scale(1.05);
            transform: scale(1.05);
  }
}
@keyframes bulge-data-v-xxxxxxxx {
  50% {
    -webkit-transform: scale(1.05);
            transform: scale(1.05);
  }
}

.typing-indicator {
  ...
  -webkit-animation: 2s bulge-data-v-xxxxxxxx infinite ease-out;
          animation: 2s bulge-data-v-xxxxxxxx infinite ease-out;
}

.typing-indicator span:nth-of-type(1) {
  -webkit-animation: 1s blink-data-v-xxxxxxxx infinite 0.3333s;
          animation: 1s blink-data-v-xxxxxxxx infinite 0.3333s;
}
.typing-indicator span:nth-of-type(2) {
  -webkit-animation: 1s blink-data-v-xxxxxxxx infinite 0.6666s;
          animation: 1s blink-data-v-xxxxxxxx infinite 0.6666s;
}
.typing-indicator span:nth-of-type(3) {
  -webkit-animation: 1s blink-data-v-xxxxxxxx infinite 0.9999s;
          animation: 1s blink-data-v-xxxxxxxx infinite 0.9999s;
}

However it only get's transformed to:然而,它只会被转换为:

@-webkit-keyframes blink-data-v-xxxxxxxx {
  50% {
    opacity: 1;
  }
}

@keyframes blink-data-v-xxxxxxxx {
  50% {
    opacity: 1;
  }
}
@-webkit-keyframes bulge-data-v-xxxxxxxx {
  50% {
    -webkit-transform: scale(1.05);
            transform: scale(1.05);
  }
}
@keyframes bulge-data-v-xxxxxxxx {
  50% {
    -webkit-transform: scale(1.05);
            transform: scale(1.05);
  }
}

.typing-indicator {
  ...
  -webkit-animation: 2s bulge infinite ease-out;
          animation: 2s bulge infinite ease-out;
}

.typing-indicator span:nth-of-type(1) {
  -webkit-animation: 1s blink infinite 0.3333s;
          animation: 1s blink infinite 0.3333s;
}
.typing-indicator span:nth-of-type(2) {
  -webkit-animation: 1s blink infinite 0.6666s;
          animation: 1s blink infinite 0.6666s;
}
.typing-indicator span:nth-of-type(3) {
  -webkit-animation: 1s blink infinite 0.9999s;
          animation: 1s blink infinite 0.9999s;
}

Something to note: in the actual transformation, references to keyframe names in animation rules are missing the -data-v-xxxxxxxx at the end.需要注意的是:在实际转换中,动画规则中关键帧名称的引用在末尾缺少-data-v-xxxxxxxx This is the bug.这是错误。

Currently (as of 47c3317 ) the animation name in shorthand animation rule declarations is identified by getting the first value out of splitting the animation rule by any whitespace character [1] .目前(从47c3317 开始)速记动画规则声明中的动画名称是通过从任何空白字符[1]分割动画规则中获取第一个值来标识的。 However the formal definition for the animation property states the animation name could appear anywhere within the rule definition.然而,动画属性的正式定义规定动画名称可以出现在规则定义中的任何位置。

<single-animation> = <time> || <single-timing-function> || <time> || <single-animation-iteration-count> || <single-animation-direction> || <single-animation-fill-mode> || <single-animation-play-state> || [ none | <keyframes-name> ]

animation formal syntax [2] animation形式语法[2]

Therefore, while your animation declarations are valid, vue-loader is not able to parse it.因此,虽然您的动画声明有效,但 vue-loader 无法解析它。

Workaround解决方法

The current workaround for this is to move your animation names to the beginning of animation rule declarations.当前的解决方法是将动画名称移动到动画规则声明的开头。 Your keyframe declarations do not need changing, they remain inside the scoped stylesheet.您的关键帧声明不需要更改,它们保留在范围样式表中。 Your animation declarations should now look like this:您的动画声明现在应如下所示:

.typing-indicator {
  ...
  -webkit-animation: bulge 2s infinite ease-out;
          animation: bulge 2s infinite ease-out;
}
.typing-indicator span:nth-of-type(1) {
  -webkit-animation: blink 1s infinite 0.3333s;
          animation: blink 1s infinite 0.3333s;
}
.typing-indicator span:nth-of-type(2) {
  -webkit-animation: blink 1s infinite 0.6666s;
          animation: blink 1s infinite 0.6666s;
}
.typing-indicator span:nth-of-type(3) {
  -webkit-animation: blink 1s infinite 0.9999s;
          animation: blink 1s infinite 0.9999s;
}

References参考

I met the same problem and the first answer did tell me why it does not work, but the workaround part did not quite fix my issue... this is my code:我遇到了同样的问题,第一个答案确实告诉我为什么它不起作用,但解决方法部分并没有完全解决我的问题......这是我的代码:

/* Animations */
@keyframes moveOut1 {
    from {
        transform: translateY(0) scale(0);
    }
    3% {
        transform: translateY(0.2em) scale(1);
    }
    97% {
        transform: translateY(7.3em) scale(1);
    }
    to {
        transform: translateY(7.5em) scale(0);
    }
}
@keyframes moveOut2 {
    from {
        transform: rotate(60deg) translateY(0) scale(0);
    }
    3% {
        transform: rotate(60deg) translateY(0.2em) scale(1);
    }
    97% {
        transform: rotate(60deg) translateY(7.3em) scale(1);
    }
    to {
        transform: rotate(60deg) translateY(7.5em) scale(0);
    }
}
@keyframes moveOut3 {
    from {
        transform: rotate(120deg) translateY(0) scale(0);
    }
    3% {
        transform: rotate(120deg) translateY(0.2em) scale(1);
    }
    97% {
        transform: rotate(120deg) translateY(7.3em) scale(1);
    }
    to {
        transform: rotate(120deg) translateY(7.5em) scale(0);
    }
}
@keyframes moveOut4 {
    from {
        transform: rotate(180deg) translateY(0) scale(0);
    }
    3% {
        transform: rotate(180deg) translateY(0.2em) scale(1);
    }
    97% {
        transform: rotate(180deg) translateY(7.3em) scale(1);
    }
    to {
        transform: rotate(180deg) translateY(7.5em) scale(0);
    }
}
@keyframes moveOut5 {
    from {
        transform: rotate(240deg) translateY(0) scale(0);
    }
    3% {
        transform: rotate(240deg) translateY(0.2em) scale(1);
    }
    97% {
        transform: rotate(240deg) translateY(7.3em) scale(1);
    }
    to {
        transform: rotate(240deg) translateY(7.5em) scale(0);
    }
}
@keyframes moveOut6 {
    from {
        transform: rotate(300deg) translateY(0) scale(0);
    }
    3% {
        transform: rotate(300deg) translateY(0.2em) scale(1);
    }
    97% {
        transform: rotate(300deg) translateY(7.3em) scale(1);
    }
    to {
        transform: rotate(300deg) translateY(7.5em) scale(0);
    }
}
@keyframes ripple {
    from,
    to {
        width: 0.2em;
    }
    33% {
        width: 2.4em;
    }
}

so I asked a friend and the solution he provided me with is simply to place the css code out side and then import it into the vue component via所以我问了一个朋友,他给我的解决方案就是把css代码放在外面,然后通过

<style>
@import url(./{css_file_name}.css);
</style>

but I do not understand the mechanism behind this... but to me, it's fine as long as it works.但我不明白这背后的机制......但对我来说,只要它有效就可以了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM