由于 SVG 图像可以内联到 HTML 中,因此我们可以使用 JavaScript 来操作它们。这意味着我们可以用代码为图像的各个部分制作动画,使其具有交互性,或者翻转图像并从数据中生成图形。
在这个例子中,我们将创建一只手表。我们将使用 SVG 绘制手表,并使用 JavaScript 为指针制作动画。
本教程更高级一些,深入介绍一些不太明显的 SVG 属性,并重点介绍使用 JavaScript 的动画。如果您想更全面地了解 SVG,请查看我 之前的文章 ,其中我们介绍了 7 个简单 SVG 图像的代码。
您还可以 观看本文的视频, 其中的内容更加丰富。在视频中,我们还介绍了互动。
HTML 中的 SVG
在上一篇文章中,我们了解到 SVG 图像可以内联到 HTML 文档中。我们讨论了 SVG 标签本身,它定义了图像的大小以及图像元素的位置。
图像元素按其位置放置在图像中。 viewBox
定义了应如何解释这些位置。
该属性的前两个数字设置左上角的位置。它与后两个数字定义的尺寸一起构成一个坐标系。
在这个例子中,我们将坐标系居中。 0,0
坐标位于图像的中间。我们设置 viewBox
左上角为坐标 -100,-100
,宽度和高度均为 200 个单位。
和 width
定义的大小 height
与 定义的大小 viewBox
相同。这意味着图像中的一个单位将是浏览器中的一个像素。这并不总是正确的。如果两者不匹配,则图像会放大或缩小。
如何制作手表的分针和时针
现在我们已经打好了基础,让我们开始编写手表本身的代码。我们从分针和时针开始。
有很多种方法可以绘制这些小线。我们可以逐条绘制每条线,但最有效的方法可能是使用特殊的 dash 属性绘制一个圆圈。
标签 circle
具有一个中心位置、一个尺寸半径、一个填充和边框颜色以及一个边框宽度。
SVG 元素通常具有与带有 CSS 的 HTML 元素类似的样式选项。但这些选项具有不同的属性名称。您可以将属性视为 fill
CSS background-color
中的属性。并且 stroke
和 stroke-width
属性也类似于 border-color
和 border-width
属性。只需记住它们并不完全相同。
我们还将使用该 fill
属性来设置文本颜色,并且我们将使用该 stroke
属性来设置线条的颜色。
现在我们如何将连续的圆圈变成分钟标记?您可能熟悉 border-style
CSS 中的属性。大多数情况下,您会使用实线边框,但也可以采用点线或虚线边框。这些边框样式并不常见,因为您在 CSS 中没有那么多选项来微调它们。
在 SVG 中,我们有类似的可能性,但自定义选项更多。我们可以使用 stroke-dasharray
、 stroke-dashoffset
和 pathLength
属性。
让我们举几个例子。在第一个例子中,我们将单个数字设置为 stroke-dasharray
。这将导致虚线边框,其中线段和间隙的长度相同。
这个属性虽然是一个数组,但是如果我们设置两个数字,那么第一个数字就是线段的长度,第二个数字就是间隙的长度。你甚至可以设置两个以上的数字,那么线段的长度和间隙的长度将始终取下一个数字。直到数组用完,然后从头开始。
我们将设置两个数字。一个表示分钟标记的长度,一个表示它们之间的间隙。这两个数字的总和应该正好是圆上一分钟的长度。我们知道一小时是 60 分钟。所以我们可以计算圆的周长,然后将其除以 60 得到一分钟的长度。
但还有更好的方法。我们可以用另一种方法,而不是计算圆的周长。我们可以设置属性 pathLength
。
此属性有点棘手。它不会调整圆的大小,但会影响 dasharray 属性的解释方式。虚线将按圆的周长由以下公式定义的方式绘制: pathLength
.
因此,我们将设置 pathLength
为 60
,代表 60 分钟。现在线和间隙段的总和必须为 1。 0.2
在此示例中, 0.8
和
现在我们几乎完成了,但仍然缺少一个小部分。虚线从错误的位置开始。为了解决这个问题,我们必须使用属性将其移动线段长度的一半 stroke-dashoffset
。
冲刺偏移属性可能有点违反直觉,因为此处的正值会使冲刺向后移动。您也可以将其设置为正数以使其向前移动。
用同样的方式,我们可以设置小时标记。我们添加一个具有几乎相同属性的新圆形标签。唯一不同的是颜色,并且我们在虚线数组中有更长的间隙。
. . .
. . .
这里需要注意的是,SVG 中的分层很重要。文档中稍后添加的标签将位于先前标签之上。如果我们以相反的顺序添加这两个圆圈,那么分钟将完全覆盖小时标记。
由于 SVG 现在存在于 HTML 中,我们可以从 CSS 中移出其中一些属性。但我们不能移动所有属性。定义样式的属性和定义元素形状的属性之间存在差异。
例如,半径定义了圆的形状,因此它必须与 SVG 代码保持一致。另一方面,我们可以移动填充和描边属性。
. . .
. . .
.hour_marker {
fill: transparent;
stroke: #f0f0c9;
stroke-width: 7;
stroke-dasharray: 0.2, 4.8;
stroke-dashoffset: 0.1;
}
.minute_marker {
fill: transparent;
stroke: #0f0e0e;
stroke-width: 7;
stroke-dasharray: 0.2, 0.8;
stroke-dashoffset: 0.1;
}
2. 如何画表针
让我们添加显示时间的指针。首先,我们将其绘制为指向上方,然后使用 JavaScript 将其调整到位。
我们用 line
元素来画手。要定义线元素,我们必须设置起始和终止坐标,以及颜色 stroke
和 stroke-width
属性。
为了让事情变得更好一点,我们还可以添加属性 stroke-linecap
来使线头圆润。这些样式属性是我们用 CSS 添加的。
. . .
. . .
. . .
.hand {
stroke: #ffffff;
stroke-width: 2;
stroke-linecap: round;
}
.hand--thick {
stroke-width: 7;
}
.hand--second {
stroke: yellow;
}
如何让手表指针指向正确方向
现在我们如何将这些线放到合适的位置?如果我们为元素分配一个 ID,我们就可以通过 JavaScript 访问和操作它。
但是,我们应该为哪个元素分配 ID?我们有两个元素代表一只手。为了解决这个问题,我们可以将这两个线元素分组到一个组标签中。您可以将组标签视为 div
HTML 中的元素。
我们可以为该组分配一个 ID,然后我们就可以通过 JavaScript 将整个组旋转到合适的位置。
. . .
. . .
在 JavaScript 文件中,首先,我们通过 ID 获取指针元素。然后,我们创建一个 Date 对象并获取当前的小时、分钟和秒。最后,我们 transform
根据这些值设置元素的属性。
const hoursElement = document.getElementById("hour_hand");
const minutesElement = document.getElementById("minute_hand");
const secondsElement = document.getElementById("second_hand");
const date = new Date();
const hour = date.getHours();
const minute = date.getMinutes();
const second = date.getSeconds();
hoursElement.setAttribute("transform", `rotate(${(360 / 12) * hour})`);
minutesElement.setAttribute("transform", `rotate(${(360 / 60) * minute})`);
secondsElement.setAttribute("transform", `rotate(${(360 / 60) * second})`);
变换属性可以包括多种变换,如缩放、平移或倾斜。
我们正在设置 rotate
转换,这需要一个数字。这个数字是 0 到 360 度之间的旋转。对于时针,我们将 360 除以 12 以获得每小时需要的旋转量,并将其乘以当前小时数。这应该会将时针转向当前小时。
对于分针和秒针,我们做同样的事情,只是我们将 360 除以 60,因为 1 小时由 60 分钟组成,1 分钟是 60 秒。
幸运的是,默认情况下变换中心是原点,即 0,0
坐标。如果不是这种情况,我们可以设置另一个变换原点,但由于我们的 viewBox
设置,我们不需要这样做。
如何制作手表指针动画
现在,这应该已经显示了当前时间,但我们的图像是静态的。为了跟上时间,我们可以使用该 requestAnimationFrame
函数来移动指针。
const hoursElement = document.getElementById("hour_hand");
const minutesElement = document.getElementById("minute_hand");
const secondsElement = document.getElementById("second_hand");
function animate() {
const date = new Date();
const hour = date.getHours() % 12;
const minute = date.getMinutes();
const second = date.getSeconds();
hoursElement.setAttribute("transform", `rotate(${(360 / 12) * hour})`);
minutesElement.setAttribute("transform", `rotate(${(360 / 60) * minute})`);
secondsElement.setAttribute("transform", `rotate(${(360 / 60) * second})`);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
我们将旋转逻辑移到动画函数中,并使用 requestAnimationFrame 函数。
首先,我们通过在 animate 函数之外调用 requestAnimationFrame 来触发它。然后,为了继续动画,我们还在每个动画周期结束时请求另一帧。
如果想要更流畅的动画,可以优化定位。我们可以定义指针,让它们指向分秒、分和时,而不是让它们分散位置。
const hoursElement = document.getElementById("hour_hand");
const minutesElement = document.getElementById("minute_hand");
const secondsElement = document.getElementById("second_hand");
function animate() {
const date = new Date();
const hour = date.getHours() + date.getMinutes() / 60;
const minute = date.getMinutes() + date.getSeconds() / 60;
const second = date.getSeconds() + date.getMilliseconds() / 1000;
hoursElement.setAttribute("transform", `rotate(${(360 / 12) * hour})`);
minutesElement.setAttribute("transform", `rotate(${(360 / 60) * minute})`);
secondsElement.setAttribute("transform", `rotate(${(360 / 60) * second})`);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
时针不仅会根据小时来确定其位置,还会根据当前的分钟数稍微转动。
分针在旋转时会考虑当前秒数。秒针也会考虑毫秒数。这样我们的指针就会连续移动。它们不会一秒一秒地跳动,而是会动起来。
下一步——如何让手表具有交互性
现在,如果我们检查结果,我们应该有一个流畅的动画手表。
更进一步,您还可以使用元素添加显示当前日期的日历窗口 text
。为了将其提升到一个新的水平,您甚至可以为此元素添加事件处理程序,该处理程序可在当前日期和 AM/PM 指示器之间切换其内容。
如果您遇到困难,请观看下面的视频,我们也在其中介绍了这部分内容。
将 SVG 与 JavaScript 混合使用会带来很多很酷的选项。您可以制作动画、添加交互并生成图形。迫不及待地想看看您的想法。
发表评论 取消回复