Canvas入门学习笔记系列之:应用样式和色彩

  • 原创
  • 作者:程序员三丰
  • 发布时间:2025-01-09 00:45
  • 浏览量:376
在上一篇绘制图形的文章中,只用到默认的线条和填充样式,而在本文将会介绍如何给canvas应用样式和色彩。

色彩

可以给canvas图形上色的两个重要属性:

  • fillStyle=color:设置图形的填充颜色。
  • strokeStyle=color:设置图形轮廓的颜色。

默认情况下,canvas 线条和填充都是黑色(色值:#000000)。

一旦设置了 strokeStyle 或者 fillStyle 的值,那么这个新值就会成为新绘制的图形的默认值。如果你要给每个图形上不同的颜色,你需要重新设置 fillStyle 或 strokeStyle 的值。

示例:

实现代码如下:

<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas设置颜色</title>
    <style>
      body {
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
      }
    </style>
    <script>
      function draw() {
        var canvas = document.getElementById('canvas');
        if (canvas.getContext) {
          const ctx = canvas.getContext('2d');

          ctx.fillStyle = 'rgb(200, 0, 0)';
          ctx.fillRect(10, 10, 55, 50);

          ctx.fillStyle = 'rgb(0, 0, 200)';
          ctx.fillRect(70, 70, 55, 50);

          ctx.strokeStyle = '#008800';
          ctx.strokeRect(130, 130, 55, 50);

          ctx.strokeStyle = 'rgb(0, 0, 200)';
          ctx.strokeRect(190, 190, 55, 50);

        } else {
          alert('您的浏览器不支持Canvas!');
        }
      }
    </script>
  </head>

  <body onload="draw();">
    <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate;"></canvas>
  </body>

</html>

透明度

有两种方式可以设置canvas图形透明度:

globalAlpha 属性

该属性设置canvas里所有图形的透明度,有效值范围:0.0(完全透明)到 1.0(完全不透明),默认是1.0。
示例:

实现代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Canvas 设置透明度 globalAlpha</title>
    <style>
        body {
            height: 100vh;
            padding: 30px;
        }
    </style>
    <script>
        function draw() {
            var canvas = document.getElementById('canvas');
            if (canvas.getContext) {
                const ctx = canvas.getContext('2d');

                // 画背景
                ctx.fillStyle = '#FD0';
                ctx.fillRect(10, 10, 125, 125);
                ctx.fillStyle = '#6C0';
                ctx.fillRect(135, 10, 125, 125);
                ctx.fillStyle = '#09F';
                ctx.fillRect(10, 135, 125, 125);
                ctx.fillStyle = '#F30';
                ctx.fillRect(135, 135, 125, 125);

                ctx.fillStyle = '#FFF';

                // 设置透明度
                ctx.globalAlpha = 0.2;

                // 画半透明圆
                for (let i = 0; i < 7; i++) {
                    ctx.beginPath();
                    ctx.arc(135, 135, 15 + 15 * i, 0, Math.PI * 2, true);
                    ctx.fill();
                }

            } else {
                alert('您的浏览器不支持Canvas!');
            }
        }
    </script>
</head>

<body onload="draw();">
    <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate"></canvas>
</body>

</html>

rgba(推荐)

将 strokeStyle 和 fillStyle 的属性值设置为 rgba 格式的色值。这种方式优点是更灵活,可操作性强。
示例:

实现代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Canvas 设置透明度 rgba()</title>
    <style>
        body {
            height: 100vh;
            padding: 30px;
        }
    </style>
    <script>
        function draw() {
            var canvas = document.getElementById('canvas');
            if (canvas.getContext) {
                const ctx = canvas.getContext('2d');

                // 画背景
                ctx.fillStyle = 'rgb(255, 221, 0)';
                ctx.fillRect(10, 10, 280, 60);
                ctx.fillStyle = 'rgb(102, 204, 0)';
                ctx.fillRect(10, 71, 280, 60);
                ctx.fillStyle = 'rgb(0, 153, 255)';
                ctx.fillRect(10, 132, 280, 60);
                ctx.fillStyle = 'rgb(255, 51, 0)';
                ctx.fillRect(10, 193, 280, 60);

                // 画半透明矩形
                for (let i = 0; i < 19; i++) {
                    ctx.fillStyle = `rgba(255, 255, 255, ${(i + 1) / 19})`;
                    for (let j = 0; j < 4; j++) {
                        ctx.fillRect(15 + i * 14, 16 + j * 60, 14, 50);
                    }
                }

            } else {
                alert('您的浏览器不支持Canvas!');
            }
        }
    </script>
</head>

<body onload="draw();">
    <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate"></canvas>
</body>

</html>

线型

可以通过一系列属性来设置线的样式。

lineWidth 设置线条粗细

属性值必须为正数。默认值是 1.0。

注意:线宽是指给定路径的中心到两边的粗细。换句话说就是在路径的两边各绘制线宽的一半。(不理解,请参考下面的示例)

示例:

实现代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Canvas 线型 lineWidth</title>
    <style>
        body {
            height: 100vh;
            padding: 30px;
        }
    </style>
    <script>
        function draw() {
            var canvas = document.getElementById('canvas');
            if (canvas.getContext) {
                const ctx = canvas.getContext('2d');


                for (let i = 0; i < 10; i++) {
                    // 不同粗细的线条
                    ctx.lineWidth = 1 + i;
                    ctx.beginPath();
                    ctx.moveTo(5 + i * 14, 5);
                    ctx.lineTo(5 + i * 14, 140);
                    ctx.stroke();

                    // 路径中心
                    ctx.lineWidth = 1;
                    ctx.beginPath();
                    ctx.moveTo(5 + i * 14, 140);
                    ctx.lineTo(5 + i * 14, 180);
                    ctx.stroke();
                }

            } else {
                alert('您的浏览器不支持Canvas!');
            }
        }
    </script>
</head>

<body onload="draw();">
    <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate"></canvas>
</body>

</html>

lineCap 设置线条末端样式

可选值:butt、round、square。
示例:

实现代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Canvas 线型 lineCap</title>
    <style>
        body {
            height: 100vh;
            padding: 30px;
        }
    </style>
    <script>
        function draw() {
            var canvas = document.getElementById('canvas');
            if (canvas.getContext) {
                const ctx = canvas.getContext('2d');

                const lineCap = ['butt', 'round', 'square'];

                // 创建路径
                ctx.strokeStyle = '#09F';
                ctx.beginPath();
                ctx.moveTo(20, 20);
                ctx.lineTo(180, 20);
                ctx.moveTo(20, 140);
                ctx.lineTo(180, 140);
                ctx.stroke();

                // 画线条
                ctx.strokeStyle = '#000';
                for (let i = 0; i < lineCap.length; i++) {
                    ctx.lineWidth = 15;
                    ctx.lineCap = lineCap[i];
                    ctx.beginPath();
                    ctx.moveTo(45 + i * 50, 20);
                    ctx.lineTo(45 + i * 50, 140);
                    ctx.stroke();
                }

            } else {
                alert('您的浏览器不支持Canvas!');
            }
        }
    </script>
</head>

<body onload="draw();">
    <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate"></canvas>
</body>

</html>

lineJoin 设置线条连接处的样式

lineJoin 的属性值决定了图形中两线段连接处的样式。可选值:round、bevel 和 miter。默认是 miter。
示例:

实现代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Canvas 线型 lineJoin</title>
    <style>
        body {
            height: 100vh;
            padding: 30px;
        }
    </style>
    <script>
        function draw() {
            var canvas = document.getElementById('canvas');
            if (canvas.getContext) {
                const ctx = canvas.getContext('2d');

                const lineJoin = ['round', 'bevel', 'miter'];
                ctx.strokeStyle = '#000';
                ctx.lineWidth = 15;
                for (let i = 0; i < lineJoin.length; i++) {
                    ctx.lineJoin = lineJoin[i];
                    ctx.beginPath();
                    ctx.moveTo(25, 25 + i * 80);
                    ctx.lineTo(55, 65 + i * 80);
                    ctx.lineTo(85, 25 + i * 80);
                    ctx.lineTo(115, 65 + i * 80);
                    ctx.lineTo(145, 25 + i * 80);
                    ctx.stroke();
                }

            } else {
                alert('您的浏览器不支持Canvas!');
            }
        }
    </script>
</head>

<body onload="draw();">
    <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate"></canvas>
</body>

</html>

miterLimit 限制线条交界处最大长度

两条线段相交,线段的外侧边缘会被延伸交汇于一点上。线段之间夹角比较大时,交点不会太远,但随着夹角变小,交点距离会呈指数级增大。结合上面 lineJoin 的示例来看更容易理解。

miterLimit 属性就是用来设定外延焦点与连接点的最大距离(即:指线条交接处内角顶点到外角顶点的长度)。

示例:

实现代码如下:(可以更改miterLimit的值来观察不同的绘制结果)

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Canvas 线型 miterLimit</title>
        <style>
            body {
                height: 100vh;
                padding: 30px;
            }
        </style>
        <script>
            function draw() {
                var canvas = document.getElementById('canvas');
                if (canvas.getContext) {
                    const ctx = canvas.getContext('2d');

                    // 清空画布
                    ctx.clearRect(0, 0, 300, 300);

                    // 绘制参考线
                    ctx.strokeStyle = '#09f';
                    ctx.lineWidth = 2;
                    ctx.strokeRect(10, 50, 360, 50)

                    // 设置线条样式
                    ctx.strokeStyle = '#000';
                    ctx.lineWidth = 10;

                    // 设置miterLimit
                    ctx.miterLimit = 5;

                    // 绘制相交的线条
                    ctx.beginPath();
                    ctx.moveTo(0, 100);
                    for (let i = 0; i < 24; i++) {
                        let dy = i % 2 == 0 ? 25 : -25;
                        ctx.lineTo(Math.pow(i, 1.5) * 2, 75 + dy);
                    }
                    ctx.stroke();

                } else {
                    alert('您的浏览器不支持Canvas!');
                }
            }
        </script>
    </head>

    <body onload="draw();">
        <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate;"></canvas>
    </body>

</html>

绘制虚线

先了解几个绘制虚线的API:

  • getLineDash() 方法返回一个包含当前虚线样式,长度为非负偶数的数据。
  • setLineDash(segments) 方法设置当前虚线的样式。segments 参数是一个数组,包含一组描述交替绘制线段和间隙长度的数字:
    • 建议:数组长度为偶数,如:[5, 15],则表示虚线中实线线段长度为5,每个实现间隔是15。
    • 注意:数组长度为奇数,数组的元素会被复制和拼接,例如:[5, 15, 25] 会变成 [5, 15, 25, 5, 15, 25]。
    • 注意:如果数组为空,则虚线列表会被清空,线条描边就变成了实线。
  • lineDashOffset 属性虚线样式的起始偏移量。
    基础示例:

    实现代码如下:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas 绘制虚线</title>
    <style>
        body {
            height: 100vh;
            padding: 30px;
        }
    </style>
    <script>
        function draw() {
            var canvas = document.getElementById('canvas');
            if (canvas.getContext) {
                const ctx = canvas.getContext('2d');

                // 绘制一些常见的虚线样式
                let y = 15;
                function drawDashedLine(pattern) {
                    ctx.beginPath();
                    ctx.setLineDash(pattern);
                    ctx.moveTo(10, y);
                    ctx.lineTo(260, y);
                    ctx.stroke();
                    y += 20;
                }

                drawDashedLine([]);
                drawDashedLine([1, 1]);
                drawDashedLine([10, 10]);
                drawDashedLine([20, 5]);
                drawDashedLine([15, 3, 3, 3]);
                drawDashedLine([20, 3, 3, 3, 3, 3, 3, 3]);

                drawDashedLine([12, 3, 3]); // 等于 [12, 3, 3, 12, 3, 3],可以通过getLineDash获取虚线样式来验证
                console.log(ctx.getLineDash()) // 获取上面的虚线样式

                // 虚线偏移:无偏移的虚线
                ctx.setLineDash([8, 16]);
                ctx.beginPath();
                ctx.moveTo(10, 200);
                ctx.lineTo(200, 200);
                ctx.stroke();

                // 虚线偏移:有偏移的虚线
                ctx.beginPath();
                ctx.lineDashOffset = 8;
                ctx.moveTo(10, 210);
                ctx.lineTo(200, 210);
                ctx.stroke();

            } else {
                alert('您的浏览器不支持Canvas!');
            }
        }
    </script>
</head>

<body onload="draw();">
    <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate;"></canvas>
</body>

</html>

蚂蚁线示例:

实现代码如下:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Canvas 虚线 蚂蚁线 </title>
        <style>
            body {
                height: 100vh;
                padding: 30px;
            }
        </style>
        <script>
            function draw() {
                var canvas = document.getElementById('canvas');
                if (canvas.getContext) {
                    const ctx = canvas.getContext('2d');

                    let offset = 0;

                    function drawDashRect() {
                        ctx.clearRect(0, 0, 200, 200)
                        ctx.setLineDash([4, 2]);
                        ctx.lineDashOffset = -offset;
                        ctx.strokeRect(10, 10, 100, 100);
                    }

                    function march() {
                        offset++;
                        if (offset > 16) offset = 0;
                        drawDashRect();
                        setTimeout(march, 20);
                    }

                    march();

                } else {
                    alert('您的浏览器不支持Canvas!');
                }
            }
        </script>
    </head>

    <body onload="draw();">
        <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate"></canvas>
    </body>

</html>

渐变

首先新建一个 canvasGradient 对象,然后 addColorStop() 方法上色。最后就可以把渐变像颜色一样赋值给 fillStyle 或 strokeStyle 了。

  • 创建线性渐变的方法:

    createLinearGradient(x1, y1, x2, y2)
    

    createLinearGradient 方法接受 4 个参数,表示渐变的起点 (x1,y1) 与终点 (x2,y2)。

  • 创建径向渐变的方法:

    createRadialGradient(x1, y1, r1, x2, y2, r2)
    

    createRadialGradient 方法接受 6 个参数,前三个定义一个以 (x1,y1) 为原点,半径为 r1 的圆,后三个参数则定义另一个以 (x2,y2) 为原点,半径为 r2 的圆。

  • 给渐变上色

    addColorStop(position, color)
    

    addColorStop 方法接受 2 个参数,position 参数必须是一个 0.0 与 1.0 之间的数值,表示渐变中颜色所在的相对位置。例如,0.5 表示颜色会出现在正中间。color 参数必须是一个有效的 CSS 颜色值(如 #FFF,rgba(0,0,0,1),等等)。

createLinearGradient 示例:

实现代码如下:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Canvas 渐变示例 createLinearGradient </title>
        <style>
            body {
                height: 100vh;
                padding: 30px;
            }
        </style>
        <script>
            function draw() {
                var canvas = document.getElementById('canvas');
                if (canvas.getContext) {
                    const ctx = canvas.getContext('2d');

                    const lingrad = ctx.createLinearGradient(0, 0, 0, 300);
                    lingrad.addColorStop(0, '#00ABEB');
                    lingrad.addColorStop(0.5, '#FFF');
                    lingrad.addColorStop(0.5, '#26C000');
                    lingrad.addColorStop(1, '#FFF');

                    const lingrad2 = ctx.createLinearGradient(0, 100, 0, 195);
                    lingrad2.addColorStop(0.5, '#555');
                    lingrad2.addColorStop(1, 'rgba(0, 0, 0, 0)');

                    ctx.fillStyle = lingrad;
                    ctx.strokeStyle = lingrad2;

                    ctx.fillRect(10, 10, 280, 280);
                    ctx.lineWidth = 3;
                    ctx.strokeRect(50, 70, 200, 190);


                } else {
                    alert('您的浏览器不支持Canvas!');
                }
            }
        </script>
    </head>

    <body onload="draw();">
        <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate"></canvas>
    </body>

</html>

createRadialGradient 示例:

实现代码如下:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Canvas 渐变示例 createRadialGradient </title>
        <style>
            body {
                height: 100vh;
                padding: 30px;
            }
        </style>
        <script>
            function draw() {
                var canvas = document.getElementById('canvas');
                if (canvas.getContext) {
                    const ctx = canvas.getContext('2d');

                    const radgrad = ctx.createRadialGradient(65, 65, 10, 72, 70, 30);
                    radgrad.addColorStop(0, '#A7D30C');
                    radgrad.addColorStop(0.9, '#019F62');
                    radgrad.addColorStop(1, 'rgba(1, 159, 98, 0)');

                    const radgrad2 = ctx.createRadialGradient(175, 175, 20, 182, 180, 50);
                    radgrad2.addColorStop(0, "#FF5F98");
                    radgrad2.addColorStop(0.75, "#FF0188");
                    radgrad2.addColorStop(1, "rgba(255,1,136,0)");

                    const radgrad3 = ctx.createRadialGradient(145, 95, 15, 155, 90, 40);
                    radgrad3.addColorStop(0, "#00C9FF");
                    radgrad3.addColorStop(0.8, "#00B5E2");
                    radgrad3.addColorStop(1, "rgba(0,201,255,0)");

                    const radgrad4 = ctx.createRadialGradient(100, 150, 50, 100, 140, 90);
                    radgrad4.addColorStop(0, "#F4F201");
                    radgrad4.addColorStop(0.8, "#E4C700");
                    radgrad4.addColorStop(1, "rgba(228,199,0,0)");

                    ctx.fillStyle = radgrad4;
                    ctx.fillRect(0, 0, 300, 300);

                    ctx.fillStyle = radgrad3;
                    ctx.fillRect(0, 0, 300, 300);

                    ctx.fillStyle = radgrad2;
                    ctx.fillRect(0, 0, 300, 300);

                    ctx.fillStyle = radgrad;
                    ctx.fillRect(10, 10, 150, 150);


                } else {
                    alert('您的浏览器不支持Canvas!');
                }
            }
        </script>
    </head>

    <body onload="draw();">
        <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate"></canvas>
    </body>

</html>

图案样式 Patterns

上面使用渐变填充 canvas 图形,可以通过图案样式 Pattern 使用图片填充 canvas 图形。

createPattern(image, type)

该方法接受两个参数:

  • image 可以是一个 Image 对象的引用,或者另一个 canvas 对象。
  • type 必须是下面的字符串值之一:repeat,repeat-x,repeat-y 和 no-repeat。

图案的应用跟渐变很类似的,创建出一个 pattern 之后,赋给 fillStyle 或 strokeStyle 属性即可。

示例:

实现代码如下:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Canvas 图案 Pattern 填充示例 </title>
        <style>
            body {
                height: 100vh;
                padding: 30px;
            }
        </style>
        <script>
            function draw() {
                var canvas = document.getElementById('canvas');
                if (canvas.getContext) {
                    const ctx = canvas.getContext('2d');

                    const img = new Image();
                    img.src = './logo.png';
                    img.onload = function () {
                        const ptrn = ctx.createPattern(img, 'repeat');
                        ctx.fillStyle = ptrn;
                        ctx.fillRect(10, 10, 280, 280);
                    }


                } else {
                    alert('您的浏览器不支持Canvas!');
                }
            }
        </script>
    </head>

    <body onload="draw();">
        <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate"></canvas>
    </body>

</html>

阴影

相关属性如下:

  • shadowOffsetX = float:用来设定阴影在 X 轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往左延伸,正值则表示会往右延伸,它们默认都为 0。
  • shadowOffsetY = float:用来设定阴影在 Y 轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上延伸,正值则表示会往下延伸,它们默认都为 0。
  • shadowBlur = float:用于设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为 0。
  • shadowColor = color:用于设定阴影颜色效果,默认是全透明的黑色。

文字阴影示例:

实现代码如下:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Canvas 文字阴影 </title>
        <style>
            body {
                height: 100vh;
                padding: 30px;
            }
        </style>
        <script>
            function draw() {
                var canvas = document.getElementById('canvas');
                if (canvas.getContext) {
                    const ctx = canvas.getContext('2d');

                    ctx.font = '40px Times New Roman';
                    ctx.fillStyle = '#000';
                    ctx.fillText('Sample String', 25, 130);

                    ctx.shadowOffsetX = 2;
                    ctx.shadowOffsetY = 2;
                    ctx.shadowBlur = 2;
                    ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
                    ctx.fillText('Sample String', 25, 180);


                } else {
                    alert('您的浏览器不支持Canvas!');
                }
            }
        </script>
    </head>

    <body onload="draw();">
        <canvas id="canvas" width="300" height="300" style="background-color: #f3f4f8; color: chocolate"></canvas>
    </body>

</html>
声明:本文为原创文章,51blog.xyz和作者拥有版权,如需转载,请注明来源于51blog.xyz并保留原文链接:https://mp.51blog.xyz/article/81.html

文章归档

推荐文章

buildadmin logo
Thinkphp8 Vue3 Element PLus TypeScript Vite Pinia

🔥BuildAdmin是一个永久免费开源,无需授权即可商业使用,且使用了流行技术栈快速创建商业级后台管理系统。

热门标签

PHP ThinkPHP ThinkPHP5.1 Go Mysql Mysql5.7 Redis Linux CentOS7 Git HTML CSS CSS3 Javascript JQuery Vue LayUI VMware Uniapp 微信小程序 docker wiki Confluence7 学习笔记 uView ES6 Ant Design Pro of Vue React ThinkPHP6.0 chrome 扩展 翻译工具 Nuxt SSR 服务端渲染 scrollreveal.js ThinkPHP8.0 Mac webman 跨域CORS vscode GitHub ECharts Canvas