【问题标题】:Move transform-origin back to the center of the element in CSS在 CSS 中将 transform-origin 移回元素的中心
【发布时间】:2017-09-29 04:30:29
【问题描述】:

当 CSS 元素从其原始位置转换出来时,transform-origin 不会随之移动;使所有后续的变换和旋转仍然源自它的原始transform-origin(示例如下)。

据我所知,有一种方法可以解决这个问题……那就是继续将所有新的变换添加到以前的变换的右侧。像这样:transform: translateY ( -5rem ) rotate( 45deg ) translateY( -10rem ) rotate( 15deg )... 等。这似乎总是根据需要从当前元素的中心开始新的变换。

问题

当您使用这种技术根据用户输入转换元素时,您将不断地向 DOM 添加转换......无休止。占用大量内存并导致其他不需要的滑动效果(可能)。

这是一个动画,展示了transform-origin 在每次变换后如何不移动到元素的中心:

'use strict';

function firstRotation() { 
  var v = document.getElementById( 'v-wrp' ),
      status = document.getElementById( 'status' )
      
  setTimeout( function() { 
    status.innerHTML = 'First, the V is rotated around it\'s central axis.\
                          The <b>transform-origin</b> is centered.';
    
    v.classList.add( 'first-rotation' );
    status.classList.add( 'update' );
  }, 1000 ); 
}

function firstTranslation() { 
  var v = document.getElementById( 'v-wrp' ),
      status = document.getElementById( 'status' )
      
  setTimeout( function() { 
    status.innerHTML = 'Next, the element is translated forward in it\'s \
                        current orientation. The <b>transform-origin</b> stays\
                        behind where it was.';
                        
    v.classList.remove( 'first-rotation' );
    v.classList.add( 'first-translation' );
  }, 6000 ); 
}

function info() { 
  var v = document.getElementById( 'v-wrp' ),
      status = document.getElementById( 'status' )
      
  setTimeout( function() { 
    status.innerHTML = 'This next animation is where it\'s evident that\
                        the <b>transform-origin</b> is no longer centered, but\
                        back where it was at the beginning of these transforms';
    
  }, 11000 ); 
}

function lastRotation() { 
  var v = document.getElementById( 'v-wrp' ),
      status = document.getElementById( 'status' )
      
  setTimeout( function() { 
    status.innerHTML = 'This last rotation is far wider than desired because the\
                        transform origin is back where it started.'
  
    v.classList.remove( 'first-translation' );
    v.classList.add( 'last-rotation' );
  }, 16000 ); 
}

function end() { 
  var v = document.getElementById( 'v-wrp' ),
      status = document.getElementById( 'status' )
      
  setTimeout( function() { 
    status.classList.remove( 'update' );
  }, 21000 ); 
}

function start() {
  firstRotation();
  firstTranslation();
  info();
  lastRotation();
  end();
}

start();
/* / / / / / / / / / / / / / ANIMATION DEFINITIONS / / / / / / / / / / / / / */
.first-rotation, .first-translation, .update, .last-rotation {
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-fill-mode: forwards;
}
.first-rotation {
  animation-name: first-rotation;
}
.first-translation {
  animation-name: first-translation;
}
.update {
  animation-name: update;
  animation-iteration-count: infinite;
}
.last-rotation {
  animation-name: last-rotation;
}

/*/ / / / / / / / / / / / / / ANIMATION KEYFRAMES / / / / / / / / / / / / / /*/
@keyframes first-rotation {
  100% {
    transform: rotate( 315deg );
  }
}
@keyframes first-translation {
  0% {
    transform: rotate( 315deg );
  }
  100% {
    transform: rotate( 315deg ) translate( 0, -5rem );
  }
}
@keyframes update {
  0% {
    background-color: mediumslateblue;
  }
}
@keyframes last-rotation {
  0% {
    transform: rotate( 315deg ) translate( 0, -5rem );
  }
  100% {
    transform: rotate( 400deg ) translate( 0, -5rem );
  }
}
<head>
  <style>
    @import url( "https://fonts.googleapis.com/css?family=Nunito" );

    html {
      overflow: hidden;
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100%;
      font-family: "Nunito", sans-serif;
    }

    html,
    body {
      margin: 0;
      padding: 0;
    }

    .v {
      display: block;
      font-size: 2rem;
      transform: rotate( 180deg );
    }

    p {
      width: 100%;
      padding: 0.5rem;
      position: absolute;
      bottom: 0;
      left: 0;
    }
  </style>
</head>
<body>
  <div id="v-wrp" class="v-wrp">
    <b class="v">V</b>
  </div>
  
  <p id="status" class="status"></p>
</body>

问题

我需要在 CSS 或 JS 中找到一种方法来重置 transform-origin 的位置或将其移回转换后元素的中心。在右侧添加更多变换是一种在实时交互环境中不适合我的技术。可以在这里看到一个尝试:Transforms are added...endlessly

如何计算转换元素的位置并将transform-origin 移回其中心,或者如何获取多个转换值并将它们压缩为一个值以使元素保持在同一个位置?

【问题讨论】:

  • 您应该将旋转应用于 .v 包装器,而不是其父级。试试看:)
  • @GCyrillus 动画应用于v-wrp 元素。如果我向它的父母申请,那将是body? "var v = document.getElementById( 'v-wrp' ),"
  • 您可以为元素的位置设置动画,而不是使用变换进行翻译。即使这不是最佳实践。当您使用 top/left/... 移动元素时,整个元素都在移动,包括它的所有属性,包括 transform-origin。

标签: javascript html css animation css-transforms


【解决方案1】:

您也可以使用topleft 来翻译#v-wrp。您可以在transform-origin 属性中创建动画。在&lt;b&gt; 元素中尝试一下。我希望它现在可以正常工作。

【讨论】:

    【解决方案2】:

    您的问题是结构性问题。保持参考点 (transform-origin) 不变,并按层分离旋转和平移变换(从其原点平移的包含元素和根据需要旋转的内部包含元素)。

    从结构上分离这些转换元素将从一开始就防止诸如此类的“跨域”问题。

    【讨论】:

      【解决方案3】:

      如果有人还在寻找这样的东西。

      @jmcgrory 是对的,有单独的层真的很方便。但是对于某些情况,以下将起作用。

      如果您可以使用 svg,那么 transform-b​​ox 可能会有所帮助。

      但是如果你使用任何其他元素会很困难,因为引用框(可以认为是元素的初始位置)在应用变换后不会改变,并且transform-origin将根据其引用框进行处理。 transform-origin 需要与 translate 一起移动,它变得更复杂 transform: rotate 因为您必须计算元素移动的 x 和 y 长度,并基于此您需要替换 transform(如果用于任何一个轴)到 x 和 y 并相应地更新变换原点,这种方法非常棘手,但它工作得很好并且完全取决于元素移动的数学计算(你不需要写太多的 css 但必须做一些计算)。

      我已经从问题中复制了 sn-p 并使其按预期工作。请检查以下代码以供参考。

      'use strict';
      
      function firstRotation() { 
        var v = document.getElementById( 'v-wrp' ),
            status = document.getElementById( 'status' )
            
        setTimeout( function() { 
          status.innerHTML = 'First, the V is rotated around it\'s central axis.\
                                The <b>transform-origin</b> is centered.';
          
          v.classList.add( 'first-rotation' );
          status.classList.add( 'update' );
        }, 1000 ); 
      }
      
      function firstTranslation() { 
        var v = document.getElementById( 'v-wrp' ),
            status = document.getElementById( 'status' )
            
        setTimeout( function() { 
          status.innerHTML = 'Next, the element is translated forward in it\'s \
                              current orientation. The <b>transform-origin</b> stays\
                              behind where it was.';
                              
          v.classList.remove( 'first-rotation' );
          v.classList.add( 'first-translation' );
        }, 6000 ); 
      }
      
      function info() { 
        var v = document.getElementById( 'v-wrp' ),
            status = document.getElementById( 'status' )
            
        setTimeout( function() { 
          status.innerHTML = 'This next animation is where it\'s evident that\
                              the <b>transform-origin</b> is no longer centered, but\
                              back where it was at the beginning of these transforms';
          
        }, 11000 ); 
      }
      
      function lastRotation() { 
        var v = document.getElementById( 'v-wrp' ),
            status = document.getElementById( 'status' )
            
        setTimeout( function() { 
          status.innerHTML = 'This last rotation is working correctly with calculated transform-origin.'
        
          v.classList.remove( 'first-translation' );
          v.classList.add( 'last-rotation' );
        }, 16000 ); 
      }
      
      function end() { 
        var v = document.getElementById( 'v-wrp' ),
            status = document.getElementById( 'status' )
            
        setTimeout( function() { 
          status.classList.remove( 'update' );
        }, 21000 ); 
      }
      
      function start() {
        firstRotation();
        firstTranslation();
        info();
        lastRotation();
        end();
      }
      
      start();
      /* / / / / / / / / / / / / / ANIMATION DEFINITIONS / / / / / / / / / / / / / */
      .first-rotation, .first-translation, .update, .last-rotation {
        animation-duration: 5s;
        animation-timing-function: ease-in-out;
        animation-fill-mode: forwards;
      }
      .first-rotation {
        animation-name: first-rotation;
      }
      .first-translation {
        animation-name: first-translation;
      }
      .update {
        animation-name: update;
        animation-iteration-count: infinite;
      }
      .last-rotation {
        animation-name: last-rotation;
      }
      
      /*/ / / / / / / / / / / / / / ANIMATION KEYFRAMES / / / / / / / / / / / / / /*/
      @keyframes first-rotation {
        100% {
          transform: rotate( 315deg );
        }
      }
      @keyframes first-translation {
        0% {
          transform: rotate( 315deg );
        }
        100% {
          transform-origin: calc(-3.536em + 10px) calc(-3.536em + 22px);
          transform: rotate( 315deg ) translate( -3.536rem, -3.536rem );
        }
      }
      @keyframes update {
        0% {
          background-color: mediumslateblue;
        }
      }
      @keyframes last-rotation {
        0% {
          transform-origin: calc(-3.536em + 10px) calc(-3.536em + 22px);
          transform: rotate( 315deg ) translate( -3.536rem, -3.536rem );
        }
        100% {
          transform-origin: calc(-3.536em + 10px) calc(-3.536em + 22px);
          transform: rotate( 400deg ) translate( -3.536rem, -3.536rem );
        }
      }
      <head>
        <style>
          @import url( "https://fonts.googleapis.com/css?family=Nunito" );
      
          html {
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100%;
            font-family: "Nunito", sans-serif;
          }
      
          html,
          body {
            margin: 0;
            padding: 0;
          }
      
          .v {
            display: block;
            font-size: 2rem;
            transform: rotate( 180deg );
          }
      
          p {
            width: 100%;
            padding: 0.5rem;
            position: absolute;
            bottom: 0;
            left: 0;
          }
        </style>
      </head>
      <body>
        <div id="v-wrp" class="v-wrp">
          <b class="v">V</b>
        </div>
        
        <p id="status" class="status"></p>
      </body>

      【讨论】:

        【解决方案4】:

        您将能够使用 JavaScript 的 DOMMatrix 或您自己的值来跟踪平移和旋转值。

        这是一个非常基本的示例,例如您的小行星游戏: https://jsfiddle.net/omjktrsh/

        矩阵为您提供了操作它的函数,而无需学习所有数学知识。 2D 变体有六个属性,它们是 3x3 矩阵的一部分:

        a c e
        b d f
        0 0 1
        

        地点:

        • a = x 轴刻度
        • b = y 轴剪切
        • c = x 轴剪切
        • d = y 轴刻度
        • e = x 轴平移
        • f = y 轴平移

        您可以使用rotateSelf(deg) 让矩阵对其当前值执行旋转,或使用rotate(deg) 让矩阵返回一个新的矩阵并添加旋转(保留原始矩阵的相同值)。

        您可以使用translateSelf(x, y) 相对于其当前旋转来移动它。如果您想相对于当前旋转移动它,您可以像这样直接操作ef 值:

        matrix.e += x; // x axis translation amount
        matrix.f += y; // y axis translation amount
        

        您可以轻松应用任意数量的旋转,而无需在 CSS 中堆叠它们,然后使用toString() 以 CSS 形式输出矩阵:

        const matrix = new DOMMatrix();
        const el = document.getElementById("transform-me");
        // manipulate matrix as much as you want
        ...
        matrix.translateSelf(...);
        ...
        matrix.rotateSelf(...);
        ...
        // apply matrix to elements css -> matrix(a, b, c, d, e, f)
        el.style.transform = matrix.toString();
        

        缺点是 DOMMatrix 浏览器支持较少:https://caniuse.com/dommatrix

        如果您想支持不支持 DOMMatrix 的浏览器,您仍然可以在这些浏览器中使用自己的数学运算和 transform: matrix(a, b, c, d, e, f) 语法:https://caniuse.com/transforms2d

        如果您想了解二维变换矩阵背后的数学原理,请参阅 Wikipedia 上的示例。请注意,某些资源使用上述矩阵的翻转矩阵:

        a b 0
        c d 0
        e f 1
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-01-26
          • 2017-07-04
          • 2011-10-12
          • 2015-09-18
          • 2020-01-13
          • 1970-01-01
          • 1970-01-01
          • 2013-10-21
          相关资源
          最近更新 更多