在CSS Grid布局中使用CSS过渡效果

65

我正在尝试让我的固定头部具有过渡效果,以便它可以缓慢移动而不是突然移动。

我做错了什么?

这是我的一个工作版本:

http://codepen.io/juanmata/pen/RVMbmr

基本上以下代码将类tiny添加到我的wrapper类中,这很好地实现了。
$(window).on('load', function() {
    $(window).on("scroll touchmove", function () {
        $('.wrapper').toggleClass('tiny', $(document).scrollTop() > 0);
    });
});

这是 CSS 部分:

.wrapper {
    grid-template-rows: 80px calc(75vh - 80px) 25vh 80px;
    -o-transition: all 0.5s;
    -moz-transition: all 0.5s;
    -webkit-transition: all 0.5s;
    transition: all 0.5s;
}
.tiny {
    grid-template-rows: 40px calc(75vh - 40px) 25vh 80px;
}

所以标题确实会收缩,但没有动画效果,我错过了什么还是网格不支持过渡效果?在这里可以找到css-grid文档链接。

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout

$(window).on('load', function() {
  $(window).on("scroll touchmove", function() {
    $('.wrapper').toggleClass('tiny', $(document).scrollTop() > 0);
  });
});
* {
 margin:0;
 padding:0;
}

.wrapper {
 display: grid;
 grid-gap: 0px;
 grid-template-columns: 1fr 1fr 1fr 1fr;
 grid-template-rows: 80px calc(75vh - 80px) 25vh 80px;
 grid-template-areas:
  "head head head head"
  "main main main main"
  "leader leader leader leader"
  "foot foot foot foot";
 background-color: #fff;
 color: #444;
}
.tiny {
 grid-template-rows: 40px calc(75vh - 40px) 25vh 80px;
}
.box {
 background-color: #444;
 color: #fff;
 border-radius: 5px;
 font-size: 150%;
}
.box .box {
 background-color: lightcoral;
}

.head {
 grid-area: head;
 background-color: #999;
 z-index: 2;
 display: grid;
 grid-gap: 0px;
 grid-template-columns: 20% 1fr;
 -o-transition: all 0.5s;
 -moz-transition: all 0.5s;
 -webkit-transition: all 0.5s;
 transition: all 0.5s;
 position: sticky;
 top: 0;
}

.logo{
        height: inherit;
        grid-column: 1;
        grid-row: 1;
        background-color:red;
        position: relative;
        overflow: hidden;
    }
.logo img {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        display: block;
        max-width: 100%;
        height: auto;
    }
.main {
 grid-area: main;
 /* CSS Grid */
 z-index: 1;
 grid-column: head-start / head-end;
 grid-row: head-start / leader-start;
 background-color: red;
}
.leader {
 grid-area: leader;
 z-index:1;
 display: grid;
 grid-gap: 0px;
 grid-template-columns: repeat(4, 1fr  );
}
.foot {
 grid-area: foot;
 z-index:1;
}
<div class="wrapper">
  <div class="box head">
    <div class="box logo">
      <a href="#"><img src="https://unsplash.it/200/300/?random" /></a>
    </div>
    <div class="box nav">nav</div>
  </div>
  <div class="box main">main</div>
  <div class="box leader">
    <div class="box leader-1">1</div>
    <div class="box leader-2">2</div>
    <div class="box leader-3">3</div>
    <div class="box leader-4">4</div>
  </div>
  <div class="box foot">foot</div>
</div>

9个回答

59

根据规范,grid-template-columnsgrid-template-rows属性应该支持过渡效果。

7.2. 显式轨道大小: grid-template-rowsgrid-template-columns属性

可动画化:作为长度、百分比或计算出的简单列表,只要列表中的长度、百分比或计算组件的值不同即可。

因此,如果我的理解是正确的,只要属性的值有所更改,而没有更改规则的结构,过渡效果就应该起作用。但它们并没有起作用。更新:这个功能目前只在Firefox浏览器中实现了。

这确实可以工作,但目前只在Firefox浏览器中实现。其他浏览器的更新请参见这里

~ 评论区的贡献者 @bcbrian


这是我创建的一个测试:

grid-container {
  display: inline-grid;
  grid-template-columns: 100px 20vw 200px;
  grid-template-rows: repeat(2, 100px);
  background-color: black;
  height: 230px;
  transition: 2s;
  
  /* non-essential */
  grid-gap: 10px;
  padding: 10px;
  box-sizing: border-box;
}

grid-container:hover {
  grid-template-columns: 50px 10vw 100px;
  grid-template-rows: repeat(2, 50px);
  background-color: red;
  height: 130px;
  transition: 2s;
}

grid-item {
  background-color: lightgreen;
}
<grid-container>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
</grid-container>

jsFiddle演示

在这个测试中,过渡效果对高度和背景颜色起作用,但不对grid-template-rowsgrid-template-columns起作用。


2
嗨,Michael,感谢你的回答。虽然你的背景是动画的,但你的网格框不是。我已经成功地通过给每个选择的div添加一个高度标签并基本上覆盖该div的grid-template-columns来使框动画化,它可以工作,但更像是一种hack。 - Dan
2
是的,过渡效果仅适用于高度和背景颜色。这在我的答案中有提到(请参见代码片段下面的最后一句话)。 - Michael Benjamin
2
错过了那部分内容。我会将其标记为已回答,因为我们无法继续进行下去了。 - Dan
1
那么这是否意味着通常情况下网格列/行不会有任何动画效果,还是说这仍在进行中?如果是这样,那么任何人如何使用CSS网格?如果您不能进行过渡,以便您可以在一个统一的解决方案中制作动画侧边栏等,那还有什么意义?如果它仍在进行中,是否有任何文章说明他们认为这将在何时完成或其他内容?谢谢 - Return-1
3
这种方法确实有效,但目前只在 Firefox 中实现。关注此处以获取其他浏览器的更新。 https://codepen.io/matuzo/post/animating-css-grid-layout-properties - bcbrian
显示剩余5条评论

14
作为一种解决方法,您可以通过调整网格项的大小而不是使用grid-template-columnsgrid-template-rows来解决问题。

You could do:

grid-container {
  display: inline-grid;
  grid-template-columns: 100px 20vw 200px;
  background-color: black;
  height: 230px;
  transition: 2s;
  
  /* non-essential */
  grid-gap: 10px;
  padding: 10px;
  box-sizing: border-box;
}

grid-container:hover {
  background-color: red;
  height: 130px;
}

grid-item {
  height: 100px;
  transition: height 2s;
  background-color: lightgreen;
}

grid-container:hover .first-row {
  height: 25px;
}

grid-container:hover .last-row {
  height: 75px;
}
<grid-container>
  <grid-item class='first-row'></grid-item>
  <grid-item class='first-row'></grid-item>
  <grid-item class='first-row'></grid-item>
  <grid-item class='last-row'></grid-item>
  <grid-item class='last-row'></grid-item>
  <grid-item class='last-row'></grid-item>
</grid-container>

这是另一个2x2的例子:https://codepen.io/erik127/pen/OvodQR

这里有一个更详细的例子,可以添加更多的列或行:https://codepen.io/erik127/pen/rdZbxL

不幸的是它有很多JavaScript代码,如果grid-template-columnsgrid-template-rows可以实现动画效果的话就好了。

另一种替代方案是在某些情况下可能有效(如果您的网格项目不跨越多行),即将flexbox与grid结合使用。


3
请勿发布仅链接到外部网站的答案;当这些网站宕机时,您的答案对读者失去了有用性! - TylerH
2
谢谢@TylerH,我在答案中添加了一个代码片段。 - Erik
1
2x2的例子是这个帖子中唯一正确的答案。顺便说一下,我在所有地方都使用了“auto”而不是“1fr”。对于我来说,使用过渡进行类切换也起作用了。 - prograils

9
我使用GSAP来动画化grid-template-columns和grid-template-rows属性:

function changeGridTemplateColumns(pattern) {
  TweenMax.to(".container",
    1, {
      gridTemplateColumns: pattern
    }
  );
}

function changeGridTemplateRows(pattern) {
  TweenMax.to(".container",
    1, {
      gridTemplateRows: pattern
    }
  );
}

$(document).ready(
  function() {
    $(".el-a,.el-b,.el-c").mouseenter(
      function() {
        changeGridTemplateRows("2fr 1fr");
      }
    );
    $(".el-d,.el-e,.el-f").mouseenter(
      function() {
        changeGridTemplateRows("1fr 2fr");
      }
    );

    $(".el-a,.el-d").mouseenter(
      function() {
        changeGridTemplateColumns("2fr 1fr 1fr");
      }
    );

    $(".el-b,.el-e").mouseenter(
      function() {
        changeGridTemplateColumns("1fr 2fr 1fr");
      }
    );

    $(".el-c,.el-f").mouseenter(
      function() {
        changeGridTemplateColumns("1fr 1fr 2fr");
      }
    );

    $(".container").mouseleave(
      function() {
        changeGridTemplateColumns("1fr 1fr 1fr");
        changeGridTemplateRows("1fr 1fr");
      }
    );
  }
);
.container {
  width: 50vw;
  height: 50vw;
  margin: auto;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(2, 1fr);
  grid-template-areas: "a b c" "d e f";
}

.el-a {
  grid-area: a;
  background-color: skyblue;
}

.el-b {
  grid-area: b;
  background-color: darkseagreen;
}

.el-c {
  grid-area: c;
  background-color: coral;
}

.el-d {
  grid-area: d;
  background-color: gold;
}

.el-e {
  grid-area: e;
  background-color: plum;
}

.el-f {
  grid-area: f;
  background-color: beige;
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.2/TweenMax.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="container">
  <div class="el-a"></div>
  <div class="el-d"></div>
  <div class="el-b"></div>
  <div class="el-e"></div>
  <div class="el-c"></div>
  <div class="el-f"></div>
</div>


4
很遗憾,他们的许可证不是 MIT 许可证。 - Alter
1
@Alter 如果是这样的话,那么它就像其他无数工具一样不再维护了 :) 由于其授权,GSAP 已经存在了很长时间。https://greensock.com/licensing/ - Zach Saucier

7
在我的情况下,我正在尝试使用以下代码打开侧边栏菜单:
.wrapper{
  display: grid;
  grid-template-columns: 0 100%;
  transition: all 1s;
  .sidebar{
    background-color: blue;
  }
  .content{
    background-color: red;
  }
}
.wrapper.sidebar-open{
  grid-template-columns: 300px 100%;
  transition: all 1s;
}

但是 grid-template-columns 的过渡效果无法实现。这是我的解决方案:

.wrapper{
  display: grid;
  grid-template-columns: auto 100%;
  .sidebar{
    width: 0;
    background-color: blue;
    transition: all 1s;
  }
  .content{
    background-color: red;
  }
}
.sidebar.sidebar-open{
  width: 300px;
  transition: all 1s;
}

也许,这会对某些人有帮助。

3
非常有帮助的答案,因为它专注于CSS问题。关键是将要过渡到“auto”的grid-template-columns设置,然后将未打开元素的宽度设置为固定像素宽度(例如width: 0),将打开元素的宽度设置为所需的打开像素宽度(例如width: 300px)。 - africola

7

目前网格模板上的转换效果不起作用。但您可以像这样使用 transform:

jsFiddle

var button = document.querySelector("#btnToggle");
button.addEventListener("click",function(){
 var content = document.querySelector(".g-content");
  if(content.classList.contains("animate"))
    content.classList.remove("animate");
  else
   content.classList.add("animate");
});
html,body{
  width:100%;
  height:100%;
  padding:0;
  margin:0;
}

.g-content{
  display:grid;
  height:100%;
  grid-template-columns: 200px 1fr;
  grid-template-rows:60px 1fr 30px;
  grid-template-areas: 
    "g-header g-header"
    "g-side g-main"
    "g-footer g-footer";
 overflow-x:hidden;
}

.g-header{
  grid-area:g-header;
  background:#2B4A6B;
  display:grid;
  grid-template-columns: max-content 1fr;
}

.g-header button{
  align-self:center;
  margin:0 5px;
}

.g-side{
  grid-area:g-side;
  background:#272B30;
  transition:all 0.5s;
}

.g-main{
  grid-area:g-main;
  background:#FFFFFA;
  transition:all 0.5s;
}

.g-footer{
  grid-area:g-footer;
  background:#7E827A
}

.animate .g-main{
  width:calc(100% + 200px);
  transform:translateX(-200px);
}

.animate .g-side{
  transform:translateX(-200px);
}  
<div class="g-content">
  <div class="g-header">
    <button id="btnToggle">
    Toggle
    </button>
  </div>
  <div class="g-side">
  </div>
  <div class="g-main">
  test
  </div>
  <div class="g-footer">
  </div>
</div>


2

另一种方法是使用transform。实际上,这可能更好,因为transform和透明度一起可以实现60fps,因为它是GPU加速而不是CPU加速(浏览器需要做的工作更少)。

以下是一个示例:https://codepen.io/oneezy/pen/YabaoR

.sides {
  display: grid;
  grid-template-columns: 50vw 50vw;
}

.monkey { animation: 0.7s monkey cubic-bezier(.86,0,.07,1) 0.4s both; }
.robot { animation: 0.7s robot cubic-bezier(.86,0,.07,1) 0.4s both; }

@keyframes monkey {
  0% { transform: translate(-50vw); }
  100% { transform: 0; }
}

@keyframes robot {
  0% { transform: translate(50vw); }
  100% { transform: 0; }
}

0

我找到了一个解决方法,但它需要至少另一列填满宽度。要过渡的列必须在grid-template-columns中为auto。另外一列必须是1fr,否则auto列将使用这个缺失的空间。此外,要过渡的列在两种状态下都必须具有绝对宽度。

这是代码:

CodePen

document.querySelector('.js-button').addEventListener('click', function() {
  document.querySelector('.js-grid').classList.toggle('grid--full')
})
.grid {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-rows: 100px;
  grid-gap: 20px;
  transition: all 1s;
}

.item {
  background-color: #D73B38;
  color: #ffffff;
  padding: 10px;
  border-radius: 5px;
  border: none;
  transition: 1s;
}

.grid .item:nth-child(3n + 2) {
  width: 200px;
  background: blueviolet; 
}
.grid--full .item:nth-child(3n + 2) {
  width: 300px;
  background: cadetblue;
}

button {
  background-color: #123456;
  color: #ffffff;
  margin: 2rem 0;
  padding: 1.4rem;
  border: none;
  border-radius: 5px;
  text-transform: uppercase;
  font-size: 1.2rem;
}
<p>Fork from <a href="https://codepen.io/matuzo/pen/rmQvMG">Manuel Matuzovic</a></p>

<button class="js-button">Animate</button>

<div class="grid js-grid">
  <article class="item">
    <h2>Element 1</h2>
  </article>
  <article class="item">
    <h2>Element 2</h2>
  </article>
  <article class="item">
    <h2>Element 3</h2>
  </article>
  <article class="item">
    <h2>Element 4</h2>
  </article>
  <article class="item">
    <h2>Element 5</h2>
  </article>
  <article class="item">
    <h2>Element 6</h2>
  </article>
</div>

它使用选择器:nth-child(3n + 2)在网格的所有列中完成技巧。但是,如果您只想更改网格中的一个块,则也可以使用此方法。


0
我只是想提一下页面视图转换API,因为这个问题在搜索结果中排名很高,关键词是transition grid-template-columns,而且这个页面上还没有人提到过它。
正如Michael Benjamin在上面提到的,使用纯CSS无法实现在更改列/行数(即更改网格轨道)时的过渡效果。所以试图看到从以下状态的过渡效果是不可能的:
grid-template-columns: repeat(1,minmax(0,1fr));

给:
grid-template-columns: repeat(2,minmax(0,1fr));

不起作用,而且更改将是即时的,无论是否应用了css transition:

然而,现在可以通过使用Page View Transitions API和使用少量的JavaScript切换CSS选择器来实现列或行计数之间的过渡动画。所使用的JS非常简洁,并且对不支持的浏览器有回退的代码。它不是一个繁重的框架,目前在所有Chromium浏览器中都可以使用。我不希望提到它会让人们放弃尝试。它可以非常简单,就像这样:

.grid {
    display: grid;
    grid-template-columns: repeat(1,minmax(0,1fr));
}

.grid--2-cols {
    grid-template-columns: repeat(2,minmax(0,1fr));
}

然后在JS中,你可以在按钮的切换中添加它,例如:
const grid = document.querySelector( '.grid' );
const button = document.querySelector( 'button' );

button.addEventListener( 'click', () => {
    if ( ! document.startViewTransition ) {
      // view transitions not supported, just toggle the class and see the change instantly
      grid.classList.toggle( 'grid--2-columns' );
    } else {
      // view transitions supported! now they will fade/animate 8-)
      document.startViewTransition( () => grid.classList.toggle( 'grid--2-columns' ) ); 
    }
});

如果你想要一个漂亮的动画效果而不是基本的淡入淡出效果,你只需要在网格中的项目上添加一个单独的view-transition-name属性。例如,可以使用帖子ID或项目计数作为网格中项目的view-transition-name值。规则是页面上不能有重复的view-transition-name值,否则过渡将失败,效果将立即出现。
在这里可以看到一个简单的代码片段:

const grid = document.querySelector( '.grid' );
const button = document.querySelector( '.toggle-pvt' );
const buttonNoPvt = document.querySelector( '.toggle-no-pvt' );

button.addEventListener( 'click', () => {
    if ( ! document.startViewTransition ) {
      // view transitions not supported, just toggle the class
      grid.classList.toggle( 'grid--2-columns' );
    } else {
      // view transitions supported! now they animate 8-)
      document.startViewTransition( () => grid.classList.toggle( 'grid--2-columns' ) ); 
    }
});

buttonNoPvt.addEventListener( 'click', () => {
    grid.classList.toggle( 'grid--2-columns' );
});
.grid {
  display: grid;
  grid-template-columns: repeat(1,minmax(0,1fr));
  gap: 1rem;
}

.grid--2-columns {
  grid-template-columns: repeat(2,minmax(0,1fr));
}

/* with individual view-transition-name's the browser will animate them changing */

.box--1 {
  view-transition-name: box-1;
}
.box--2 {
  view-transition-name: box-2;
}
.box--3 {
  view-transition-name: box-3;
}
.box--4 {
  view-transition-name: box-4;
}
.box--5 {
  view-transition-name: box-5;
}
.box--6 {
  view-transition-name: box-6;
}
.box--7 {
  view-transition-name: box-7;
}
.box--8 {
  view-transition-name: box-8;
}

/* comment out the box-- classes above to see the default Page View Transitions API fade effect */

/* basic styling */

.box {
  width: 100%;
  background: tomato;
  color: white;
  height: 2rem;
}

button {
  margin-bottom: 2rem;
}
<button class="toggle-pvt">Toggle 2 columns</button>
<button class="toggle-no-pvt">Toggle 2 columns without page view transitions</button>

<div class="grid">
  <div class="box box--1">Box 1</div>
  <div class="box box--2">Box 2</div>
  <div class="box box--3">Box 3</div>
  <div class="box box--4">Box 4</div>
  <div class="box box--5">Box 5</div>
  <div class="box box--6">Box 6</div>
  <div class="box box--7">Box 7</div>
  <div class="box box--8">Box 8</div>
</div>

我做了一个快速的Codepen,你可以在这里看到效果 - Codepen: https://codepen.io/patrickwc/pen/jOdxdgP(在Firefox和Chrome中查看差异)。
页面视图过渡API可以在任何时候调用,当你添加或删除页面元素,甚至切换CSS类的开关时(可能还有其他数百种用例)。
我认为应该在这里引用它,以帮助人们为网页添加动画效果。
目前只能在Chromium浏览器中使用,https://caniuse.com/?search=View%20Transition%20API#:~:text=View%20Transitions%20API%20(single%2Ddocument)&text=Provides%20a%20mechanism%20for%20easily,document%20transitions%20is%20being%20planned
感谢https://www.bram.us/2023/05/09/rearrange-animate-css-grid-layouts-with-the-view-transition-api/的分享,我在发布这个答案之前找到了他的博客文章。 如果你对页面视图转换API感兴趣,请查看上面的谷歌链接获取更多信息。这个YouTube视频展示了他们如何在1-2小时内添加页面视图转换支持,也值得一看https://www.youtube.com/watch?v=5K5wNqCUrL8

-1
经过数小时的试错,我终于找到了一种动画网格列的方法。这会导致列间隙变为0,并且悬停字段会按其宽度扩展。
以下是我目前得到的内容:

$('.cards').each(function(){
  let cols = window.getComputedStyle($(this).get(0));
  let colGapAmount = cols.getPropertyValue("grid-template-columns").split(" ").length - 1;
  let card = $(this).find('.card');
  let standardColWidth = parseInt(card.css('width'), 10);
  let colGapSpace = colGapAmount * parseInt($(this).css('column-gap'), 10);
  let newSize = (standardColWidth + colGapSpace) + 'px';
  //Set Values
  $(this).closest('.cards').get(0).style.setProperty('--scaledSize', newSize);
  $(this).closest('.cards').get(0).style.setProperty('--normalSize', standardColWidth + 'px');
});

$('.card').hover(function(){
  $(this).closest('.cards').addClass('noGap');
},
function(){
  $(this).closest('.cards').removeClass('noGap');
});
.cards {
     --normaleSize: 100%;
     --scaledSize: unset;
     display: grid;
     grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
     column-gap: 20px;
     transition: column-gap 1s ease-in-out;
}
 .cards .card {
     transition: width 1s ease-in-out;
     border: 1px solid black;
     width: var(--normalSize);
}
 .cards .card:hover {
     width: var(--scaledSize);
}
 .cards.noGap {
     column-gap: 0px;
}
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="cards">
  <div class="card">1</div>
  <div class="card">2</div>
  <div class="card">3</div>
  <div class="card">4</div>
  <div class="card">5</div>
</section>

<section class="cards" style="margin-top: 3rem">
  <div class="card">1</div>
  <div class="card">2</div>
  <div class="card">3</div>
  <div class="card">4</div>
  <div class="card">5</div>
</section>


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