由于 SVG 图像可以内联到 HTML 中,因此我们可以使用 JavaScript 来操作它们。这意味着我们可以用代码为图像的各个部分制作动画,使其具有交互性,或者翻转图像并从数据中生成图形。

在这个例子中,我们将创建一只手表。我们将使用 SVG 绘制手表,并使用 JavaScript 为指针制作动画。

本教程更高级一些,深入介绍一些不太明显的 SVG 属性,并重点介绍使用 JavaScript 的动画。如果您想更全面地了解 SVG,请查看我 之前的文章 ,其中我们介绍了 7 个简单 SVG 图像的代码。

您还可以 观看本文的视频, 其中的内容更加丰富。在视频中,我们还介绍了互动。

HTML 中的 SVG

在上一篇文章中,我们了解到 SVG 图像可以内联到 HTML 文档中。我们讨论了 SVG 标签本身,它定义了图像的大小以及图像元素的位置。

图像元素按其位置放置在图像中。 viewBox 定义了应如何解释这些位置。

该属性的前两个数字设置左上角的位置。它与后两个数字定义的尺寸一起构成一个坐标系。

SVG-Watch.001-1

  
    Watch
    
  

  
    
      
    

    
  
Our starting point

在这个例子中,我们将坐标系居中。 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-Watch.001-2
The border-style property in CSS for HTML elements

在 SVG 中,我们有类似的可能性,但自定义选项更多。我们可以使用 stroke-dasharray stroke-dashoffset pathLength 属性。

让我们举几个例子。在第一个例子中,我们将单个数字设置为 stroke-dasharray 。这将导致虚线边框,其中线段和间隙的长度相同。

SVG-Watch.002
The stroke-dasharray property for SVG

这个属性虽然是一个数组,但是如果我们设置两个数字,那么第一个数字就是线段的长度,第二个数字就是间隙的长度。你甚至可以设置两个以上的数字,那么线段的长度和间隙的长度将始终取下一个数字。直到数组用完,然后从头开始。

我们将设置两个数字。一个表示分钟标记的长度,一个表示它们之间的间隙。这两个数字的总和应该正好是圆上一分钟的长度。我们知道一小时是 60 分钟。所以我们可以计算圆的周长,然后将其除以 60 得到一分钟的长度。

但还有更好的方法。我们可以用另一种方法,而不是计算圆的周长。我们可以设置属性 pathLength

此属性有点棘手。它不会调整圆的大小,但会影响 dasharray 属性的解释方式。虚线将按圆的周长由以下公式定义的方式绘制: pathLength .

因此,我们将设置 pathLength 60 ,代表 60 分钟。现在线和间隙段的总和必须为 1。 0.2 在此示例中, 0.8

SVG-Watch.001-3
Using the pathLength property. Note that the sum of the two numbers at the stroke-dasharray property is one, matching the length of one minute.

现在我们几乎完成了,但仍然缺少一个小部分。虚线从错误的位置开始。为了解决这个问题,我们必须使用属性将其移动线段长度的一半 stroke-dashoffset

冲刺偏移属性可能有点违反直觉,因为此处的正值会使冲刺向后移动。您也可以将其设置为正数以使其向前移动。

SVG-Watch.002-1
Example with and without 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 将其调整到位。

SVG-Watch.001-4

我们用 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 混合使用会带来很多很酷的选项。您可以制作动画、添加交互并生成图形。迫不及待地想看看您的想法。

订阅有关 Web 开发的更多教程:

Hunor Márton Borbély Game development with JavaScript, creative coding tutorials, HTML canvas, SVG, Three.js, and some React and Vue https://twitter.com/HunorBorbelyhttps://codepen.io/HunorMarton… favicon_144x144 YouTube AKedOLTWcmGND4Uh63a9CGS2fHu_gSo41Kh7XscBMwO-2g=s900-c-k-c0x00ffffff-no-rj

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部