前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CSS3 transform 和 canvas 背后不为人知的秘密

CSS3 transform 和 canvas 背后不为人知的秘密

作者头像
羽月
发布2022-10-08 13:54:35
8990
发布2022-10-08 13:54:35
举报
文章被收录于专栏:羽月技术羽月技术羽月技术

身为一个合格切图仔,CSS3 的 transfrom 那是熟的不能再熟,什么平移、缩放、旋转更是信手捏来,完全没有难度。不过碰到 transform: matrix() 这个样式,立刻脑袋一片空白,这个 matrix() CSS 函数接收高达 6 个参数!完全不知道它是用来干啥的,去看官方文档也完全没说,一条下面这个简单的样式。

transform: matrix(1.2, 0.2, -1, 0.9, 0, 20);

却可以实现下面这个效果。

看上面代码 6 个参数是传满了,但是每个参数表示什么含义?完全一脸懵逼。

这篇文章将详细讲解 transfrom 2D 变换背后的原理,相信看完这篇文章,你可以从脑袋一片空白进来到头皮发麻的出去。

起步

在详细聊到 matrix() 之前,首先来看看那些简单的平移、缩放、旋转变换是如何实现的,这里不使用现成的方法,完全自己来实现!

我们知道一个图形是由一个个点组成的,例如矩形是由 4 个顶点组成,三角形是由 3 个顶点组成,就连圆形也是由一个个点组成,只是点的数量非常多。那么我们对这些图形做 2D 变换其实就是对组成相应图形的点做变换。

为了简单,本篇使用一个 100x100 正方形做变换演示,以下代码作为变换的模板,这个代码非常简单,就是把变换过后的点全部画到画布上。

const canvas = document.createElement('canvas')
canvas.width = canvas.height = 300;
document.body.appendChild(canvas)
const ctx = canvas.getContext('2d')

const transform = `{下面小节的变换函数}`

const shape = [[0,0],[0,100],[100,100],[100,0]] // 正方形的 4 个顶点

ctx.beginPath()
shape.forEach(p => ctx.lineTo(...transform(p))) 
// "transform" 函数为下方小节指定的变换函数
// 下面的 "transform" 函数返回新的坐标位置
ctx.closePath()
ctx.fillStyle='rgba(0,255,255,1)'
ctx.fill()

上面代码中的 transform 是一个函数,它完全由我们自己来实现。shape 是我们要去变换的图形,现在它是一个 4 个点组成的正方形,你完全可以将它替换成任意图形。

平移

首先第一个,从简单的平移变换开始。在 CSS3 我们可以用 transform: translateX(100px) 将图形平移 100 像素,在 canvas 中,会使用渲染 2d 上下文的 transform() 方法来实现平移变换。

对图形做平移变换,其实就是增加或减少相应图形 X 和 Y 坐标,如下所示。

function translate([x, y], dx = 0, dy = 0) {
  return [x + dx, y + dy]
}

缩放

第二是缩放操作,在 CSS3 我们会使用 transform: scale(1) 来做缩放操作,在 canvas 中是使用 scale() 方法来实现缩放操作。

对一个图形做缩放操作,只需要将它的坐标乘上一个缩放因子就行了,比如这个正方形缩放 0.5 倍,它的新坐标就为 [[0,0],[0,50],[50,50],[50,0]]

我们可以使用两个缩放因子来分别缩放 X 轴和 Y 轴。

function scale([x, y], xs = 1, ys = xs) {
  return [x * xs, y * ys]
}

另外还有一个镜像变换,其实就是将图形缩放 -1 倍。

错切

第三个是错切变换,错切变换可以用来倾斜物体,它会不均匀拉伸物体,物体错切后它的面积和体积不会发生变化。在 CSS3 中可以使用 transform: skew() 来实现错切变换,canvas 中好像没有错切变换的方法。

执行错切的一个思路是将一个坐标的倍数加到另一个坐标上,比如下面将 1 倍的 Y 坐标加到 X 坐标上。

function skew([x, y]) {
  return [x + 1 * y, y]
}

效果如下。

不过 CSS 中的 skew 函数支持设置倾斜角度,如下所示。

div {
  transform: skew(30deg);
}

我们可以稍微改造下,来支持度数参数。

function skew([x, y], sx = 0, sy = 0) {
  const rad = r => r * Math.PI / 180
  return [x + Math.tan(rad(sx)) * y, y + Math.tan(rad(sy)) * x]
}

我们首先把角度变为弧度,然后利用 tan 函数来计算具体倾斜量,因为 tan 等于对边比临边,再乘上临边就是需要的倾斜量了。

旋转

最后一个变换,我们来实现最难的旋转变换。在 CSS3 中可以使用 transfrom: rotate() 实现,在 canvas 中可以使用 rotate() 方法来实现。

在实现 rotate 函数之前,我们可以先看下下面这幅图。

function rotate([x, y], deg = 0) {
  const rad = deg * Math.PI / 180
  return [
     x * Math.cos(rad) - y * Math.sin(rad), 
     x * Math.sin(rad) + y * Math.cos(rad)
  ]
}

效果如下,其中有一部分是超出画布被截掉了。

矩阵

我们上面实现的这些变换,其实都可以使用一个矩阵来实现。在 CSS3 中就是使用 transform: matrix() 方法,canvas 中的 transform() 方法,它们的参数都是传入一个 3x3 的矩阵来实现变换。

这里我们使用 2x2 的矩阵来实现,例如下方将缩放变换变成矩阵形式。

\begin{aligned} scaledPosition&=\begin{bmatrix} sx & 0 \\ 0 & sy \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} \\ &=\begin{bmatrix} sx * x \\ sy * y \end{bmatrix} \end{aligned}

同样的错切可以用这个矩阵。

skewMatrix=\begin{bmatrix} 1 & tan(\theta) \\ tan(\theta) & 1 \end{bmatrix}

旋转可以用这个矩阵。

rotationMatrix=\begin{bmatrix} cos(\theta) & -sin(\theta) \\ sin(\theta) & cos(\theta) \end{bmatrix}

需要注意我们使用的是列矢量,如果使用的是横矢量(也就是矢量在矩阵左边),需要将旋转矩阵转置下。

\begin{bmatrix} x & y \end{bmatrix}\begin{bmatrix} cos(\theta) & sin(\theta) \\ -sin(\theta) & cos(\theta) \end{bmatrix}

但是对于最简单的平移变换,我们没有办法使用 2x2 矩阵来完成。要实现平移矩阵我们需要提升一个维度,也就是二维升到三维。二维坐标 [x,y] 升级为 [x,y,w]w 一般为 1,这也可以称为齐次坐标。这也我们就可以使用如下矩阵来实现平移变换了。

\begin{aligned} translated&=\begin{bmatrix} 1 & 0 & dx \\ 0 & 1 & dy \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \\ &=\begin{bmatrix} x + dx \\ y + dy \\ 1 \end{bmatrix} \end{aligned}

关于矩阵相关知识和一些操作技巧以后专门写文章讲解~

我们在 2x2 矩阵上加了一行和一列,在新增的一列中包含了平移的信息。如果我们对矢量进行平移变换。这也是为什么 CSS3 的 matrix() 和 canvas 的 transform() 方法参数为什么是 3x3 矩阵。

CSS3 的 matrix() 参数如上图所示,其中 txty 就是上面介绍的 X 和 Y 轴的位移量,abcd 就是上面计算出来的二维矩阵中的项目,套用上面二维矩阵中的值,就可以利用 matrix() 来实现旋转、缩放、错切等变换。

终于我们通过矩阵实现了所有的变换了。使用矩阵看起来没有上面那些方法直观,但是矩阵有很多优势,例如非常方便就可以是组合变换,逆变换等。

总结

这篇文章介绍了 CSS3 transform 和 canvas 变换方法背后的数学原理,完全自己实现 2D 变换,看到这里相信你应该大概差不多对 CSS3 的 transform 有更深的了解了吧。

参考资料

[1]https://juejin.cn/post/7112770927082864653: https://juejin.cn/post/7112770927082864653

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-10-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 羽月技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 起步
  • 平移
  • 缩放
  • 错切
  • 旋转
  • 矩阵
  • 总结
    • 参考资料
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档