元素的平面几何变换:旋转、缩放、倾斜、平移

TailwindCSS

旋转

rotate系列类名用来很方便的对元素进行旋转操作。基本写法有四种,如下所示:

写法 示例 解释
rotate-角度 rotate-45 将元素顺时针旋转45度
-rotate-角度 -rotate-45 将元素逆时针旋转45度
rotate-[任意值] rotate-[3.142rad] 将元素顺时针旋转3.142弧度

注意是没有-rotate-[任意值]这个写法的,如果想让元素以逆时针旋转指定角度,那么其实应该把“负”号加在“值”里面。

下面是例子:

    <div class="flex p-10 justify-between w-[800px]">
        <div class="size-30 outline-1 hover:rotate-15">顺时针旋转15度</div>
        <div class="size-30 outline-1 hover:-rotate-15">逆时针旋转15度</div>
        <div class="size-30 outline-1 hover:rotate-[0.6rad]">顺时针旋转0.6弧度</div>
        <div class="size-30 outline-1 hover:rotate-[-0.55rad]">逆时针旋转0.55弧度(顺时针旋转-0.55弧度)</div>
    </div>

旋转中心点

默认情况下,旋转的中心点位于元素的几何中心。可以通过origin系列类名来设定旋转的中心点,基本写法就是:

origin-位置

其中位置可以是center,也可以是top, bottom, left, right,分别代表上、下、左、右边线的中心点。

也可以将top/bottomleft/right组合起来,代表四个顶点,比如top-left代表左上顶点,bottom-right代表右下顶点。

如果你不满足于这几个位置,想自由的设定位置,则可以通过方括号的语法,即origin-[坐标位置]来灵活指定。其中坐标位置的写法是X坐标_Y坐标用下划线连接,坐标系是屏幕坐标系:即X轴向右,Y轴向下,原点在左上角。

通过这种方式,可以把旋转点设置在元素的几何范围外部。

以下是例子

    <span>元素围绕不同的点顺时针旋转45度</span>
    <div class="flex flex-col">
        <div class="flex p-10 justify-between w-[800px]">
            <div tabindex="0" class="size-30 outline-1 active:rotate-45 origin-left">origin-left</div>
            <div tabindex="0" class="size-30 outline-1 active:rotate-45 origin-right">origin-right</div>
            <div tabindex="0" class="size-30 outline-1 active:rotate-45 origin-top">origin-top</div>
            <div tabindex="0" class="size-30 outline-1 active:rotate-45 origin-bottom">origin-bottom</div>
        </div>
        <div class="flex p-10 justify-between w-[800px]">
            <div tabindex="0" class="size-30 outline-1 active:rotate-45 origin-top-left">origin-top-left</div>
            <div tabindex="0" class="size-30 outline-1 active:rotate-45 origin-top-right">origin-top-right</div>
            <div tabindex="0" class="size-30 outline-1 active:rotate-45 origin-bottom-left">origin-bottom-left</div>
            <div tabindex="0" class="size-30 outline-1 active:rotate-45 origin-bottom-right">origin-bottom-right</div>
        </div>
        <div class="flex p-10 justify-between w-[800px]">
            <div tabindex="0" class="size-30 outline-1 active:rotate-45 origin-center">origin-center</div>
            <div tabindex="0" class="size-30 outline-1 active:rotate-45 origin-[80%_80%]">origin-[80%_80%]</div>
            <div tabindex="0" class="size-30 outline-1 active:rotate-45 origin-[-50%_-50%]">origin-[-50%_-50%]</div>
            <div tabindex="0" class="size-30 outline-1 active:rotate-45 origin-[200px_200px]">origin-[200px_200px]</div>
        </div>

    </div>

origin.gif

缩放

scale系列类用来对元素进行缩放,语法与rotate基本类似,不同点在于:

  1. 在旋转中,负值意味着“逆时针”,而在缩放中,负值意味着“缩小到0之后接着负向缩放” -- 其实就是小孔镜像:上下颠倒左右也颠倒。
  2. 在旋转中,数值意味着角度值,在缩放中,数值意味着百分比

缩放也可以只缩放X方向,或Y方向,或者X/Y方向选取不同的缩放尺度,这时就可以使用scale-x-*scale-y-*来分别指定两个方向上的缩放尺度了

画框框不太直观,这里直接用下面这张140*140的小图片做例子:

sekiro.jpg

    <div class="flex flex-col">
        <div class="flex p-10 justify-between w-[800px]">
            <img src="./sekiro.jpg" class="scale-50" onclick="AlertScale('scale-50')"/>
            <img src="./sekiro.jpg" class="scale-y-50" onclick="AlertScale('scale-y-50')"/>
            <img src="./sekiro.jpg" class="-scale-y-50" onclick="AlertScale('-scale-y-50')"/>
        </div>
        <div class="flex p-10 justify-between w-[800px]">
            <img src="./sekiro.jpg" class="scale-x-50" onclick="AlertScale('scale-x-50')"/>
            <img src="./sekiro.jpg" class="scale-100" onclick="AlertScale('scale-100')"/>
            <img src="./sekiro.jpg" class="scale-x-150" onclick="AlertScale('scale-x-150')"/>
        </div>
        <div class="flex p-10 justify-between w-[800px]">
            <img src="./sekiro.jpg" class="-scale-x-50" onclick="AlertScale('-scale-x-50')"/>
            <img src="./sekiro.jpg" class="scale-y-150" onclick="AlertScale('scale-y-150')"/>
            <img src="./sekiro.jpg" class="scale-150" onclick="AlertScale('scale-150')"/>
        </div>
    </div>
    <script>
        function AlertScale(str) {
            alert(str);
        }
    </script>

scale_1.gif

中心点对缩放的影响

虽然尺寸上来讲,缩放与中心点是没什么关系的:无论是横竖哪个方向上的抻长或者压缩,都只是尺度的变化。

但考虑到缩放后元素摆放的位置的话,就必须考虑中心点了。即origin系列类对缩放也是有影响的。

浏览器在决定缩放后的图片应该如何摆放时,可以简单的理解为以下三步:

  1. 先按origin样式,找到原图片指定的origin
  2. 进行变换,变换后的图片也存在一个与原始图片origin点对应的“变换后的origin点”
  3. 通过水平平移,使上两步得到的中心点重合起来

看下面这个例子可能更直观一点:

    <div class="flex flex-col">
        <div class="flex p-10 justify-between w-[800px]">
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="scale-50 active:origin-top-left"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="scale-50 active:origin-top"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="scale-50 active:origin-top-right"/>
            </div>
        </div>
        <div class="flex p-10 justify-between w-[800px]">
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="-scale-50 active:origin-top-left"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="-scale-50 active:origin-top"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="-scale-50 active:origin-top-right"/>
            </div>
        </div>
        <div class="flex p-10 justify-between w-[800px]">
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="scale-120 active:origin-top-left"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="scale-120 active:origin-top"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="scale-120 active:origin-top-right"/>
            </div>
        </div>
    </div>

这个例子通过在<img>外部包裹一个<div>的方式,让<div>记住缩放前图片的原始位置,并用红色的框线将原位置标出来。这样我们在缩放时就很容易观察中心点的逻辑:

scale_2.gif

检验你是否理解了origin的运行逻辑,就看你能不能解释得清下面这个例子:

    <div class="flex flex-col">
        <div class="flex p-10 justify-between w-[800px]">
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="scale-50 active:origin-[180px_180px]"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="scale-x-50 active:origin-[180px_180px]"/>
            </div>
        </div>
    </div>

scale_3.gif

倾斜

通过skew系列类名可以使元素倾斜,语法和缩放scale基本一致,但倾斜中的数值代表的是角度值。倾斜也分X方向与Y方面,即水平方向的倾斜与垂直方向的倾斜。

  • 水平方向的倾斜,即skew-x-*,是让元素左右摇摆:倾斜值为正,则向左摆,值为负,则向右摆
  • 倾斜后元素的位置也与中心点有关,浏览器的处理逻辑与缩放一致

直接看例子感受一下即可:

    <span id="style_indicator">Current Style: </span>
    <div class="flex flex-col">
        <div class="flex p-10 justify-between w-[800px]">
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:skew-x-15" onmousedown="UpdateStyleIndicator('skew-x-15')"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:skew-x-15 origin-top-left" onmousedown="UpdateStyleIndicator('skew-x-15 origin-top-left')"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:-skew-x-15 origin-[-100px_-100px]" onmousedown="UpdateStyleIndicator('-skew-x-15 origin-[-100px_-100px]')"/>
            </div>
        </div>
        <div class="flex p-10 justify-between w-[800px]">
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:skew-y-15" onmousedown="UpdateStyleIndicator('skew-y-15')"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:skew-y-15 origin-top-left" onmousedown="UpdateStyleIndicator('skew-y-15 origin-top-left')"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:-skew-y-15 origin-[-100px_-100px]" onmousedown="UpdateStyleIndicator('-skew-y-15 origin-[-100px_-100px]')"/>
            </div>
        </div>
        <div class="flex p-10 justify-between w-[800px]">
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:skew-15" onmousedown="UpdateStyleIndicator('skew-15')"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:skew-15 origin-top-left" onmousedown="UpdateStyleIndicator('skew-15 origin-top-left')"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:-skew-15 origin-[-100px_-100px]" onmousedown="UpdateStyleIndicator('-skew-15 origin-[-100px_-100px]')"/>
            </div>
        </div>
    </div>
    <script>
        function UpdateStyleIndicator(style) {
            const styleIndicator = document.getElementById('style_indicator');
            styleIndicator.innerText = `Current Style: ${style}`;
        }
    </script>

skew.gif

平移

通过translate系列类对元素进行平移,语法与之前都类似,也是分X轴方向与Y轴两个方向,默认是两个方向都挪。数值的意思是基本尺寸单位,即我们之前文章中讲过的,默认1代表0.25rem,即4像素。

与旋转、缩放、倾斜不同,平移还支持分数作为数值,直接看例子你就明白了:

    <span id="style_indicator">Current Style: </span>
    <div class="flex flex-col">
        <div class="flex p-10 justify-between w-[800px]">
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:translate-5" onmousedown="UpdateStyleIndicator('translate-5 (向右向下均平移5*0.25rem == 20像素)')"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:-translate-5" onmousedown="UpdateStyleIndicator('-translate-5 (向左向上均平移5*0.25rem == 20像素)')"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:-translate-1/3" onmousedown="UpdateStyleIndicator('-translate-1/3 (向左向上均平移1/3)')"/>
            </div>
        </div>
        <div class="flex p-10 justify-between w-[800px]">
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:translate-x-5" onmousedown="UpdateStyleIndicator('translate-x-5 (向右平移5*0.25rem == 20像素)')"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:-translate-x-5" onmousedown="UpdateStyleIndicator('-translate-x-5 (向左平移5*0.25rem == 20像素)')"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:-translate-x-[46px]" onmousedown="UpdateStyleIndicator('-translate-x-[46px] (向左平移46像素)')"/>
            </div>
        </div>
        <div class="flex p-10 justify-between w-[800px]">
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:translate-y-5" onmousedown="UpdateStyleIndicator('translate-y-5 (向下平移5*0.25rem == 20像素)')"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:-translate-y-5" onmousedown="UpdateStyleIndicator('-translate-y-5 (向上平移5*0.25rem == 20像素)')"/>
            </div>
            <div class="outline-3 outline-red-400">
                <img src="./sekiro.jpg" tabindex="0" class="active:-translate-y-full" onmousedown="UpdateStyleIndicator('-translate-y-full (向上平移100%)')"/>
            </div>
        </div>
    </div>
    <script>
        function UpdateStyleIndicator(style) {
            const styleIndicator = document.getElementById('style_indicator');
            styleIndicator.innerText = `Current Style: ${style}`;
        }
    </script>

translate.gif

这种写法是不支持的

缩放、倾斜和平移都支持在两个方向上进行变换,普通的写法就是分别把两个方向上的变换都写一遍,比如:

    <div class="translate-x-1/2 translate-y-[60px] ..."></div>

如果你把写origin-[33%_20%]的经验用在这里,写出如下的东西的话,就会发现,在变换类里,是不支持一次性把两个维度的值都写在中括号里的:

    <div class="translate-[1/2_60px] ..."></div>

最终总结

旋转rotate比较好理解,按常识理解就可以了。origin对旋转的影响也是非常直观的,不需要过多思考。

平移translate也比较好理解,按常识理解就可以了:你既可以把“平移”理解为与origin没关系,也可以理解为“平移”是在挪动origin点的位置 -- 顺便把元素的其它部分也挪了过去。

但缩放scale和倾斜skeworigin点之间的关系就不是那么直观了,这里再说一遍工作原理:

  1. origin点是锚点,无论是缩放还是倾斜本质上都是线性变换
  2. 浏览器会让变换前和变换后的锚点重合,从而决定变换后的位置