小工具知识网

当前位置: 首页 > 行业知识 > 【HTML】【一文全解Canvas】从初学到实战,彻底掌握前端绘图神器!

行业知识

【HTML】【一文全解Canvas】从初学到实战,彻底掌握前端绘图神器!

2026-11-01 03:36:14 来源:互联网转载

【HTML】Canvas 基本介绍与应用

    • 前言
    • 一、Canvas 概述
    • 二、基本用法
      • 常用的 Canvas API
        • 1. 获取上下文:使用 `getContext('2d')` 获取上下文对象,在该对象上进行图形绘制和变换。
        • 2. 绘制形状:
        • 3. 图片操作:
        • 4. 变换操作:
        • 5. 渐变和样式:
        • 三、Canvas 绘制图形
          • 1、绘制矩形
            • a. fillRect()
            • b. strokeRect()
            • c. clearRect()
            • 2、绘制圆形
              • a. 绘制实心圆形
              • b. 绘制空心圆形
              • 3、绘制路径
                • a. 画笑脸
                • b. 绘制两个三角形,一个是填充的,一个是描边的
                • 四、Canvas 绘制文本
                  • 1、 fillText()
                  • 2、 strokeText()
                  • 五、Canvas 绘制图片
                    • 1、drawImage()
                    • 2、createPattern()
                    • 六、Canvas 动画效果
                      • 1、setInterval() 实现动画
                      • 2、requestAnimationFrame() 实现动画
                      • 七、Canvas 使用注意事项
                      • 八、实现鼠标点击画图--画笔(想画啥画啥)- 简单练习
                      • 九、利用Canvas绘制挂钟(原理-绘制弧形)- 简单练习
                        • 1、 还原坐标系
                        • 2、 绘制背景
                        • 3、 绘制时钟指针
                        • 4、循环绘制
                        • 5、最后的效果:(视频我就不展示了,下边有源码,你们可以自己试试)
                        • 6、总结
                        • 7、完整代码如下,其中注释中有更详细的解释:
                        • 十、实现倒计时效果
                          • 1、完整代码:
                          • 2、效果图
                          • 十一、实现粒子效果
                            • 1、完整代码
                            • 2、效果图
                            • 十二、实现图形的裁剪
                              • 1、完整代码
                              • 2、效果图
                              • 总结

                                前言

                                Canvas 是 Web 开发中一个重要的绘图工具,其最大的优势在于:支持动态绘制和动画效果,且对 CPU 资源使用较少,可以实现日常开发中的非常多的想象和需求。本文将会讲解 Canvas 的相关知识,包括它的基本概念、如何应用以及使用注意事项等。

                                一、Canvas 概述

                                • Canvas 是一种使用 JavaScript 在网页上绘制图像的 HTML 标签,它提供了一组 API,可以绘制 2D 和 3D 两种图形。
                                • Canvas 可以画出各种图形,包括点、线、圆、弧、多边形等,也可以显示文本、图片等。
                                • Canvas API 很强大且易于使用,可以绘制动画、图表、游戏和其他效果,这些效果可以拥有不同的交互和开发自定义的功能。

                                  二、基本用法

                                  在 HTML页面中引入Canvas标签:

                                   

                                  Canvas标签只有两个属性,即id和width/height,其中id用来标识该Canvas元素,而width和height则用来设置Canvas的宽度和高度,单位为像素。如果不设置宽高,则Canvas默认大小为300像素宽和150像素高。

                                  在JavaScript中获取Canvas对象后,就可以通过上下文(context)来绘制图形了:

                                  let canvas = document.getElementById('myCanvas');let ctx = canvas.getContext('2d');

                                  Canvas API 提供了丰富的绘图函数和属性,通过这些绘图函数,我们可以在Canvas上面绘制出我们想要的各种效果。

                                  常用的 Canvas API

                                  下面是一些常用的 Canvas API 的详细介绍:

                                  1. 获取上下文:使用 getContext('2d') 获取上下文对象,在该对象上进行图形绘制和变换。
                                  const canvas = document.getElementById('my-canvas');const ctx = canvas.getContext('2d');
                                  2. 绘制形状:
                                  // 矩形ctx.fillRect(x, y, width, height);               // 填充矩形ctx.strokeRect(x, y, width, height);             // 描边矩形// 圆形或弧形ctx.beginPath();                                // 开始一条路径描述ctx.arc(x, y, radius, startAngle, endAngle);     // 绘制弧形或圆形ctx.closePath();                                // 关闭路径ctx.fill();                                     // 填充当前绘图(路径区域)ctx.stroke();                                   // 描边当前绘图(路径)// 多边形ctx.beginPath();                                // 开始一条路径描述ctx.moveTo(x1, y1);                             // 将绘图位置移动到 x1, y1ctx.lineTo(x2, y2);                             // 连接到 x2, y2// ...                                          // 更多的点连接ctx.closePath();                                // 关闭路径ctx.fill();                                     // 填充当前绘图(路径区域)ctx.stroke();                                   // 描边当前绘图(路径)// 文本ctx.font = '16px serif';                        // 设置字体ctx.fillStyle = '#000';                         // 设置字体颜色ctx.fillText(text, x, y);                       // 填充文本ctx.strokeText(text, x, y, maxWidth);           // 描边文本
                                  3. 图片操作:
                                  const img = new Image();img.src = 'path/to/image';// 绘制图片ctx.drawImage(img, x, y, width, height);// 图片裁剪ctx.drawImage(img, sx, sy, swidth, sheight, x, y, width, height);
                                  4. 变换操作:
                                  // 平移ctx.translate(x, y);// 缩放ctx.scale(scaleX, scaleY);// 旋转ctx.rotate(angle);// 变换矩阵ctx.transform(a, b, c, d, e, f);// 重置变换矩阵ctx.resetTransform();
                                  5. 渐变和样式:
                                  // 线性渐变const gradient = ctx.createLinearGradient(x1, y1, x2, y2);gradient.addColorStop(offset, color);ctx.fillStyle = gradient;// 径向渐变const gradient = ctx.createRadialGradient(x1, y1, r1, x2, y2, r2);gradient.addColorStop(offset, color);ctx.fillStyle = gradient;// 渐变及样式应用操作ctx.fillStyle = color;                          // 填充颜色ctx.strokeStyle = color;                        // 描边颜色ctx.lineWidth = width;                          // 描边线条宽度ctx.lineJoin = type;                            // 指定线条链接方式ctx.lineCap = type;                             // 线条端点连接方式ctx.globalCompositeOperation = type;            // 指定元素重叠时的图形组合方式

                                  这些API可以让我们在canvas中绘制出各种形状,图像或网络向量图形,并配合上黑科技 WebGL(Web Graphics Library )等,可以提供如三维物体渲染等功能。

                                  三、Canvas 绘制图形

                                  Canvas 目前支持的图形主要包括:矩形、圆、弧、线段、文本和图片等,这里只简单介绍矩形和圆。

                                  1、绘制矩形

                                  Canvas 绘制矩形主要用到了 fillRect() 和 strokeRect() 方法。图形绘制原理如下图所示:

                                  a. fillRect()

                                  fillRect(x, y, width, height) 方法用于绘制填充矩形,其中 x 和 y 表示左上角坐标,width 和 height 分别表示矩形的宽和高。示例代码如下:

                                  ctx.fillRect(50, 50, 100, 100);
                                  b. strokeRect()

                                  strokeRect(x, y, width, height) 方法用于绘制描边矩形,可以为描边设置样式。示例代码如下:

                                  ctx.strokeStyle = '#FF0000';ctx.strokeRect(50, 50, 100, 100);
                                  c. clearRect()

                                  clearRect(x, y, width, height) 方法用于清除指定矩形区域,让清除部分完全透明。示例代码如下:

                                  ctx.fillRect(25, 25, 100, 100);ctx.clearRect(45, 45, 60, 60);ctx.strokeRect(50, 50, 50, 50);
                                  • fillRect()函数绘制了一个边长为 100px 的黑色正方形。
                                  • clearRect()函数从正方形的中心开始擦除了一个 60*60px 的正方形,
                                  • 接着strokeRect()在清除区域内生成一个 50*50 的正方形边框。

                                    2、绘制圆形

                                    Canvas 绘制圆形需要使用到 arc() 和 stroke() 方法。其中,arc() 方法需要设置圆心坐标、半径、起始弧度和终止弧度。起始弧度和终止弧度是以弧度为单位,通常指定为 0 和 2× π,起始弧度必须大于终止弧度,否则图形会出现问题。绘制圆形的原理如下图所示:

                                    a. 绘制实心圆形

                                    fill() 方法用于填充整个路径区域,示例代码如下:

                                    //绘制实心圆形ctx.beginPath();ctx.arc(100, 100, 50, 0, 2 * Math.PI);ctx.fillStyle = "red";ctx.fill();

                                    在这个例子中,arc() 方法用于画圆,分别传入圆心的 x 坐标、y 坐标、圆的半径、起始弧度和终止弧度(这里指绘制整个圆形的起始弧度和终止弧度),使用 fillStyle 设置填充颜色,最后使用 fill() 方法填充整个路径区域。

                                    b. 绘制空心圆形

                                    stroke() 方法用于描边路径,示例代码如下:

                                    ctx.beginPath();ctx.arc(100, 100, 50, 0, 2 * Math.PI);ctx.strokeStyle = "blue";ctx.lineWidth = 5;ctx.stroke();

                                    在这个例子中,arc() 方法和上面一样用于画圆,使用 strokeStyle 设置描边颜色,lineWidth 设置描边线条的宽度,最后使用 stroke() 方法描边路径。

                                    3、绘制路径

                                    图形的基本元素是路径。路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。一个路径,甚至一个子路径,都是闭合的。使用路径绘制图形需要一些额外的步骤:

                                    • 首先,你需要创建路径起始点。

                                      生成路径的第一步叫做 beginPath()。本质上,路径是由很多子路径构成,这些子路径都是在一个列表中,所有的子路径(线、弧形、等等)构成图形。而每次这个方法调用之后,列表清空重置,然后我们就可以重新绘制新的图形。

                                      备注: 当前路径为空,即调用 beginPath() 之后,或者 canvas 刚建的时候,第一条路径构造命令通常被视为是 moveTo(),无论实际上是什么。出于这个原因,你几乎总是要在设置路径之后专门指定你的起始位置。

                                      // 将笔触移动到指定的坐标 x 以及 y 上(这个函数实际上并不能画出任何东西)// 当 canvas 初始化或者`beginPath()`调用后,你通常会使用moveTo()函数设置起点。// 我们也能够使用`moveTo()`绘制一些不连续的路径。可以看一下下面的笑脸例子moveTo(x, y)
                                      • 然后你使用画图命令去画出路径。
                                        // 1、绘制一条从当前位置到指定 x 以及 y 位置的直线。lineTo(x, y)// 2、画一个以(x,y)为圆心的以 radius 为半径的圆弧(圆)//  x,y为绘制圆弧所在圆上的圆心坐标// `startAngle`以及`endAngle`参数用弧度定义了开始以及结束的弧度。这些都是以 x 轴为基准// 参数`anticlockwise`为一个布尔值。为 true 时,是逆时针方向,否则顺时针方向。arc(x, y, radius, startAngle, endAngle, anticlockwise)// 3、根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。arcTo(x1, y1, x2, y2, radius)

                                        注意:

                                        1、arc() 函数中表示角的单位是弧度,不是角度。角度与弧度的 js 表达式:弧度=(Math.PI/180)*角度。

                                        • 之后你把路径封闭。

                                          闭合路径 closePath(),不是必需的。这个方法会通过绘制一条从当前点到开始点的直线来闭合图形。如果图形是已经闭合了的,即当前点为开始点,该函数什么也不做。

                                          注意: 当你调用 fill() 函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用 closePath() 函数。

                                          但是调用 stroke() 时不会自动闭合。

                                          • 一旦路径生成,你就能通过描边或填充路径区域来渲染图形。
                                            a. 画笑脸
                                            function draw() {  var canvas = document.getElementById('canvas');  if (canvas.getContext){    var ctx = canvas.getContext('2d');    ctx.beginPath();    ctx.arc(75, 75, 50, 0, Math.PI * 2, true); // 绘制    ctx.moveTo(110, 75);    ctx.arc(75, 75, 35, 0, Math.PI, false);   // 口 (顺时针)    ctx.moveTo(65, 65);    ctx.arc(60, 65, 5, 0, Math.PI * 2, true);  // 左眼    ctx.moveTo(95, 65);    ctx.arc(90, 65, 5, 0, Math.PI * 2, true);  // 右眼    ctx.stroke();  }}
                                            b. 绘制两个三角形,一个是填充的,一个是描边的
                                            function draw() {  var canvas = document.getElementById('canvas');  if (canvas.getContext){  var ctx = canvas.getContext('2d');  // 填充三角形  ctx.beginPath();  ctx.moveTo(25, 25);  ctx.lineTo(105, 25);  ctx.lineTo(25, 105);  ctx.fill();  // 描边三角形  ctx.beginPath();  ctx.moveTo(125, 125);  ctx.lineTo(125, 45);  ctx.lineTo(45, 125);  ctx.closePath();  ctx.stroke();  }}

                                            四、Canvas 绘制文本

                                            Canvas 还支持在图形上绘制文本。下面介绍两个绘制文本的方法。

                                            1、 fillText()

                                            fillText(text, x, y, maxWidth) 方法用于以给定颜色填充文本,x 和 y 表示文本的起始坐标,maxWidth 表示文本允许的最大宽度,如果文本超出最大宽度,则文本会被自动缩放到适合宽度。示例代码如下:

                                            ctx.font = "30px Arial";ctx.fillStyle = "blue";ctx.fillText("Hello, World!", 50, 100);

                                            在这个例子中,使用 font 属性设置文本字体和大小,使用 fillStyle 设置填充颜色,使用 fillText() 方法绘制文本,传入要绘制的文本、文本起始坐标和最大宽度。

                                            2、 strokeText()

                                            strokeText(text, x, y, maxWidth) 方法用于以给定颜色描边文本,示例代码如下:

                                            ctx.font = "30px Arial";ctx.strokeStyle = "red";ctx.strokeText("Hello, World!", 50, 100);

                                            在这个例子中,同样使用 font 属性和 strokeStyle 属性设置文本风格和颜色,使用 strokeText() 方法描边文本。

                                            五、Canvas 绘制图片

                                            Canvas 还支持在图像上绘制图片。要在 Canvas 上绘制图片,需要先加载图片并将其存储到 Image 对象中。下面介绍两个绘制图片的方法。

                                            1、drawImage()

                                            drawImage(image, x, y) 方法可以在 Canvas 上绘制图片,需要传入 Image 对象、图片位置的 x 坐标和 y 坐标。示例代码如下:

                                            const img = new Image();img.src = "https://img.yzcdn.cn/vant/cat.jpeg";img.onload = function() {  ctx.drawImage(img, 50, 50);}

                                            在这个例子中,创建一个 Image 对象,设置图片的 src 属性,加载图片后将其绘制到 Canvas 上。绘制使用 drawImage() 方法,传入 Image 对象和图片起始坐标。

                                            2、createPattern()

                                            createPattern(image, type) 方法可以创建一个模式,可以用于填充或描边。需要传入 Image 对象和模式类型。示例代码如下:

                                            const img = new Image();img.src = "https://img.yzcdn.cn/vant/cat.jpeg";img.onload = function() {  const pattern = ctx.createPattern(img, "repeat");  ctx.fillStyle = pattern;  ctx.fillRect(0, 0, canvas.width, canvas.height);}

                                            在这个例子中,创建一个 Image 对象,加载图片后使用 createPattern() 方法创建一个重复填充模式,传入 Image 对象和模式类型。然后使用 fillStyle 属性设置填充颜色,使用 fillRect() 方法填充整个 Canvas 区域。

                                            六、Canvas 动画效果

                                            利用 Canvas 可以方便地实现各种动画效果。主要借助的是定时器的 setInterval() 和 setTimeout() 方法以及上文提到的 Canvas 常用方法。

                                            1、setInterval() 实现动画

                                            setInterval() 方法会定期调用函数,以达到动画效果。该方法接受两个参数:第一个参数是要调用的函数,第二个参数是调用函数之间的时间间隔(以毫秒为单位)。

                                            let x = 0;setInterval(() => {  ctx.clearRect(0, 0, canvas.width, canvas.height);  ctx.fillRect(x, 50, 50, 50);  x++;}, 10);

                                            效果是一个黑色方块在区域内从左到右移动,在这个例子中,使用 setInterval() 方法实现了一个简单的动画效果,每隔 10 毫秒就执行一次函数。函数中每次依次清除整个 Canvas 区域,然后在 Canvas 上绘制一个矩形,通过修改矩形的 x 坐标实现动画效果。

                                            2、requestAnimationFrame() 实现动画

                                            requestAnimationFrame() 方法也可以实现 Canvas 动画效果。与 setInterval() 相比,requestAnimationFrame() 的 优点 在于:它采用系统时间作为时间间隔,不会因为页面的隐藏/显示而导致误差,同时可以更好地与浏览器的渲染机制配合;而 setInterval() 每次执行的时间间隔是固定的,存在误差,对系统负荷也会产生一定的影响。

                                            let x = 0;function animate() {  ctx.clearRect(0, 0, canvas.width, canvas.height);  ctx.fillRect(x, 50, 50, 50);  x++;  requestAnimationFrame(animate);}animate();

                                            效果跟setInterval()一样,在这个例子中,使用 requestAnimationFrame() 方法,实现了一个矩形动画。在函数 animate() 中,每次依次清除整个 Canvas 区域,然后绘制一个矩形,通过修改矩形的 x 坐标实现动画效果。最后,在函数调用的最后,使用 requestAnimationFrame() 方法递归调用 animate() 函数,使得动画效果可以循环播放。

                                            七、Canvas 使用注意事项

                                            在使用 Canvas 前,需要注意以下几点:

                                            • Canvas 是一种自由绘制工具,可以通过 JavaScript 动态绘制各种图形,但也容易出现因为**代码逻辑错误**导致图形无法正常显示或报错等情况,需要小心谨慎使用。

                                            • 在绘制图像时,请注意 浏览器的性能和兼容性,对于一些需要动态效果的图形如动画,应该使用合适的方式来进行优化。同时,要考虑浏览器的兼容性,避免使用一些只支持较新版本浏览器的动画特效。

                                            • 在 Canvas 的使用过程中,需要特别注意 刷新频率,高刷新率可能会对电脑和移动设备的性能造成一定影响,同时也可能会影响使用体验。在绘制过程中借助 requestAnimationFrame() 方法可以实现更平滑的动画效果,避免不必要的耗能。

                                              八、实现鼠标点击画图–画笔(想画啥画啥)- 简单练习

                                                            Document                      // 调用函数绘制图形      const canvas = document.getElementById("canvas");      const ctx = canvas.getContext("2d");      ctx.lineWidth = 5;      ctx.strokeStyle = "red";      ctx.fillStyle = "blue";      // 设置开关      var flag = false;      // 鼠标按下绘图      canvas.onmousedown = (e) => {        flag = true;        ctx.beginPath();        ctx.moveTo(e.clientX, e.clientY);      };      // 鼠标抬起停止绘图      canvas.onmouseup = () => {        flag = false;      };      // 鼠标移入绘图      canvas.onmousemove = (e) => {        if (flag) {          ctx.lineTo(e.clientX, e.clientY);          ctx.stroke();        }      };      

                                              最终效果:

                                              九、利用Canvas绘制挂钟(原理-绘制弧形)- 简单练习

                                              利用 Canvas 绘制挂钟是一个常见的练习,也是为了演示 Canvas 的绘制弧形功能。下面我们来一步一步实现。

                                              首先,我们需要在 HTML 中添加一个 Canvas 标签,并设置好宽度和高度,如下所示:

                                               

                                              接下来,我们可以在 JavaScript 中获取该 Canvas 节点,获取它的上下文(context),并开始对 Canvas 进行绘制:

                                              const canvas = document.getElementById("clock");const ctx = canvas.getContext("2d");

                                              1、 还原坐标系

                                              Canvas 默认的 (0, 0) 坐标是位于左上角的。为了便于我们绘制,我们需要将坐标系移动到 Canvas 的中心点。

                                              const centerX = canvas.width / 2;const centerY = canvas.height / 2;// 将坐标系移动到中心点ctx.translate(centerX, centerY);

                                              2、 绘制背景

                                              接下来,我们需要绘制钟表的背景。在绘制背景之前,我们需要设置钟表的半径和文本的样式。文本的样式可以通过 font 属性进行设置,如下所示:

                                              const radius = canvas.width / 2 - 5; // 时钟的半径ctx.font = "bold 14px Arial"; // 设置文本样式ctx.textAlign = "center"; // 设置文本对齐方式ctx.textBaseline = "middle"; // 设置文本基线为中间

                                              接下来,我们可以在画布上用圆形绘制钟表的外框,并在轮廓上用数字表示每个小时的位置。这里我们可以使用 for 循环和一些数学计算来绘制每个刻度和数字。

                                              // 绘制钟表轮廓ctx.beginPath(); //开启路径ctx.arc(0, 0, radius, 0, 2 * Math.PI); // 绘制圆形ctx.stroke(); // 画线填充// 绘制钟表数字for (let i = 1; i    const angle = i * Math.PI / 6; // 分成12份    ctx.rotate(angle);   ctx.translate(0, -radius + 15); // 将原点向上平移   ctx.rotate(-angle);   ctx.fillText(i.toString(), 0, 0);   ctx.rotate(angle);   ctx.translate(0, radius - 15); // 将原点向下平移   ctx.rotate(-angle)}    const now = new Date();    const radius = canvas.width / 2 - 5;    ctx.clearRect(-centerX, -centerY, canvas.width, canvas.height); // 每次绘制前清空整个画布    ctx.beginPath();    ctx.arc(0, 0, radius, 0, 2 * Math.PI);    ctx.stroke();    // 绘制钟表数字    ctx.font = "bold 14px Arial";    ctx.textAlign = "center";    ctx.textBaseline = "middle";    for (let i = 1; i         const angle = i * Math.PI / 6;        ctx.rotate(angle);        ctx.translate(0, -radius + 15);        ctx.rotate(-angle);        ctx.fillText(i.toString(), 0, 0);        ctx.rotate(angle);        ctx.translate(0, radius - 15);        ctx.rotate(-angle)    }    // 绘制时针    const hour = now.getHours();    const minute = now.getMinutes();    const second = now.getSeconds();    const hourAngle = (hour % 12 + minute / 60 + second / 3600) * Math.PI / 6;    const hourLength = 0.6 * radius;    const hourX = Math.sin(hourAngle) * hourLength;    const hourY = -Math.cos(hourAngle) * hourLength;    ctx.beginPath();    ctx.moveTo(0, 0);    ctx.lineTo(hourX, hourY);    ctx.lineWidth = 4;    ctx.lineCap = "round";    ctx.stroke();// 绘制分针const minuteAngle = (minute + second / 60) * Math.PI / 30;const minuteLength = 0.8 * radius;const minuteX = Math.sin(minuteAngle) * minuteLength;const minuteY = -Math.cos(minuteAngle) * minuteLength;ctx.beginPath();ctx.moveTo(0, 0);ctx.lineTo(minuteX, minuteY);ctx.lineWidth = 2;ctx.stroke();// 绘制秒针const secondAngle = second * Math.PI / 30;const secondLength = 0.9 * radius;const secondX = Math.sin(secondAngle) * secondLength;const secondY = -Math.cos(secondAngle) * secondLength;ctx.beginPath();ctx.moveTo(0, 0);ctx.lineTo(secondX, secondY);ctx.lineWidth = 1;ctx.strokeStyle = "red"; // 设置颜色ctx.stroke();// 绘制中央圆点ctx.beginPath();ctx.arc(0, 0, 5, 0, 2 * Math.PI);ctx.fillStyle = "#333"; // 设置颜色ctx.fill();// 循环绘制setTimeout(drawClock, 1000);}drawClock();            background-color: #F5F5F5;        }        canvas {            border: 1px solid #444;        }                const now = new Date();            const radius = canvas.width / 2 - 5;            ctx.clearRect(-centerX, -centerY, canvas.width, canvas.height); // 每次绘制前清空整个画布            ctx.beginPath();            ctx.arc(0, 0, radius, 0, 2 * Math.PI);            ctx.stroke();            // 绘制钟表数字            ctx.font = "bold 14px Arial";            ctx.textAlign = "center";            ctx.textBaseline = "middle";            for (let i = 1; i                 const angle = i * Math.PI / 6;                ctx.rotate(angle);                ctx.translate(0, -radius + 15);                ctx.rotate(-angle);                ctx.fillText(i.toString(), 0, 0);                ctx.rotate(angle);                ctx.translate(0, radius - 15);                ctx.rotate(-angle)            }            // 绘制时针            const hour = now.getHours();            const minute = now.getMinutes();            const second = now.getSeconds();            const hourAngle = (hour % 12 + minute / 60 + second / 3600) * Math.PI / 6;            const hourLength = 0.6 * radius;            const hourX = Math.sin(hourAngle) * hourLength;            const hourY = -Math.cos(hourAngle) * hourLength;            ctx.beginPath();            ctx.moveTo(0, 0);            ctx.lineTo(hourX, hourY);            ctx.lineWidth = 4;            ctx.lineCap = "round";            ctx.stroke();            // 绘制分针            const minuteAngle = (minute + second / 60) * Math.PI / 30;            const minuteLength = 0.8 * radius;            const minuteX = Math.sin(minuteAngle) * minuteLength;            const minuteY = -Math.cos(minuteAngle) * minuteLength;            ctx.beginPath();            ctx.moveTo(0, 0);            ctx.lineTo(minuteX, minuteY);            ctx.lineWidth = 2;            ctx.stroke();            // 绘制秒针            const secondAngle = second * Math.PI / 30;            const secondLength = 0.9 * radius;            const secondX = Math.sin(secondAngle) * secondLength;            const secondY = -Math.cos(secondAngle) * secondLength;            ctx.beginPath();            ctx.moveTo(0, 0);            ctx.lineTo(secondX, secondY);            ctx.lineWidth = 1;            ctx.strokeStyle = "red"; // 设置颜色            ctx.stroke();            // 绘制中央圆点            ctx.beginPath();            ctx.arc(0, 0, 5, 0, 2 * Math.PI);            ctx.fillStyle = "#333"; // 设置颜色            ctx.fill();            // 循环绘制            setTimeout(drawClock, 1000);        }        drawClock();            background-color: #f5f5f5;      }      canvas {        border: 1px solid #444;      }            let nowTime = Date.parse(new Date());        if (nowTime           let leftTime = endTime - nowTime;          let day = Math.floor(leftTime / (1000 * 60 * 60 * 24));          let hour = Math.floor((leftTime / (1000 * 60 * 60)) % 24);          let minute = Math.floor((leftTime / (1000 * 60)) % 60);          let second = Math.floor((leftTime / 1000) % 60);          ctx.clearRect(0, 0, canvas.width, canvas.height);          ctx.font = "50px Arial";          ctx.fillStyle = "#000";          ctx.textAlign = "center";          ctx.textBaseline = "middle";          ctx.fillText(            day + "天" + hour + "小时" + minute + "分" + second + "秒",            canvas.width / 2,            canvas.height / 2          );        } else {          clearInterval(interval);        }      }, 1000);            background-color: #f5f5f5;      }      canvas {        border: 1px solid #444;      }            constructor(x, y, radius, color, velocity) {          this.x = x;          this.y = y;          this.radius = radius;          this.color = color;          this.velocity = {            x: (Math.random() - 0.5) * velocity,            y: (Math.random() - 0.5) * velocity,          };          this.opacity = 0;        }        draw() {          ctx.beginPath();          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);          ctx.fillStyle = `rgba(${this.color.r}, ${this.color.g}, ${this.color.b}, ${this.opacity})`;          ctx.fill();        }        update() {          this.x += this.velocity.x;          this.y += this.velocity.y;          if (this.opacity 

上一篇:怎么把服务器备份到本地?

下一篇:解决网络延迟的实用方法大揭秘!