【问题标题】:CSS scaling from a specific point when that point may change along the way?当该点可能会沿途改变时,CSS从特定点缩放?
【发布时间】:2018-12-19 04:52:21
【问题描述】:

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

换句话说,缩放的“原点”可以沿途改变。

看看这个例子: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 模拟transform-origin,以便能够转换一个属性。然而,动画并不“均匀”——尤其是在第一个比例期间(它会再次放大和缩小一点——很难解释,但我希望你能看到)。

示例中的底部框: 同时更改transform-origintransform 会变得非常跳跃,因为transform 有一个过渡而transform-origin 没有.

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

【问题讨论】:

标签: javascript css css-transitions css-transforms


【解决方案1】:

我认为您能做的最好的事情就是依靠选项 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,放大/缩小效果是(我认为),因为您使用了两个翻译(模拟transform-origin ) 围绕您要进行的主要转换。所以效果是这样的:移动到点 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,并保留了第一次尝试,即首先更改 transform-origin 以进行比较。

【讨论】:

  • 大师发话了!不幸的是,对于我正在开发的产品来说,首先更改原点并不被认为是可以接受的。
  • @MathiasLykkegaardLorenzen 在这种情况下,您可以保留这个想法并尝试使用延迟和计时功能。例如,您可以同时更改它们,但使变换具有缓入效果以及原点和缓出效果.. 像这样:jsfiddle.net/2h4oaeu1/1
  • 谢谢,不幸的是,当捏缩放(在我的实际应用程序中也支持)时,在部分放大内容后更改原点时,这看起来很有趣。我真的相信我们必须以某种方式使选项 1 起作用。你怎么看?
  • @MathiasLykkegaardLorenzen 选项 1 不容易处理,因为我们将平移和缩放相结合的方式很复杂。我尝试使用它,但这并不容易:/因为所有属性都在一个变换中
  • @MathiasLykkegaardLorenzen 顺便等一下,会有更多高手有其他想法 ;)
猜你喜欢
  • 2015-08-24
  • 2016-01-05
  • 1970-01-01
  • 1970-01-01
  • 2022-11-04
  • 2015-12-26
  • 2014-08-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多