3

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

这是通过以与放大父组件相同的比例缩小子组件来实现的(父组件缩放到 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>

4

2 回答 2

7

问题

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

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

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

为了缓解这种情况,我们可以制作一个自定义的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 错误报告) ,动画不连贯


方法2:使用剪贴蒙版制作圆形抠图效果,为边框添加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 标签。边缘有时可能看起来与内部图像脱节。

于 2019-03-03T18:57:59.697 回答
1

这是与其他答案类似的想法,您可以仅使用一个元素来执行此操作。

增加宽度/高度。

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>

于 2019-03-03T19:44:55.183 回答