为什么CSS变量动画在网络组件中不起作用

发布于 2025-02-09 22:03:56 字数 4038 浏览 2 评论 0 原文

我使用实验 @property 功能成功创建了CSS变量动画。

重要的是:这仅在Chrome,Opera或Edge

document.addEventListener('DOMContentLoaded', () => {
  const rangeInput = document.querySelector('#scale');
  const htmlElement = document.querySelector('#html-element');
  rangeInput.addEventListener('change', (e) => {
    htmlElement.style.setProperty(
      '--html-element-scale',
      (e.target.value / 100).toFixed(2)
    );
  });
});
@property --html-element-background-color {
  syntax: '<color>';
  inherits: false;
  initial-value: #00ff00;
}

@property --html-element-rotate {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

@property --html-element-scale {
  syntax: '<number>';
  inherits: false;
  initial-value: 1;
}

#html-element {
  transform: rotate(var(--html-element-rotate))
    scale(var(--html-element-scale));

  border: 1px solid #000;
  height: 100px;
  width: 100px;
  background-color: var(--html-element-background-color);
  animation: anim 1s ease-in infinite alternate;
}

@keyframes anim {
  100% {
    --html-element-rotate: 180deg;
    --html-element-background-color: #ff00ff;
  }
}
<div id="html-element">HTML Element</div>

<div id="range-container">
  <input
    type="range"
    id="scale"
    name="scale"
    min="0"
    max="100"
    value="100"
  />
  <label for="scale">Scale</label>
</div>

但是,将其包装在Web组件中时,只有CSS值会更改,但是CSS VAR的值并未动画。此外,旋转不起作用,直到您更改比例滑块或AD AD a :host {-Html-元素尺度:1} 作为默认值...这很奇怪。

const html = `
<style>
  @property --web-component-background-color {
    syntax: '<color>';
    inherits: false;
    initial-value: #00ff00;
  }

  @property --web-component-rotate {
    syntax: '<angle>';
    inherits: false;
    initial-value: 0deg;
  }

  @property --web-component-scale {
    syntax: '<number>';
    inherits: false;
    initial-value: 1;
  }

  #web-component {
    transform: scale(var(--web-component-scale)) rotate(var(--web-component-rotate));

    border: 1px solid #000;
    height: 100px;
    width: 100px;
    background-color: var(--web-component-background-color);
    animation: anim 1s ease-in infinite alternate;
  }

  @keyframes anim {
    100% {
      --web-component-rotate: 180deg;
      --web-component-background-color: #ff00ff;
    }
  }
</style>

<div id="web-component">Web Component</div>
<div id="range-container">
  <input type="range" id="scale" name="scale" min="0" max="100" value="100" />
  <label for="scale">Scale</label>
</div>`;

customElements.define(
  'web-component',
  class WebComponent extends HTMLElement {
    shadowRoot = this.attachShadow({ mode: 'open' });
    constructor() {
      super();
      let template = document.createElement('template');
      template.innerHTML = html;
      this.shadowRoot.appendChild(template.content.cloneNode(true));
    }
    connectedCallback() {
      const rangeInput = this.shadowRoot.querySelector('#scale');
      const webComponent = this.shadowRoot.querySelector('#web-component');
      rangeInput.addEventListener('change', (e) => {
        webComponent.style.setProperty(
          '--web-component-scale',
          (e.target.value / 100).toFixed(2)
        );
      });
    }
  }
);
<web-component></web-component>

最后但并非最不重要的一点是,如果将样式表从Web-Component复制到常规DOM,以便Web-Component继承CSS,则可以再次工作。

问题是,由于功能的实验阶段,这是一个错误,还是我在Web组件中实现它做错了?

I've successfully created a css variable animations, using the experimental @property feature.

IMPORTANT: this only works in Chrome, Opera or Edge compatibility table

document.addEventListener('DOMContentLoaded', () => {
  const rangeInput = document.querySelector('#scale');
  const htmlElement = document.querySelector('#html-element');
  rangeInput.addEventListener('change', (e) => {
    htmlElement.style.setProperty(
      '--html-element-scale',
      (e.target.value / 100).toFixed(2)
    );
  });
});
@property --html-element-background-color {
  syntax: '<color>';
  inherits: false;
  initial-value: #00ff00;
}

@property --html-element-rotate {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

@property --html-element-scale {
  syntax: '<number>';
  inherits: false;
  initial-value: 1;
}

#html-element {
  transform: rotate(var(--html-element-rotate))
    scale(var(--html-element-scale));

  border: 1px solid #000;
  height: 100px;
  width: 100px;
  background-color: var(--html-element-background-color);
  animation: anim 1s ease-in infinite alternate;
}

@keyframes anim {
  100% {
    --html-element-rotate: 180deg;
    --html-element-background-color: #ff00ff;
  }
}
<div id="html-element">HTML Element</div>

<div id="range-container">
  <input
    type="range"
    id="scale"
    name="scale"
    min="0"
    max="100"
    value="100"
  />
  <label for="scale">Scale</label>
</div>

But when wrapping it inside of a web-component, only the css values are changed, but the values of the css var's are not animated. Furthermore the rotation doesn't work, until you change the scale slider or alternatively ad a :host {--html-element-scale: 1} as default value... which is weird.

const html = `
<style>
  @property --web-component-background-color {
    syntax: '<color>';
    inherits: false;
    initial-value: #00ff00;
  }

  @property --web-component-rotate {
    syntax: '<angle>';
    inherits: false;
    initial-value: 0deg;
  }

  @property --web-component-scale {
    syntax: '<number>';
    inherits: false;
    initial-value: 1;
  }

  #web-component {
    transform: scale(var(--web-component-scale)) rotate(var(--web-component-rotate));

    border: 1px solid #000;
    height: 100px;
    width: 100px;
    background-color: var(--web-component-background-color);
    animation: anim 1s ease-in infinite alternate;
  }

  @keyframes anim {
    100% {
      --web-component-rotate: 180deg;
      --web-component-background-color: #ff00ff;
    }
  }
</style>

<div id="web-component">Web Component</div>
<div id="range-container">
  <input type="range" id="scale" name="scale" min="0" max="100" value="100" />
  <label for="scale">Scale</label>
</div>`;

customElements.define(
  'web-component',
  class WebComponent extends HTMLElement {
    shadowRoot = this.attachShadow({ mode: 'open' });
    constructor() {
      super();
      let template = document.createElement('template');
      template.innerHTML = html;
      this.shadowRoot.appendChild(template.content.cloneNode(true));
    }
    connectedCallback() {
      const rangeInput = this.shadowRoot.querySelector('#scale');
      const webComponent = this.shadowRoot.querySelector('#web-component');
      rangeInput.addEventListener('change', (e) => {
        webComponent.style.setProperty(
          '--web-component-scale',
          (e.target.value / 100).toFixed(2)
        );
      });
    }
  }
);
<web-component></web-component>

Last but not least, if you copy the stylesheet from the web-component into the regular dom, so that the web-component inherits the css, it works again.

The question is, is this a bug due to the experimental stage of the feature or am I doing something wrong with implementing it in a web-component?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

×眷恋的温暖 2025-02-16 22:03:56

它看起来像 @property 定义在Web组件内无法使用。我不确定这是预期的还是错误的,我会将其报告为CSS-Houdini项目的问题。

但是,如果您对工作版本只想使用全球定义的CSS变量进行动画感兴趣(在Web-Component的用例中非常有限)

const html = `
<style>
  #web-component {
    transform: rotate(var(--html-element-rotate))
      scale(var(--html-element-scale));
    border: 1px solid #000;
    height: 100px;
    width: 100px;
    background-color: var(--html-element-background-color);
    animation: anim 1s ease-in infinite alternate;
  }

  @keyframes anim {
    100% {
      --html-element-rotate: -180deg;
      --html-element-background-color: #0000ff;
    }
  }
</style>

<div id="web-component">Web Component</div>
<div id="range-container">
  <input type="range" id="scale" name="scale" min="0" max="100" value="100" />
  <label for="scale">Scale</label>
</div>`;

document.addEventListener('DOMContentLoaded', () => {
  const rangeInput = document.querySelector('#scale');
  const htmlElement = document.querySelector('#html-element');
  rangeInput.addEventListener('change', (e) => {
    htmlElement.style.setProperty(
      '--html-element-scale',
      (e.target.value / 100).toFixed(2)
    );
  });
});


customElements.define(
  'web-component',
  class WebComponent extends HTMLElement {
    shadowRoot = this.attachShadow({ mode: 'open' });
    constructor() {
      super();
      let template = document.createElement('template');
      template.innerHTML = html;
      this.shadowRoot.appendChild(template.content.cloneNode(true));
    }
    connectedCallback() {
      const rangeInput = this.shadowRoot.querySelector('#scale');
      const webComponent = this.shadowRoot.querySelector('#web-component');
      rangeInput.addEventListener('change', (e) => {
        webComponent.style.setProperty(
          '--html-element-scale',
          (e.target.value / 100).toFixed(2)
        );
      });
    }
  }
);
@property --html-element-background-color {
  syntax: '<color>';
  inherits: false;
  initial-value: #00ff00;
}

@property --html-element-rotate {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

@property --html-element-scale {
  syntax: '<number>';
  inherits: false;
  initial-value: 1;
}

#html-element {
  transform: rotate(var(--html-element-rotate))
    scale(var(--html-element-scale));
  border: 1px solid #000;
  height: 100px;
  width: 100px;
  background-color: var(--html-element-background-color);
  animation: anim 1s ease-in infinite alternate;
}

@keyframes anim {
  100% {
    --html-element-rotate: 180deg;
    --html-element-background-color: #ff00ff;
  }
}
<web-component></web-component>


<div id="html-element">HTML Element</div>

<div id="range-container">
  <input
    type="range"
    id="scale"
    name="scale"
    min="0"
    max="100"
    value="100"
  />
  <label for="scale">Scale</label>
</div>

It looks like @property definitions do not work inside of web-components. I'm not sure if this is intended or a bug, I will report it as an issue to the css-houdini project.

However, if you're interested in what a working version would look like if you only want to use globally defined css variables for animations (which is quite limited in it's use case for a web-component)

const html = `
<style>
  #web-component {
    transform: rotate(var(--html-element-rotate))
      scale(var(--html-element-scale));
    border: 1px solid #000;
    height: 100px;
    width: 100px;
    background-color: var(--html-element-background-color);
    animation: anim 1s ease-in infinite alternate;
  }

  @keyframes anim {
    100% {
      --html-element-rotate: -180deg;
      --html-element-background-color: #0000ff;
    }
  }
</style>

<div id="web-component">Web Component</div>
<div id="range-container">
  <input type="range" id="scale" name="scale" min="0" max="100" value="100" />
  <label for="scale">Scale</label>
</div>`;

document.addEventListener('DOMContentLoaded', () => {
  const rangeInput = document.querySelector('#scale');
  const htmlElement = document.querySelector('#html-element');
  rangeInput.addEventListener('change', (e) => {
    htmlElement.style.setProperty(
      '--html-element-scale',
      (e.target.value / 100).toFixed(2)
    );
  });
});


customElements.define(
  'web-component',
  class WebComponent extends HTMLElement {
    shadowRoot = this.attachShadow({ mode: 'open' });
    constructor() {
      super();
      let template = document.createElement('template');
      template.innerHTML = html;
      this.shadowRoot.appendChild(template.content.cloneNode(true));
    }
    connectedCallback() {
      const rangeInput = this.shadowRoot.querySelector('#scale');
      const webComponent = this.shadowRoot.querySelector('#web-component');
      rangeInput.addEventListener('change', (e) => {
        webComponent.style.setProperty(
          '--html-element-scale',
          (e.target.value / 100).toFixed(2)
        );
      });
    }
  }
);
@property --html-element-background-color {
  syntax: '<color>';
  inherits: false;
  initial-value: #00ff00;
}

@property --html-element-rotate {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

@property --html-element-scale {
  syntax: '<number>';
  inherits: false;
  initial-value: 1;
}

#html-element {
  transform: rotate(var(--html-element-rotate))
    scale(var(--html-element-scale));
  border: 1px solid #000;
  height: 100px;
  width: 100px;
  background-color: var(--html-element-background-color);
  animation: anim 1s ease-in infinite alternate;
}

@keyframes anim {
  100% {
    --html-element-rotate: 180deg;
    --html-element-background-color: #ff00ff;
  }
}
<web-component></web-component>


<div id="html-element">HTML Element</div>

<div id="range-container">
  <input
    type="range"
    id="scale"
    name="scale"
    min="0"
    max="100"
    value="100"
  />
  <label for="scale">Scale</label>
</div>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文