如何使用CSS过渡和JavaScript使元素的大小立即增加,然后平滑地缩小?

nmpmafwu  于 2023-04-19  发布在  Java
关注(0)|答案(3)|浏览(133)

我的页面上有一个小部件,它是一个span元素,样式如下:

span.widget
{
        line-height: 0;
        font-size: 150%;
        transition: font-size 1s;
}

我有JavaScript修改其font-size:

var widget=document.getElementById('the_widget');
widget.style.fontSize='200%';

如果我这样做,字体大小在1秒内连续放大,效果很好。
然而,我想实现的是字体大小立即跳起来,然后平滑地缩小。我已经尝试了以下代码:

widget.style.transition='none';
widget.style.fontSize='200%';
widget.style.transition='font-size 1s';
widget.style.fontSize='150%';

然而,这并不起作用,因为浏览器在第一次调整大小后不会重排元素,所以第二次调整大小只是保持不变,没有动画,也没有任何改变的迹象。
我也想过类似的事情与其他css的大小转换,如高度和宽度,但遇到同样的问题。

xwbd5t1u

xwbd5t1u1#

你可以使用CSS过渡或动画。我将展示如何使用这两个例子如下。
在这种情况下,我建议在过渡中使用动画,这看起来更直接,更容易实现。

动画

从技术上讲,动画可以做过渡所能做的一切,它们可以很容易地在多个元素上重复使用。
但是“经典”*CSS动画 * 有相当“刚性”的关键帧作为权衡:(除了使用自定义属性)关键帧中的值不能为每个元素单独设置。
此外,每个CSS动画都需要一个唯一的名称。对于许多动画,这可能很难以有组织的方式实现。
您还可以使用 Web Animations API(更多内容请参阅下节)制作动画。此类JavaScript动画不受上述限制。

CSS动画

属性的声明值将是动画中隐含的初始值或最终值。但是初始值和最终值也可以分别在from(或0%)和to(或100%)关键帧中显式声明。
通过在元素的规则中声明font-size: 150%,150%将是动画中的“from”和“to”值的隐含值。由于我们希望从值200%到150%进行动画,我们可以:

  • 显式声明最终值:
@keyframes animation-name {
  from {font-size: 200%}
  to {font-size: 150%}
}
  • 使用隐式最终值:
@keyframes animation-name {
  from {font-size: 200%}
  /* 'To font-size:150%' is implied for elements with `font-size:150%` */
}

包含隐含最终值的示例:

@keyframes from-doubled-font {
  from {font-size: 200%}
}
p {
  font-size: 150%;
  animation: from-doubled-font 1s linear;
}
<p>This is some text!</p>

网页动画

我们还可以在JavaScript中使用Web Animations API来制作动画。作为JavaScript的API,它允许动画更具动态性(在关键帧,属性值和动画选项方面)。
使用API可能会更详细,但由于显式命名了所有属性,因此比简写的animation属性更具可读性。
当传递一个只包含一个关键帧的数组时,该关键帧将是to关键帧。这意味着我们必须传递至少两个关键帧来声明'from'属性。
使用Web Animations API的示例:

const p = document.querySelector("p");

const from = {fontSize: "200%"};
const to = {}; // Use implicit final value
const keyframes = [from, to];

const options = {
  duration: 1000, // ms
  easing: "linear"
};

p.animate(keyframes, options);
p {font-size: 150%}
<p>This is some text!</p>

过渡

转换允许从一个值转换到另一个值(一次更改)。这限制了它们的用例,但也使它们易于声明,即使是在多个元素上。
由于我们希望font-size从最初的150%更改为200%,然后再次过渡到150%(这是 * 两次 * 更改),因此我们不能 * 简单地 * 使用过渡。
相反,我们将不得不使用JavaScript并引起回流,因此中间的200%将被视为“transition-from”值。

导致立即回流

在声明'transition-to'值之前,我们需要一个reflow。有多个causes for reflowing,其中一些我们可以自己立即引起(例如by reading offsetHeight)。
通过声明font-size: 200%,然后引起立即回流,我们基本上将200%“提交”为“当前状态”。之后,我们可以通过声明转换并重新声明font-size来开始转换,这将是“transition-to”值。
示例:

const p = document.querySelector("p");

p.style.transition='none';
p.style.fontSize='200%';

reflow();

p.style.transition='font-size 1s linear';
p.style.fontSize='150%';

function reflow() {
  // Example implementation:
  document.body.clientHeight;
}
p {font-size: 150%}
<p>This is some text!</p>

等待回流

我们也可以等待他们,而不是立即引起回流。我们可以通过改变风格等方式引起回流。
重绘总是在回流之前。通过等待重绘,我们可以等待回流。我们可以使用requestAnimationFrame() function来做到这一点,它的回调将在重绘的回流之前被调用。
requestAnimationFrame()的回调中调用requestAnimationFrame()将等待后续重绘的回流。
这意味着我们可以写一个helper function to wait for frames,例如:

function waitFrames(n, callback) {
  if (n > 0) {
    requestAnimationFrame(() => waitFrames(n - 1, callback));
  } else {
    requestAnimationFrame(callback);
  }
}

等待回流焊后的转换示例:

const p = document.querySelector("p");

p.style.fontSize = "200%";

waitFrames(1, function afterWaiting() {
  p.style.transition = "font-size 1s linear";
  p.style.fontSize = "150%";
});

function waitFrames(n, callback) {
  if (n > 0) {
    requestAnimationFrame(() => waitFrames(n - 1, callback));
  } else {
    requestAnimationFrame(callback);
  }
}
p {font-size: 150%}
<p>This is some text!</p>
bnl4lu3b

bnl4lu3b2#

所以,在打出这个问题后,我实际上偶然发现了一个变通方法或黑客,它可能是一个可持续的解决方案,即使它有点《双城之战》;我发现了这个问题:
Force browser to trigger reflow while changing CSS
它说你可以使用JavaScript来强制回流,使用offsetHeight属性。如果你计时
我发现这在Chrome和Firefox中有效,尚未在任何其他浏览器中测试:

widget.style.transition='none';
widget.style.fontSize='200%';
void(widget.offsetHeight);
widget.style.transition='font-size 1s';
widget.style.fontSize='150%';

我认为这是可行的,因为将表达式传递给void会强制对该表达式求值,在本例中会强制回流。
可能有一种更优雅或简洁的方法来做到这一点,只需更改CSS类,然后将font-sizetransition元素中的更改附加到相应的类。

x0fgdtte

x0fgdtte3#

需要一点JavaScript。
试试这个:

div {
    width: fit-content;
    margin: 5vh auto;
}
span#widget {
    font-size: 150%;
    color: red;
}
span.animate {
    animation: stayingalive 1.5s ease-out forwards;
}
@keyframes stayingalive {

    0%, 30%, 100% {
        font-size: 150%;
    }
    15% {
        font-size: 210%;
    }
    50% {
        font-size: 250%;
    }
}
<div>
    <p>
        <button onclick="document.getElementById('widget').setAttribute('class', 'animate')">
            Simulate programmatical trigger: add class 'animate'
        </button>
    </p>
    <p>
        <button onclick="document.getElementById('widget').setAttribute('class', '')">
            (Reset: remove classes)
        </button>
    </p>
</div>
<div>
    <span id="widget">Uh uh uh uh &#9886; &#10084; &#9887; staying alive!</span>
</div>

相关问题