代码之家  ›  专栏  ›  技术社区  ›  Mathias Lykkegaard Lorenzen

CSS从一个特定的点缩放,当那个点可能会改变?

  •  1
  • Mathias Lykkegaard Lorenzen  · 技术社区  · 6 年前

    我正在尝试构建一个应用程序,在该应用程序中,您可以使用鼠标滚轮将一个点放大一点,然后在另一个点进一步放大。

    换言之,缩放的“原点”可能会随过程而改变。

    看看这个例子: https://codesandbox.io/s/4w4m1k5zlx

    var phase = 1;
    
    var box1 = document.getElementById("box1");
    var box2 = document.getElementById("box2");
    
    box1.style.transformOrigin = "0 0";
    box2.style.transformOrigin = "0 0";
    
    var width = 100;
    var height = 100;
    
    function transform(originX, originY, translateX, translateY, scale) {
      transformElement(1, box1, originX, originY, translateX, translateY, scale);
      transformElement(2, box2, originX, originY, translateX, translateY, scale);
    }
    
    function transformElement(
      method,
      element,
      originX,
      originY,
      translateX,
      translateY,
      scale
    ) {
      element.style.transition = "transform 1s linear";
    
      if (method === 1) {
        element.style.transform = `translate(${originX}px, ${originY}px) translate(${translateX}px, ${translateY}px) scale(${scale}) translate(-${originX}px, -${originY}px)`;
      } else if (method === 2) {
        element.style.transformOrigin = `${originX}px ${originY}px`;
        element.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
      }
    
      var pointElement = document.createElement("div");
      pointElement.classList.add("point");
      pointElement.style.transform = `translate(${originX}px, ${originY -
        2 * scale}px)`;
    
      element.appendChild(pointElement);
    }
    
    function reset() {
      resetElement(box1);
      resetElement(box2);
    }
    
    function resetElement(element) {
      while (element.children.length > 0) {
        element.removeChild(element.children[0]);
      }
    
      element.style.transform = "";
      element.style.transition = "";
    
      void element.clientWidth;
    }
    
    function phase1() {
      transform(width * 0.75, height / 2, 0, 0, 1.5);
    }
    
    function phase2() {
      transform(width * 0.25, height / 2, 0, 0, 2);
    }
    
    function phase3() {
      transform(width / 2, height, 0, 0, 2.5);
    }
    
    function phase4() {
      transform(width / 2, 0, 0, 0, 3);
    }
    
    const phases = [reset, phase1, phase2, phase3, phase4];
    
    setInterval(() => phases[phase++ % phases.length](), 1500);
    * {
      box-sizing: border-box;
    }
    
    body {
      background-color: black;
    }
    
    .container {
      position: relative;
      margin: 60px;
      background-color: lightgray;
      width: 200px;
      height: 200px;
    }
    
    .point {
      width: 2px;
      height: 2px;
      background-color: white;
    }
    
    .box {
      position: absolute;
      top: 25%;
      left: 25%;
      transform-origin: 0 0;
      background-color: teal;
      opacity: 0.8;
      width: 100px;
      height: 100px;
    }
    
    .outline {
      background-color: transparent;
      border: 1px solid black;
    }
    <div class="container">
      <div class="box outline">
      </div>
      <div id="box1" class="box"></div>
    </div>
    <div class="container">
      <div class="box outline">
      </div>
      <div id="box2" class="box"></div>
    </div>

    示例中的顶框: 在这里我试着模拟 transform-origin 具有 transform 为了能够转换一个属性。然而,动画并不是“偶数”,特别是在第一次缩放时(在第一次缩放时,它会再次放大和缩小一点),很难解释,但我希望你能看到它。

    示例中的底框: 同时改变 变换原点 转型 同时,它变得非常紧张,因为 转型 有一个过渡和 变换原点 不。

    上面的例子是最理想的,但是由于缩放不流畅,它看起来仍然不太好。如何在不同阶段放大点,而不进行跳转或再次放大和缩小?

    1 回复  |  直到 6 年前
        1
  •  3
  •   Temani Afif    6 年前

    我认为你能做的最好的就是依靠 选项2 . 可以将转换应用于 transform-origin 再加上一个延迟 transform 因此,首先更改原点,然后进行变换:

    transform 1s linear 0.5s, transform-origin 0.5s
    

    完整代码:

    var phase = 1;
    
    var box2 = document.getElementById("box2");
    
    box2.style.transformOrigin = "0 0";
    
    var width = 100;
    var height = 100;
    
    function transform(originX, originY, translateX, translateY, scale) {
      transformElement(2, box2, originX, originY, translateX, translateY, scale);
    }
    
    function transformElement(
      method,
      element,
      originX,
      originY,
      translateX,
      translateY,
      scale
    ) {
      element.style.transition = "transform 1s linear 0.5s,transform-origin 0.5s";
    
      element.style.transformOrigin = `${originX}px ${originY}px`;
      element.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
    
      var pointElement = document.createElement("div");
      pointElement.classList.add("point");
      pointElement.style.transform = `translate(${originX}px, ${originY -
        2 * scale}px)`;
    
      element.appendChild(pointElement);
    }
    
    function reset() {
      resetElement(box2);
    }
    
    function resetElement(element) {
      while (element.children.length > 0) {
        element.removeChild(element.children[0]);
      }
    
      element.style.transform = "";
      element.style.transition = "";
    
      void element.clientWidth;
    }
    
    function phase1() {
      transform(width * 0.75, height / 2, 0, 0, 1.5);
    }
    
    function phase2() {
      transform(width * 0.25, height / 2, 0, 0, 2);
    }
    
    function phase3() {
      transform(width / 2, height, 0, 0, 2.5);
    }
    
    function phase4() {
      transform(width / 2, 0, 0, 0, 3);
    }
    
    const phases = [reset, phase1, phase2, phase3, phase4];
    
    setInterval(() => phases[phase++ % phases.length](), 1500);
    * {
      box-sizing: border-box;
    }
    
    body {
      background-color: black;
    }
    
    .container {
      position: relative;
      margin: 50px;
      background-color: lightgray;
      width: 200px;
      height: 200px;
    }
    
    .point {
      width: 2px;
      height: 2px;
      background-color: white;
    }
    
    .box {
      position: absolute;
      top: 25%;
      left: 25%;
      transform-origin: 0 0;
      background-color: teal;
      opacity: 0.8;
      width: 100px;
      height: 100px;
    }
    
    .outline {
      background-color: transparent;
      border: 1px solid black;
    }
    <div class="container">
      <div class="box outline">
      </div>
      <div id="box2" class="box"></div>
    </div>

    更新

    考虑到 方案1 ,放大/缩小效果为 (我想) 由于您使用两个翻译(模拟 变换原点 )围绕着你想要做的主要转变。所以效果是:移动到A点,缩放元素,移动到B点。

    下面是一个简单的例子来说明这个问题 弹跳效应 . 我们可以清楚地看到从右到左的轻微运动。

    .box {
      width:100px;
      height:100px;
      margin-left:200px;
      background:red;
      transition:2s all;
      transform-origin:0 0;
    }
    
    body:hover .box{
      transform:translate(100px,0) scale(2) translate(-100px,0);
    }
    
    body {
     margin:0;
     background:repeating-linear-gradient(to right,transparent 0px,transparent 98px,#000 98px,#000 100px)
    }
    <div class="box">
    
    </div>

    我们的目标是避免这种情况,并使元素在进行缩放时直接到达其最终位置。一个想法是将转换分为两部分。诀窍是运用 用比例尺进行翻译,然后再应用另一种翻译。

    下面是一个例子:

    document.querySelector('body').addEventListener('mouseover',function(){
      document.querySelector('.box').style.transform="scale(2) translate(-100px,0)";
      setTimeout(function(){
          document.querySelector('.box').style.transform="translate(100px,0) scale(2) translate(-100px,0)";
      },500);
    })
    document.querySelector('body').addEventListener('mouseleave',function(){
      document.querySelector('.box').style.transform="none";
    })
    .box {
      width:100px;
      height:100px;
      margin-left:200px;
      background:red;
      transition:1s all linear .5s;
      transform-origin:0 0;
    }
    
    body {
     margin:0;
     background:repeating-linear-gradient(to right,transparent 0px,transparent 98px,#000 98px,#000 100px)
    }
    <DIV class=“box”>
    
    </DIV>

    正如我们所看到的,我们不再有反弹的效果。红色 div 不再是向右然后向左,而是只向左。这个想法有点疯狂,很难解释,但技巧是使用延迟。

    在悬停状态下,我们添加一个转换和缩放,然后由于延迟,DIV将不会移动。在延迟结束后,我们通过添加另一个转换来更改转换。这将使 div 只考虑到它的新起源,我们模拟了两个翻译。


    以下是完整代码:

    var phase = 1;
    
    var box1 = document.getElementById("box1");
    var box2 = document.getElementById("box2");
    
    
    var width = 100;
    var height = 100;
    
    function transform(originX, originY,  scale) {
      transformElement(1, box1, originX, originY, scale);
      transformElement(2, box2, originX, originY, scale);
    }
    
    function transformElement(
      method,
      element,
      originX,
      originY,
      scale
    ) {
    
      if (method === 1) {
        element.style.transform =`scale(${scale}) translate(-${originX}px, -${originY}px)`;
        setTimeout(function(){
    				element.style.transform = `translate(${originX}px, ${originY}px) scale(${scale}) translate(-${originX}px, -${originY}px)`;
        },200,element,scale,originX,originY)
        
        
      } else if (method === 2) {
        element.style.transformOrigin = `${originX}px ${originY}px`;
        element.style.transform = ` scale(${scale})`;
      }
    
      var pointElement = document.createElement("div");
      pointElement.classList.add("point");
      pointElement.style.transform = `translate(${originX}px, ${originY -
        2 * scale}px)`;
    
      element.appendChild(pointElement);
    }
    
    function reset() {
      resetElement(box1);
      resetElement(box2);
    }
    
    function resetElement(element) {
      while (element.children.length > 0) {
        element.removeChild(element.children[0]);
      }
    
      element.style.transform = "";
      element.style.transition = "";
    
      void element.clientWidth;
    }
    
    function phase1() {
      transform(width * 0.75, height / 2, 1.5);
    }
    
    function phase2() {
      transform(width * 0.25, height / 2,  2);
    }
    
    function phase3() {
      transform(width / 2, height,  2.5);
    }
    
    function phase4() {
      transform(width / 2, 0,  3);
    }
    
    const phases = [reset, phase1, phase2, phase3, phase4];
    
    setInterval(() => phases[phase++ % phases.length](), 1400);
    * {
      box-sizing: border-box;
    }
    
    .container {
      position: relative;
      display:inline-block;
      margin: 50px;
      background-color: lightgray;
      width: 200px;
      height: 200px;
    }
    
    .point {
      width: 2px;
      height: 2px;
      background-color: white;
    }
    
    .box {
      position: absolute;
      top: 25%;
      left: 25%;
      transform-origin: 0 0;
      background-color: teal;
      opacity: 0.8;
      width: 100px;
      height: 100px;
      transition:transform 1s linear 0.2s, transform-origin 0.2s;
    }
    
    .outline {
      background-color: transparent;
      border: 1px solid black;
    }
    <div class="container">
      <div class="box outline">
      </div>
      <div id="box1" class="box"></div>
    </div>
    <div class="container">
      <div class="box outline">
      </div>
      <div id="box2" class="box"></div>
    </div>

    我简化了JS,并保留了依赖于更改 变换原点 首先要比较。