【问题标题】:CSS transition scale() - weird delayCSS 过渡比例() - 奇怪的延迟
【发布时间】:2019-03-04 05:39:02
【问题描述】:

运行下面的代码 sn-p - 点击圆圈来切换动画。应该发生的是,随着父容器的缩放,子容器应该看起来好像没有移动。

这是通过以与放大父组件相同的比例缩小子组件来实现的(父组件缩放为 4,子组件缩放为 0.25)。

当动画完成时,缩放是正确的,但是在动画过程中,它们看起来并没有一致地缩放。

这几乎就像父母先缩放,然后完成子缩放。

这是某种浏览器限制吗?还是我做错了什么?

谢谢!

const outer = document.querySelector('.outer');

outer.addEventListener('click', () => {
  outer.classList.toggle('outer--active');
});
body { overflow: hidden; }

.outer {
  width: 100px;
  height: 100px;
  overflow: hidden;
  border-radius: 100%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  transform-origin: top left;
  transition: transform 1s;
  cursor: pointer;
  border: 1px solid black;
}

.outer--active {
  transform: scale(4) translate(-50%, -50%);
}

.inner {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 400px;
  height: 400px;
  background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/14.jpg') center repeat;
  transform-origin: top left;
  transition: transform 1s;
}

.outer--active .inner {
  transform: scale(0.25) translate(-50%, -50%);
}
<div class="outer">
  <div class="inner"></div>
</div>

【问题讨论】:

    标签: css css-transitions css-transforms


    【解决方案1】:

    问题

    您看到的不是“浏览器错误”,而是对计算两个组合缩放的工作原理的误解。

    为简单起见,假设转换函数是linear(而不是ease,这是默认的计时函数)。在这种情况下,两个尺度的图如下:

    由于我们希望内部元素的最终比例保持不变,因此所有时间参数的 (scale up function) × (scale down function) = 1。不幸的是,如果我们进行乘法运算,结果我们会得到一个平方函数(在我们的例子中是 -¾x² + 3x + ¾)。这是您可以在过渡过程中看到的最终缩放的凸起。为了避免这种情况,我们需要在scale(1/m) css 规则中缩放m,而不是转换scale(n) 值。不幸的是,即使我们使用了 css 变量,我们也无法做到这一点,因为这些变量(还)不允许转换(请参阅this 答案)

    为了缓解这种情况,我们可以制作一个自定义的 cubic-bezier 计时函数,它是平方函数的倒数,但我无法手动执行此操作,并且可能 cubic-bezier 不会一直给出准确的曲线值,特别是如果我们想要基本计时函数不是linear

    解决办法

    方法一:我们可以改变外部div的尺寸,而不是缩放,如下所示:

    const outer = document.querySelector('.outer');
    
    outer.addEventListener('click', () => {
      outer.classList.toggle('outer--active');
    });
    body { overflow: hidden; }
    
    .outer {
      width: 100px;
      height: 100px;
      overflow: hidden;
      border-radius: 100%;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      transform-origin: top left;
      transition: all 1s;
      cursor: pointer;
      border: 1px solid black;
    }
    
    .outer--active {
      width: 400px;
      height: 400px;
    }
    
    .inner {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 400px;
      height: 400px;
      background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/14.jpg') center repeat;
      transform-origin: top left;
      transition: transform 1s;
    }
    <div class="outer">
      <div class="inner"></div>
    </div>

    优点:保留当前的 ​​html 标记结构

    缺点:由于浏览器关于亚像素过渡平滑的错误(例如firefox bug report),动画不连贯


    方法二:圆形抠图效果使用剪贴蒙版,边框添加div:

    const outer = document.querySelector('.outer');
    
    outer.addEventListener('click', () => {
      outer.classList.toggle('outer--active');
    });
    body { overflow: hidden; }
    
    .outer {
      width: 100px;
      height: 100px;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      transform-origin: top left;
      cursor: pointer;
    }
    
    .rim {
        width: 100px;
        height: 100px;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        border-radius: 100%;
        border: 1px solid black;
        transition: all 1s;
        transform-origin: top left;
    }
    
    .outer--active .rim {
        transform: scale(4)  translate(-50%, -50%);
    }
    
    .inner {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 400px;
      height: 400px;
      background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/14.jpg') center repeat;
      transform-origin: top left;
      transition: all 1s;
      clip-path: circle(50px at 200px 200px);
    }
    
    .outer--active .inner {
      clip-path: circle(200px at 200px 200px);
    }
    <div class="outer">
      <div class="inner"></div>
      <div class="rim"></div>
    </div>

    优点:平滑扩展

    缺点:需要为圆形边框/边缘添加另一个 html 标签。轮辋有时可能看起来与内部图像脱节。

    【讨论】:

    • 感谢您的彻底分解 - 解释得很好。由于性能和剪辑路径,我避免了宽度和高度缩放,因为它还没有我需要的浏览器兼容性。看来我需要考虑另一种过渡。再次感谢!
    • 很好,我第一次看到一个完整的答案,甚至没有编辑;)
    【解决方案2】:

    这是与其他答案类似的想法,您只需一个元素即可做到这一点。

    增加宽度/高度。

    const outer = document.querySelector('.outer');
    
    outer.addEventListener('click', () => {
      outer.classList.toggle('outer--active');
    });
    body { overflow: hidden; }
    
    .outer {
      width: calc(100px * var(--s,1));
      height: calc(100px * var(--s,1));
      border-radius: 100%;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background: 
        url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/14.jpg') center ;
      transition: all 1.5s;
      cursor: pointer;
      border: 1px solid black;
    }
    
    .outer--active {
      --s:4;
    }
    <div class="outer">
    </div>

    考虑到clip-path,我在其中添加了radial-gradient 来创建边框

    const outer = document.querySelector('.outer');
    
    outer.addEventListener('click', () => {
      outer.classList.toggle('outer--active');
    });
    body { overflow: hidden; }
    
    .outer {
      width: 400px;
      height: 400px;
      border-radius: 100%;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background: 
        radial-gradient(farthest-side,transparent calc(100% - 3px),#000 100%),
        url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/14.jpg');
      background-size:40% 40%,auto;
      background-position:center;
      transition: all 1.5s;
      cursor: pointer;
      -webkit-clip-path: circle(20% at 50% 50%);
      clip-path: circle(20% at 50% 50%);
    }
    
    .outer--active {
      -webkit-clip-path: circle(50% at 50% 50%);
      clip-path: circle(50% at 50% 50%);
      background-size:100% 100%,auto;
    }
    <div class="outer">
    </div>

    你也可以只考虑radial-gradient,但是点击的区域会更大,你不会有透明度:

    const outer = document.querySelector('.outer');
    
    outer.addEventListener('click', () => {
      outer.classList.toggle('outer--active');
    });
    body { overflow: hidden; }
    
    .outer {
      width: 400px;
      height: 400px;
      border-radius: 100%;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background: 
        radial-gradient(farthest-side,transparent calc(40% - 3px),#000 40%,#fff calc(40% + 1px)),
        url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/14.jpg');
      background-size:100% 100%,auto;
      background-position:center;
      transition: all 1.5s;
      cursor: pointer;
    }
    
    .outer--active {
      background-size:240% 240%,auto;
    }
    <div class="outer">
    </div>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-28
      • 2013-10-29
      • 2014-03-30
      • 2017-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-06
      相关资源
      最近更新 更多