先有问题再有答案
transform有哪些应用场景?
为什么transform性能较好?
移动变化的本质原理是什么?
translate移动的基准点哪里?
视觉位置和dom位置有什么区别?
translate(100%)是相对于父元素还是自身?
transform应用场景
transform
属性在网页设计中非常强大,能够创建多种动画和交互效果。通过结合不同的变换函数(如 translate
、scale
、rotate
和 skew
),可以实现丰富的视觉效果,并且利用 GPU 加速,确保性能良好。
transform
属性的应用场景可以大致分为两类:视觉变换和动画效果。
视觉变换
主要用于静态布局调整和简单的视觉效果
例如实现元素居中:
<div class="container">
<div class="box"></div>
</div>
.container {
position: relative;
width: 200px;
height: 200px;
background-color: lightgray;
}
.box {
position: absolute;
width: 100px;
height: 100px;
background-color: coral;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
动画变换
与 transition
或 animation
一起使用,创建复杂和高效的动画效果,如平滑的移动、旋转、缩放等。
<div class="animated-box"></div>
.animated-box {
width: 100px;
height: 100px;
background-color: skyblue;
transition: transform 0.5s ease;
}
.animated-box:hover {
transform: rotate(45deg) scale(1.2);
}
transform的本质:
视觉变换而非布局变换
- 使用
transform
时,元素的实际位置(在 DOM 中的位置)并没有发生变化,而是通过变换矩阵(Transformation Matrix)来改变元素的视觉呈现。 - 浏览器使用一个 4x4 的变换矩阵来计算元素的最终位置和形状。
translateX
、translateY
、scale
、rotate
等操作都会更新这个矩阵,最终由 GPU 渲染出变换后的效果。
变换矩阵的作用
- 变换矩阵是一个数学工具,用来描述元素的各种变换(平移、缩放、旋转等)。例如:
translateX(100px)
会更新矩阵中的平移值,使元素在屏幕上向右移动 100 像素。scale(2)
会更新矩阵中的缩放值,使元素在视觉上放大两倍。
- 这些变换操作是直接在 GPU 上完成的,因此非常高效。
transform性能好的原因
1. 不触发重排(Reflow)和重绘(Repaint)
- 当你使用
transform
属性改变元素的位置、大小或旋转时,它 不会影响文档流,也不会导致页面的重新布局(重排)。 - 传统的属性(如
top
、left
等)会改变元素的布局位置,导致浏览器重新计算布局(重排),这是一项性能开销较大的操作。 transform
只影响元素的视觉呈现,而不会改变 DOM 树或布局,因此避免了重排和重绘。
2. 硬件加速(GPU 加速)
transform
属性可以利用 GPU 进行硬件加速,而不是依赖 CPU 的计算。- 当使用
transform
时,浏览器会将相关的元素提升到一个单独的渲染层(composite layer),并在 GPU 上执行渲染操作。这种方式可以显著提高性能,尤其是在动画中。 - 传统的
top
、left
等属性通常由 CPU 处理,性能会相对较差,尤其是在复杂页面或低性能设备上。
3. 合成阶段(Compositing)优化
transform
属性的变换通常在浏览器的合成线程(compositing thread)上起作用,而不是在主线程上。这是 transform
属性在性能优化方面的一个重要优势。
详细参考 浏览器:帧&渲染流程
-
浏览器的渲染过程分为多步,包括:
- 布局(Layout): 计算元素的位置和大小。
- 绘制(Paint): 绘制像素内容。
- 合成(Compositing): 将不同的图层组合在一起,渲染到屏幕上。
-
当应用
transform
属性时,浏览器可能会将元素提升为一个独立的合成层(compositing layer)。这使得变换操作只需在最后的 合成阶段 进行调整,而无需重新布局或重新绘制。这种优化使得transform
操作,可以在合成线程处理,而不需要经过主线程的繁重计算。有效的缩短了渲染路径
translate(100%)的基准点与取值
基准点
transform: translate(100%)
是相对于元素 自身的坐标系
来进行移动的, 而不是其视觉位置或父级容器
的位置。
使用 transform: translateX(100%)
时,元素的实际 DOM 位置不变,但在视觉上,它会被移动。也就是说,元素在文档流中的占位仍然保持不变,但在页面上看到的效果是它被移动了。
具体来说元素所在位置为ele1,当应用translate(100%)
时会将元素向右移动其自身宽度的100%即视觉上来到ele2的位置。
在此基础上 我们在设置translate(-100%)
这时候虽然视觉上在ele2的位置 但是原始点依然在ele1的位置,所以当我们设置值为-100%
时 元素会移动到ele3的位置。
百分比取值
100%
的取值是相对于 元素自身的宽度
,而不是其父元素的宽度。这意味着无论父元素有多宽,translateX(100%)
总是移动元素自身宽度的 100%。
效果
累计值
所以当我们想让元素以视觉所在位置为基准点移动时 需要对transform的值做累积处理。即先获取当前值 然后再加减对应的具体值。
<script setup lang="ts">
import { ref } from 'vue';
const boxStyle = ref({});
const moveX100Percent = () => {
boxStyle.value = {
transform: 'translateX(100%)',
};
};
const moveXLeft100Percent = () => {
boxStyle.value = {
transform: 'translateX(-100%)',
};
};
const moveX20Px = () => {
// 如果 boxStyle.value.transform 已经存在,则在其基础上追加移动距离
if (boxStyle.value.transform) {
boxStyle.value.transform += ' translateX(20px)';
} else {
boxStyle.value.transform = 'translateX(20px)';
}
};
</script>
<template>
<div class="page">
<div class="parent">
<div :class="['son']" :style="boxStyle"></div>
</div>
</div>
<div class="btn" @click="moveX100Percent">moveX100%</div>
<div class="btn" @click="moveX20Px">right10</div>
<div class="btn" @click="moveXLeft100Percent">moveXLeft100%</div>
</template>
<style lang="scss" scoped>
.parent {
margin-top: 100px;
margin-left: 120px;
width: 100px;
height: 50px;
background-color: red;
}
.son {
width: 100px;
height: 50px;
background-color: blue;
transition: transform 0.5s ease-in-out;
}
.btn {
margin-top: 20px;
display: inline-block;
padding: 15px 20px;
border-radius: 15px;
background: green;
}
</style>