将transform-origin移回CSS

时间:2017-05-01 18:40:00

标签: javascript html css animation css-transforms

当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移回其中心?或者如何获取多个转换值并将它们压缩为一个值,使其中的元素保持不变放置吗

4 个答案:

答案 0 :(得分:0)

您还可以使用group_id <- c(rep(1, 22), rep(2, 27), rep(3, 51), rep(4, 26), rep(5, 8), rep(6, 17), rep(7, 26), rep(8, 17), rep(9, 14)) element_id <- c(seq.int(1, 22), seq.int(1, 27), seq.int(1, 51), seq.int(1, 26), seq.int(1, 8), seq.int(1, 17), seq.int(1, 26), seq.int(1, 17), seq.int(1, 14)) segment_id <- c(rep(1, 22), rep(1, 13), rep(2, (24-13)), rep(3, (27-24)), rep(1, 16), rep(2, (30-16)), rep(3, (51-30)), rep(1, 14), rep(2, (26-14)), rep(1, 8), rep(1, 6), rep(2, (11-6)), rep(3, (17-11)), rep(1, 9), rep(2, (20-9)), rep(3, (26-20)), rep(1, 17), rep(1,14)) solution_df <- data.frame(group_id, element_id, segment_id) top翻译left。您可以在#v-wrp属性中创建动画。在transform-origin元素中尝试一下。我希望它现在能够正常运作。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

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

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

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

我已经复制了问题的片段并使其按预期工作。请检查以下代码以供参考。

'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>

答案 3 :(得分:0)

您将能够使用 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