【问题标题】:Overwrite an element's style multiple times in loop?在循环中多次覆盖元素的样式?
【发布时间】:2019-11-15 08:05:02
【问题描述】:

我有一个简单的菜单,目前正在使用我想要动画的 CSS Grid。但是,grid-template-columns 无法流畅地制作动画。我现在做的菜单完全不同,但在寻找解决方案时,我遇到了一些我不太明白的东西。我的第一个想法,虽然它可能根本不是一个好主意,也可能不是关键帧动画的合适替代方案,但它是在 while 循环中更新样式以动画化项目的增加大小。它不起作用。

相关JS:

    $('#select-oak').hover(function () {
            let start = new Date().getTime();
            let elapsed = 0;

            while (elapsed <= 1000) {
                elapsed = new Date().getTime() - start;
                let val = elapsed / 1000;

                if (elapsed % 100 === 0) {
                    $('.selector-block').css('grid-template-columns', `${1.0 + val}fr 1fr 1fr 1fr`);
                    console.log(1 + val);
                    console.log(elapsed);
                }
            }
        }

CSS:

.selector-block {
    position: relative;
    overflow: hidden;
    top: -32.5vh;
    background-color: white;
    gap: 5px;
    width: 100vw;
    height: 90vh;
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
    clip-path: polygon(0 36%, 100% 0, 100% 60%, 0% 100%);
    z-index: 3;
}

#select-oak {
    position: relative;
    overflow: hidden;
    text-align: center;
    background-color: rgb(94, 80, 21);
    top: 21.5%;
    width: 100%;
    height: 200%;
}

#select-oak img {
    position: relative;
    left: -10vw;
    transform: scale(1.06);
    filter: blur(5px);
}

#select-oak img:hover {
    filter: none;
    transform: scale(1.1);
    transition: ease-in-out 0.5s;
}

这似乎是一种非常规的方法,所以我并不感到惊讶它不起作用,但由于我对 CSS、JS 和 JQuery 比较陌生,我很好奇是什么阻止了它的工作只是为了提供帮助我对使用 JQuery 和 CSS 的理解。这是结果。如您所见,即使打印出正确的值,它也不会更新,直到循环的最后一次通过。

所以我有两个问题:

  1. 这是我的代码有问题,还是只是您不能做的事情?
  2. 如果可以像这样更新样式,这是一种不好的做法,为什么?

【问题讨论】:

  • flexbox 会不会比 grid 更好?那么你应该可以通过简单地操作悬停项的 flex 属性来实现这种效果。
  • @04FS 是的,它会,但这纯粹是为了我自己的实践,因为我喜欢在其他项目中使用网格来响应,所以我想看看是否有可能使这种方法起作用。主要是出于好奇和学习。

标签: javascript jquery css css-grid


【解决方案1】:

while 循环阻塞了渲染进程,因为它是同步的。

我想出了一种使用 CSS 过渡的方法,它与子元素的数量无关。 它还处理鼠标离开甚至从一个菜单项移动到另一个菜单项。

$('.selector-block > *').hover(function() {
  let frValues = Array($(this).siblings().length+1).fill('1fr');
  frValues[$(this).prevAll().length] = '2fr';
  $('.selector-block').css({
    'grid-template-columns': frValues.join(' ')
  });
}, function() {
  let frValues = Array($(this).siblings().length+1).fill('1fr');
  $('.selector-block').css({
    'grid-template-columns': frValues.join(' ')
  });
});
.selector-block {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  transition: all 1s ease-in-out;
}

.selector-block > * {
  border: solid red 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="selector-block"><div id="select-oak">select-oak</div><div>foobar</div><div>baz</div><div>bat</div></div>

如果子元素的数量是固定的,你可以只用 CSS 来做到这一点,没有 JavaScript,也可以通过 :nth-child() 选择器和 :hover(),但是这样,你可以动态地将元素添加到你的任何时候都可以使用菜单。 实际上,您不能,因为您需要通过子项的悬停来触发父项的属性动画,这是不可能的。

旧答案:

您正在寻找window.requestAnimationFrame()。这是它如何工作的基本演示。您必须调整您的值以获得更平滑的过渡并处理鼠标离开元素的问题。

let start;
let elapsed = 0;
let rafHandle;

function hoverAnimation() {
  if (elapsed <= 1000) {
    elapsed = new Date().getTime() - start;
    let val = elapsed / 1000;

    $('.selector-block').css('grid-template-columns', `${1.0 + val}fr 1fr 1fr 1fr`);
    //console.log(1 + val);
    //console.log(elapsed);
    window.setTimeout(function() {
      rafHandle = window.requestAnimationFrame(hoverAnimation);
    }, 1000/60); // about 60 FPS
  }
}

$('#select-oak').hover(function() {
  start = new Date().getTime();
  elapsed = 0;
  rafHandle = window.requestAnimationFrame(hoverAnimation);
}, function() {
  // handle mouse leave animation here instead
  window.cancelAnimationFrame(rafHandle);
  elapsed = Infinity;
  $('.selector-block').css('grid-template-columns', `1fr 1fr 1fr 1fr`);
});
.selector-block {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="selector-block"><div id="select-oak">select-oak</div><div>foobar</div><div>baz</div><div>bat</div></div>

【讨论】:

  • 更新为包含一个基本示例。
  • requestAnimationFrame() 正是我想要的,我不知道它存在。非常感谢您的帮助!只是想知道,这是一种常见的做法吗?
  • 是的,它用于在资源空闲时无阻塞地更新 DOM。我想出了一个更好的方法,使用 CSS 过渡,不知道子元素的数量。查看我的答案的更新。
【解决方案2】:

执行 JavaScript 会阻止渲染页面,这仅在代码执行返回事件循环后发生。

虽然更改样式属性可能会更新 DOM 并影响元素位置和大小的动态计算,但更新屏幕仍然会延迟到脚本完成。这就是为什么您只能看到上次更新的结果。

A1:您不能影响同步 JavaScript 循环内的页面显示。

A2:这不是坏习惯,因为它无法完成:-)

【讨论】:

    猜你喜欢
    • 2014-04-13
    • 1970-01-01
    • 1970-01-01
    • 2012-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-30
    相关资源
    最近更新 更多