在 CSS 网格布局中使用 CSS 转换

Using CSS transitions in CSS Grid Layout

我正在尝试让我的粘性 header 具有过渡效果,以便它缓和而不仅仅是快速移动。

我做错了什么?

这是我的工作版本:

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

基本上下面的代码将 class tiny 添加到我的包装器 class,这工作正常。

$(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;
}

所以 header 确实缩小了,但没有动画,我是不是漏掉了什么,或者过渡是否根本不适用于网格?

这是 css-grid 文档的 link。

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>

根据规范,转换应该适用于 grid-template-columnsgrid-template-rows

7.2. Explicit Track Sizing: the grid-template-rows and grid-template-columns properties

Animatable: as a simple list of length, percentage, or calc, provided the only differences are the values of the length, percentage, or calc components in the list

因此,如果我的解释是正确的,只要仅更改属性的值,而不更改规则的结构,转换就应该有效。 但他们没有


更新

This does work but is so far only implemented in Firefox. Follow here for other browser updates. https://codepen.io/matuzo/post/animating-css-grid-layout-properties

~ @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 demo

在测试中,过渡适用于高度和背景颜色,但不适用于 grid-template-rowsgrid-template-columns

作为解决方法,您可以使用网格项目的大小来代替 grid-template-columnsgrid-template-rows

你可以这样做:

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 与网格一起使用。

另一种方法是使用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; }
}

目前网格模板上的转换不起作用。但是你可以像这样使用转换:

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>

在我的例子中,我尝试使用以下代码打开侧边栏菜单:

.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;
}

过渡不适用于网格模板列。这是我的解决方案:

.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;
}

也许,它对某人有帮助。

我使用 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>

我找到了一个解决方法,但它要求至少有一个其他列填满宽度。要转换的列必须是 grid-template-columns 中的 auto。而另外一个必须是1fr,否则auto栏会使用这个缺失的space。此外,要转换的列在两种状态下都必须具有绝对宽度。

这里是:

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,并且悬停字段会按其宽度扩展。

这是我目前得到的:

$('.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>