CSS 从特定点开始缩放,当该点可能沿途发生变化时?
CSS scaling from a specific point when that point may change along the way?
我正在尝试构建一个应用程序,您可以在其中使用鼠标滚轮稍微放大一点,然后再进一步放大另一点。
换句话说,缩放的"origin"点可以沿途改变。
看看这个例子: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
以便能够转换一个 属性。但是,动画不是 "even" - 尤其是在第一次缩放时(它会再次放大和缩小 - 很难解释,但我希望你能看到它)。
示例中的底部框: 同时更改 transform-origin
和 transform
,它变得非常跳跃,因为 transform
有过渡而 transform-origin
没有。
最上面的例子是最理想的,但由于缩放不平滑,它仍然看起来不太好。如何在不使翻译跳转或再次放大和缩小的情况下放大不同阶段的点?
我认为您最好依靠 选项 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,zoom-in/out 效果是 (我认为) 因为您正在使用围绕您要执行的主要转换的两个翻译(模拟 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>
我们的目标是避免这种情况,让元素在缩放时直接到达其最终位置。一个想法是将转换分成两部分。诀窍是应用 one 带有比例的翻译,然后再应用另一个翻译。
这是一个例子:
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
的尝试以进行比较。
我正在尝试构建一个应用程序,您可以在其中使用鼠标滚轮稍微放大一点,然后再进一步放大另一点。
换句话说,缩放的"origin"点可以沿途改变。
看看这个例子: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
以便能够转换一个 属性。但是,动画不是 "even" - 尤其是在第一次缩放时(它会再次放大和缩小 - 很难解释,但我希望你能看到它)。
示例中的底部框: 同时更改 transform-origin
和 transform
,它变得非常跳跃,因为 transform
有过渡而 transform-origin
没有。
最上面的例子是最理想的,但由于缩放不平滑,它仍然看起来不太好。如何在不使翻译跳转或再次放大和缩小的情况下放大不同阶段的点?
我认为您最好依靠 选项 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,zoom-in/out 效果是 (我认为) 因为您正在使用围绕您要执行的主要转换的两个翻译(模拟 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>
我们的目标是避免这种情况,让元素在缩放时直接到达其最终位置。一个想法是将转换分成两部分。诀窍是应用 one 带有比例的翻译,然后再应用另一个翻译。
这是一个例子:
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
的尝试以进行比较。