龙空技术网

[译] 使用 DOMMatrix 绘制一个星星

小弟调调 18

前言:

此刻各位老铁们对“css画五角星”大致比较关注,咱们都想要了解一些“css画五角星”的相关文章。那么小编同时在网络上搜集了一些有关“css画五角星””的相关知识,希望朋友们能喜欢,同学们快快来学习一下吧!

使用 DOMMatrix 绘制一个星星

Drawing a star with DOMMatrix

最近我录制了一集关于 DOMPointDOMMatrix 的 HTTP 203 视频。如果你更喜欢观看视频版本,可以点击这里[1],但是请回到这里获取一些关于我犯了一个愚蠢错误的额外细节,而我几乎没有被发现。

I recently recorded an episode of HTTP 203 on DOMPoint and DOMMatrix. If you'd rather watch the video version, here it is, but come back here for some bonus details on a silly mistake I made, which I almost got away with.

DOMMatrix 允许你对 DOMPoints 应用变换。我发现这些 API 在绘制形状和处理变换结果时非常有用,而不会在 DOM 中引起完整的布局。

DOMMatrix lets you apply transformations to DOMPoints. I find these APIs handy for drawing shapes, and working with the result of transforms without causing full layouts in the DOM.

DOMPoint

这是 DOMPoint

Here's DOMPoint:

const point = new DOMPoint(10, 15);console.log(point.x); // 10console.log(point.y); // 15

是的!很令人兴奋,对吧?好的,也许 DOMPoint 单独来看并不那么有趣,所以让我们介绍一下 DOMMatrix

Yeah! Exciting right? Ok, maybe DOMPoint isn't interesting on its own, so let's bring in DOMMatrix:

DOMMatrix

const matrix = new DOMMatrix('translate(10px, 15px)').scale(2);console.log(matrix.a); // 2;

DOMMatrix 允许你创建一个矩阵,可以选择从 CSS 变换中创建,并对其执行额外的变换操作。每个变换都会创建一个新的 DOMMatrix,但也有其他可以直接修改矩阵的方法[2],比如 scaleSelf()

DOMMatrix lets you create a matrix, optionally from a CSS transform, and perform additional transforms on it. Each transform creates a new DOMMatrix, but there are additional methods that mutate the matrix, such as scaleSelf().

当你结合使用 DOMMatrixDOMPoint 时,事情变得有趣起来:

Things start to get fun when you combine DOMMatrix and `DOMPoint:

const newPoint = matrix.transformPoint(point);

我曾经在 Squoosh[3] 上使用这个方法来绘制 blob 形状,最近我还用它来绘制一个星星。

I used this to draw the blobs on Squoosh, but even more recently I used it to draw a star.

Drawing a star 绘制一个星星

我需要一个中心点位于确切中心的星星。我可以下载一个星星图案并检查中心点,但为什么不自己绘制呢?星星只是一个有尖角的圆形,对吧?不幸的是,我不能记住这种类型的数学公式,但这并不重要,因为我可以让 DOMMatrix 来帮我完成。

I needed a star where the center was in the exact center. I could download one and check the center point, but why not draw it myself? A star's just a spiky circle right? Unfortunately I can't remember the maths for this type of thing, but that doesn't matter, because I can get DOMMatrix to do it for me.

const createStar = ({ points = 10, x = 0, y = 0, size = 1 }) =>  Array.from({ length: points }, (_, i) =>    new DOMMatrix()      .translate(x, y)      .scale(size)      .transformPoint({ x: 0, y: 0 }),  );

我正在使用 Array.from 创建和初始化一个数组。我希望有一种更友好的方式来做这个[4]。

I'm using Array.from to create and initialise an array. I wish there was a friendlier way to do this.

一个典型的星星有 10 个点 - 5 个外部点和 5 个内部点,但我觉得允许其他类型的星星也是不错的。

A typical star has 10 points – 5 outer points and 5 inner points, but I figured it'd be nice to allow other kinds of stars.

矩阵只有设置尺寸和位置的变换,所以它只会返回一堆以 x, y 为坐标的点。

The matrix only has transforms set to apply the size and position, so it's just going to return a bunch of points at x, y.

无论如何,这不会阻止我。我将在下面的 <svg> 元素中绘制它:

Anyway, I'm not going to let that stop me. I'm going to draw it in an <svg> element below:

const starPoints = createStar({ x: 50, y: 50, size: 23 });const starPath = document.querySelector('.star-path');starPath.setAttribute(  'd',  // SVG path syntax  `M ${starPoints.map((point) => `${point.x} ${point.y}`).join(', ')} z`,);

这是最终的结果:

And here's the result:

嗯,这是十个重叠在一起的点。并不完全是一个星星。好的,下一步:

So, err, that's 10 points on top of each other. Not exactly a star. Ok, next step:

const createStar = ({ points = 10, x = 0, y = 0, size = 1 }) =>  Array.from({ length: points }, (_, i) =>    new DOMMatrix()      .translate(x, y)      .scale(size)      // Here's the new bit!      // 这是新的部分!      .translate(0, -1)      .transformPoint({ x: 0, y: 0 }),  );

这是结果:

And the result:

使用滑块在前一个状态和新状态之间进行过渡。我相信你会同意这种互动是值得的。

Use the slider to transition between the previous and new state. I'm sure you'll agree it was worth making this interactive.

好的,现在所有的点仍然重叠在一起。让我们来修复一下:

Ok, so all the points are still on stop of each other. Let's fix that:

const createStar = ({ points = 10, x = 0, y = 0, size = 1 }) =>  Array.from({ length: points }, (_, i) =>    new DOMMatrix()      .translate(x, y)      .scale(size)      // Here's the new bit!      // 这是新的部分!      .rotate((i / points) * 360)      .translate(0, -1)      .transformPoint({ x: 0, y: 0 }),  );

我将每个点按照 360 度的一部分进行旋转。因此,第一个点旋转了 0/10 的 360 度,第二个点旋转了 1/10 的 360 度,然后是 2/10,以此类推。

I'm rotating each point by a fraction of 360 degrees. So the first point is rotated 0/10ths of 360 degrees, the second is rotated 1/10th, then 2/10ths and so on.

这是结果:

Here's the result:

现在我们有了一个形状!虽然不是一个星星,但我们正在取得进展。

Now we have a shape! It's not a star, but we're getting somewhere.

为了完成它,将一些点向外移动:

To finish it off, move some of the points outward:

const createStar = ({ points = 10, x = 0, y = 0, size = 1 }) =>  Array.from({ length: points }, (_, i) =>    new DOMMatrix()      .translate(x, y)      .scale(size)      .rotate((i / points) * 360)      // Here's the new bit!      // 这是新的部分!      .translate(0, i % 2 ? -1 : -2)      .transformPoint({ x: 0, y: 0 }),  );

这是结果:

Here's the result:

这就是一个星星!

And that's a star!

But then I messed it up 但后来我搞砸了

当我为 HTTP 203 节目准备幻灯片时,我意识到 points 参数不太对。它允许你像这样使用:

As I was getting the slides together for the HTTP 203 episode, I realised that the points argument wasn't quite right. It lets you do something like this:

const starPoints = createStar({ points: 9, x: 50, y: 50, size: 23 });

看起来像这样:

Which looks like this:

这...不是一个星星。要创建一个有效的星星,点的数量必须是偶数。此外,到目前为止,我们创建的十个点的形状通常称为“五角星”,所以我改变了 API 以符合这种风格:

Which… isn't a star. The number of points has to be even to create a valid star. Besides, the ten-pointed shape we've been creating so far is typically called a "five-pointed star", so I changed the API to work in that style:

const createStar = ({ points = 5, x = 0, y = 0, size = 1 }) =>  Array.from({ length: points * 2 }, (_, i) =>    new DOMMatrix()      .translate(x, y)      .scale(size)      .rotate((i / points * 2) * 360)  // 这里修正了错误      .translate(0, i % 2 ? -1 : -2)      .transformPoint({ x: 0, y: 0 }),  );

我快速测试了一下代码,看起来没问题。但是...它还不太对。你能发现我引入的 bug 吗?我直到 Dillon Pentz 在 Twitter 上指出才注意到[5]:

I quickly tested the code, and it looked fine. But… it's not quite right. Can you see the bug I've introduced? I didn't notice it until Dillon Pentz pointed it out on Twitter[5]:

等等...如果数组循环中的索引满足 0 <= i < points * 2,那么通过 points 进行除法如何得到正确的星星呢?这样不是会围绕圆圈旋转两次吗?

Wait… If the index in the array from loop is 0 <= i < points * 2, how does it produce the correct star when dividing by points? Doesn't that rotate around the circle twice?

他是对的!我在数组长度中正确地乘以了 points,但在 rotate 中忘记了这样做。我没有注意到它,因为对于奇数点的星星,它创建了一个几乎相同的形状。

And, he's right! I correctly multiply points for the array length, but I forgot to do it for the rotate. I didn't notice it, because for stars with odd-numbered points, it creates a shape that's almost identical.

上面的结果是我想要的。拖动滑块来查看代码实际上是如何工作的。

The above is the result I intended. Drag the slider to see what the code actually does.

这是一个正确的实现:

Here's a correct implementation:

const createStar = ({ points = 5, x = 0, y = 0, size = 1 }) => {  const length = points * 2;  return Array.from({ length }, (_, i) =>    new DOMMatrix()      .translate(x, y)      .scale(size)      .rotate((i / length) * 360)      .translate(0, i % 2 ? -1 : -2)      .transformPoint({ x: 0, y: 0 }),  );};

我想故事的寓意是:不要在最后一刻改变幻灯片。

I guess the moral of the story is: Don't change slides at the last minute.

参考

[^1]:

[^2]:

[^3]:

[^4]:

[^5]:

原文:

标签: #css画五角星