首页 > 其他分享 >可视化学习:CSS transform与仿射变换

可视化学习:CSS transform与仿射变换

时间:2023-12-21 13:55:40浏览次数:32  
标签:cos 矩阵 transform y0 x0 sin CSS 仿射变换

引言

在几年前,我就在一些博客中看到关于CSS中transform的分析,讲到它与线性代数中矩阵的关系,但当时由于使用transform比较少,再加上我毕竟是个数学学渣,对数学有点畏难心理,就有点看不下去,所以只是随便扫了两眼,就没有再继续了解了。现在在学习可视化,又遇到了这个点,又说到这是可视化的基础知识,既然这样,那看来还是逃不过去,那就再多了解一点吧。

transform的作用

使用过transform的前端小伙伴一定不陌生,通过对CSS中transform属性的设置,我们可以对DOM元素进行缩放、旋转、平移,以及扭曲,从而改变元素的位置、形状、大小和角度。

仿射变换

CSS中的transform对应到图形学中的概念就是仿射变换。

仿射变换简单来说就是“线性变换 + 平移”。

在CSS中对某个DOM元素应用仿射变换,可以简单理解成是把这个元素原本的整个坐标系进行了变换,并且这个坐标系的原点在最初始时位于DOM元素的中心,X轴朝右、Y轴朝上、Z轴朝外,也就是朝向屏幕。

所以就是说,对某个DOM元素进行仿射变换,就相当于对它所对应的几何图形的每个顶点向量进行仿射变换。

关于图形的仿射变换,有两个性质:

第一,仿射变换不改变直线段的形状,也就是说,应用仿射变换后,直线段依旧是直线段;

第二,应用相同的仿射变换后,两条直线段的长度比例保持不变。

平移

接下来我们先说平移,平移变换是最简单的仿射变换。

假设存在一个向量P(x0, y0),我们想要把它沿着另一个向量Q(x1, y1)的方向移动对应距离,那只要将两个向量相加,我们就可以得到这个新的向量它的坐标。

x = x0 + x1
y = y0 + y1

这就是平移变换的公式。

线性变换

根据公式可以看出,应用平移变换后,原始坐标系的原点会发生变化。

但是应用线性变换后,原点却并不会变化;下面来讲解两个常用的线性变换:旋转和缩放。

  • 旋转

    首先我们先来看旋转变换。

    rotate

    假设存在一个向量P(x0, y0),长度为r,与X轴夹角为θ,现在将它逆时针旋转α角,那么此时新的向量P'的坐标x和y分别是多少呢?

    首先我们根据圆的参数方程,可以得到如下公式:

    x0 = r * cosθ
    y0 = r * sinθ
    
    x = r * cos(α+θ)
    y = r * sin(α+θ)
    

    但这样并看不出新旧坐标之间的关联,所以需要进行推导。

    在上图中,我们假设旋转θ角后得到了一个新的坐标系(蓝色),此时我们可以求得向量P'在新坐标系的坐标,此时P'在新坐标系的坐标可以表示为:

    x' = r * cosα -> AF
    y' = r * sinα -> AI
    

    分别相当于是线段AF和AI的长度。

    此时依旧看不出新旧坐标之间的关联,我们还需要继续推导,求出向量P'在原坐标系的值,在上图中相当于我们要求出线段AJ和AK的长度。

    • 先来求AJ的长度

      首先我们从图中可以看出AJ = AG - JG, 并且 AG = AF * cosθ

      同时JG 和LF的长度相同,DF与AI的长度相同,且角FDJ的度数也是θ,所以可以得到JG = AI * sinθ

      最终我们可以得到如下公式:

      AJ = AF * cosθ - AI * sinθ 
         = r * cosα * cosθ - r * sinα * sinθ
      

      又因为:

      x0 = r * cosθ
      y0 = r * sinθ
      

      就可以得到AJ的长度,也就是新向量的x坐标

      x = x0 * cosα - y0 * sinα
      
    • 接着来求AK的长度

      从图中我们也可以看出AK = AM + MK,并且AM = AI * cosθ

      MK又可以分为MN和NK两段,相当于MK = AF * sinθ

      最终我们可以得到:

      AK = AI * cosθ + AF * sinθ
         = r * sinα * cosθ + r * cosα * sinθ
      

      再加上原坐标和角度及半径的关系,就可以得到AK的长度,也就是新向量的y坐标:

      y = x0 * sinα + y0 * cosα
      

    至此我们就得到了新坐标和原坐标以及旋转角度之间的关系,也就是旋转变换的公式:

    x = x0 * cosα - y0 * sinα
    y = x0 * sinα + y0 * cosα
    

    根据线性代数的知识,我们可以使用矩阵的形式来表示以上公式:

    [x]   [cosα  -sinα]   [x0]
    | | = |           | x |  |
    [y]   [sinα   cosα]   [y0]
    
  • 缩放

    接着我们继续看缩放变换。缩放变换相当于是让向量与标量相乘。

    比如我们使X轴缩放比例为sx,使Y轴缩放比例为sy,就可以得到新向量的坐标为:

    x = sx * x0
    y = sy * y0
    

    缩放比旋转简单一些,可以直接写出矩阵形式的公式:

    [x]   [sx  0]   [x0]
    | | = |     | x |  |
    [y]   [0  sy]   [y0]
    

至此,我们就基本了解了仿射变换的公式,并且可以看出线性变换的公式可以用矩阵相乘的形式进行表示。

除了不改变原点,线性变换还有另外一个性质,就是可以进行叠加;多个线性变换的叠加结果就是将线性变换的矩阵依次相乘,最后再与原始向量相乘。

根据以上内容,我们可以得到仿射变换的一般表达式:

P = M x P0 + P1

M为多个线性变换的叠加结果,也就是变换矩阵的相乘结果,P0为原始向量坐标,P1为平移。

公式优化

为了便于计算,我们还可以对以上的仿射变换表达式进行优化,通过增加维度来使用矩阵进行表示:

[P]   [M  P1]   [P0]
| | = |     | x |  |
[1]   [0   1]   [1 ]

这实际上就是给线性空间增加了一个维度,用高维度的线性变换表示了低维度的仿射变换。

这种n+1维坐标被称为齐次坐标,对应的矩阵被称为齐次矩阵

我们需要注意,由于平移变换会改变坐标原点,不同的变换顺序很可能会导致不同的变换结果,所以要注意矩阵相乘的顺序。

公式应用

接下来我们就来应用一下线性变换的公式。

假设现在在页面上有一个div。

<div class="block separate">我使用分开写</div>
.block {
  width: 100px;
  height: 100px;
  color: #fff;
  background: orange;

  &.separate {
    transform: rotate(30deg) translate(100px, 50px) scale(1.5);
  }
}

通过简单的旋转和平移,我们改变了元素的角度、位置和大小。

此时我们对于transform的变换是分开写的,但在CSS的transform中,可以使用一个matrix函数,让我们对这些变换进行合并编写。

首先我们引入一个ogl库,使用其中定义的矩阵类Mat3(也可以借助其他数学库,比如mathjs):

import { Mat3 } from 'ogl';

然后针对上面的3个变换,分别定义三个变换矩阵,分别是旋转矩阵、平移矩阵和缩放矩阵:

const rad = Math.PI / 6;

let a = new Mat3(
    // 旋转矩阵
    Math.cos(rad), -Math.sin(rad), 0,
    Math.sin(rad), Math.cos(rad), 0,
    0, 0, 1
);
let b = new Mat3(
    // 平移矩阵
    1, 0, 100,
    0, 1, 50,
    0, 0, 1
);
let c = new Mat3(
    // 缩放矩阵
    1.5, 0, 0,
    0, 1.5, 0,
    0, 0, 1
);

// -------------
// 使用math.js
const a = math.matrix(
  [
    [Math.cos(rad), -Math.sin(rad), 0], 
    [Math.sin(rad), Math.cos(rad), 0],
    [0, 0, 1]
  ]
);
const b = math.matrix(
  [
    [1, 0, 100], 
    [0, 1, 50],
    [0, 0, 1]
  ]
);
const c = math.matrix(
  [
    [1.5, 0, 0], 
    [0, 1.5, 0],
    [0, 0, 1]
  ]
);

接着对三个矩阵进行相乘,得到axbxc的结果:

const res = [a, b, c].reduce((prev, current) => {
  return current.multiply(prev); // prev x current 结果保存到current
});

// -------------
// 使用math.js
let res = math.multiply(a, b);
res = math.multiply(res, c);

最后我们利用CSS变量将JS的计算结果应用到样式上:

.block {
  // ...

  &.combine {
    --trans: none;
    transform: var(--trans);
  }
}

由于CSS的matrix是一个简写的齐次矩阵,它省略了三阶齐次矩阵第三行的0,0,1,所以只有6个值。

const combine = document.querySelector('.combine');
const s = res.slice(0, 6);

matrix貌似是列主序,所以在设置的时候,需要按如下顺序赋值:

const combine = document.querySelector('.combined');

combine.style.setProperty('--trans', `matrix(
${s[0]},${s[3]},
${s[1]},${s[4]},
${s[2]},${s[5]},
)`);


// -------------
// 使用math.js
const s = Array.from(res).map(item => item.value);
combine.style.setProperty('--trans', `matrix(
  ${s[0]},${s[3]},
  ${s[1]},${s[4]},
  ${s[2]},${s[5]}
)`);

可以明显看出,这样使用的效果,和rotate、translate和scale分开写的效果是一样的。

总结

利用仿射变换,我们可以快速绘制出形态、位置、大小各异的众多几何图形,比如实现粒子动画。

也许在普通的前端开发中,用不到太多,也并不太需要说去利用matrix去减少CSS的代码体积,但如果要去做可视化方面的开发,仿射变换还是可以多去了解一下。

标签:cos,矩阵,transform,y0,x0,sin,CSS,仿射变换
From: https://www.cnblogs.com/beckyyyy/p/17918820.html

相关文章

  • css垂直居中
    word-wrap:break-word;/*英文的时候需要加上这句,自动换行*//*自测了这句话没啥用*/writing-mode:vertical-rl;/*从左向右从右向左是writing-mode:vertical-rl;*/writing-mode:tb-rl;/*IE浏览器的从左向右从右向左是writing-mode:tb-rl;*/text-align:......
  • css文本换行设置
    文本换行设置word-wrapword-wrap:normal|break-word;normal:默认值,当一行剩余空间无法容纳下一个字(英文是指单词)时,会换行break-word:针对英文的文本,这个值可以让一个单词拆分开来换行。word-breakword-break:normal|break-all|keep-allnormal:不拆分单词......
  • transformer总体架构
    transformer总体架构目录transformer总体架构循环神经网络总体架构EncoderDecoder输入输出层模型输入位置编码模型输出自注意力机制关于QKV的理解Q,K,V及注意力计算多头注意力机制多头注意力机制作用FeedForward层参考资料论文地址:AttentionisAllYouNeedhttps://arxiv......
  • transformer补充细节
    transformer补充细节目录transformer补充细节注意力机制细节为什么对点积注意力进行缩放多头带来的好处数据流训练时数据流推理时数据流解码器中注意力的不同带掩码的注意力机制位置编码整型数值标记[0,1]范围标记位置二进制标记周期函数标识用sin和cos交替来表示位置训练测试细......
  • 22个实用的CSS技巧,让你的网站脱颖而出
    想要让你的网站在激烈的竞争中脱颖而出吗?使用CSS的强大功能可以帮助你实现这一目标。本文将分享22个实用的CSS技巧,帮助你提升网站的外观和用户体验。无论你是一个新手还是有经验的开发者,这些技巧都将为你的网站注入新鲜的设计元素和动感效果。自定义字体:通过使用@font-face规则,你可......
  • JS+CSS多行文本显示“更多”
    本代码展示最多显示3行,每行行高16px,3行总高48px,4行总高64px当文本行数大于3行,显示“更多”CSS:#CourseDesc{margin-top:5px;font-size:12px;position:relative;max-height:48px;line-height:16px;overflow:hidden;}#CourseDesc.temp{position:absolute;top......
  • 15.高阶定位-CSS 定位与原生定位
    目录原生定位cssselector定位原生定位官网地址元素属性定位组合定位#ID定位driver.find_element_by_android_uiautomator('\newUiSelector().resourceId("<element-ID>")')#组合定位driver.find_element_by_android_uiautomator('\newUiS......
  • 微信小程序使用iconfont通过transfonter转化成只有css样式的方式
    1.通过iconfont 网站选择对应图表加入购物车,然后添加到自己对应项目中,现在至本地。2.解压下载到本地的文件 3.进入 transfonter 后,点击Addfonts按钮,将上图中红色框中的文件上传上去,勾选启用 Base64encode ,点击Convert按钮,完成后点击下载Download 4.解压转......
  • Sw-YoloX An anchor-free detector based transformer for sea surface object detect
    Sw-YoloXAnanchor-freedetectorbasedtransformerforseasurfaceobjectdetection基于Transformer用于海上目标检测的无锚检测器:Sw-YoloX1)由于不同海洋状态下的活体和漂浮物体数据稀缺且昂贵,我们基于2022年1月至3月在中国厦门的实际海面测量,构建了XM-10000基准数据集。......
  • css3
    目录CSS介绍CSS语法规范css代码风格样式格式书写紧凑格式展开格式样式大小写小写格式(推荐)大写格式样式空格风格css选择器的作用css基础选择器选择器分类标签选择器作用优缺点类选择器语法规范多类名选择器多类名使用方式多类名开发中使用场景id选择器id选择器和类选择器的区别......