ZetCode

JavaScript Canvas LineJoin 教程

最后修改于 2025 年 4 月 3 日

在本文中,我们将探讨 JavaScript 中的 Canvas lineJoin 属性。此属性控制两条线在尖锐角度相交时角的渲染方式。掌握 lineJoin 对于创建精美的图形和可视化至关重要。

基本定义

lineJoin 属性指定两条线相交时创建的角的类型。它会影响具有尖锐角度或复杂路径的形状的外观。有三种可能的值:miter(斜接)、round(圆角)和 bevel(斜角)。

默认值为 miter,它创建尖角。Round 创建圆角,而 bevel 创建平角。该属性可用于路径和形状的描边操作。

基本的 LineJoin 用法

本示例在简单的角度路径上演示了三种 lineJoin 类型。

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Basic LineJoin</title>
</head>
<body>

<canvas id="myCanvas" width="400" height="200"></canvas>

<script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    
    // Miter join (default)
    ctx.beginPath();
    ctx.moveTo(50, 50);
    ctx.lineTo(100, 150);
    ctx.lineTo(150, 50);
    ctx.lineJoin = 'miter';
    ctx.lineWidth = 10;
    ctx.stroke();
    
    // Round join
    ctx.beginPath();
    ctx.moveTo(200, 50);
    ctx.lineTo(250, 150);
    ctx.lineTo(300, 50);
    ctx.lineJoin = 'round';
    ctx.lineWidth = 10;
    ctx.stroke();
    
    // Bevel join
    ctx.beginPath();
    ctx.moveTo(350, 50);
    ctx.lineTo(400, 150);
    ctx.lineTo(450, 50);
    ctx.lineJoin = 'bevel';
    ctx.lineWidth = 10;
    ctx.stroke();
</script>

</body>
</html>

此代码创建了三个具有不同 lineJoin 值的 V 形路径。每条路径都显示了根据 lineJoin 设置,角的渲染方式不同。

miter 连接创建尖锐的尖角,round 创建平滑的圆角,bevel 创建平角。所有路径均使用 10px 的线宽。

带斜接限制的 LineJoin

本示例展示了 miterLimit 如何影响尖锐角度的 miter 连接。

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Miter Limit</title>
</head>
<body>

<canvas id="myCanvas" width="400" height="300"></canvas>

<script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    
    // Default miter limit (10)
    ctx.beginPath();
    ctx.moveTo(50, 100);
    ctx.lineTo(100, 50);
    ctx.lineTo(150, 100);
    ctx.lineJoin = 'miter';
    ctx.lineWidth = 10;
    ctx.strokeStyle = 'blue';
    ctx.stroke();
    
    // Low miter limit (2)
    ctx.beginPath();
    ctx.moveTo(200, 100);
    ctx.lineTo(250, 50);
    ctx.lineTo(300, 100);
    ctx.lineJoin = 'miter';
    ctx.miterLimit = 2;
    ctx.lineWidth = 10;
    ctx.strokeStyle = 'red';
    ctx.stroke();
</script>

</body>
</html>

miterLimit 属性控制 miter 连接可以延伸的距离。在尖锐角度下,miter 连接可能会变得非常长。miterLimit 可防止这种情况发生。

第一条路径使用默认的 miterLimit (10),显示长而尖的角。第二条路径的 miterLimit 为 2,显示当超过限制时,连接如何自动转换为斜角。

复杂路径的 LineJoin

本示例演示了复杂路径的 lineJoin 行为。

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Complex Path LineJoin</title>
</head>
<body>

<canvas id="myCanvas" width="500" height="300"></canvas>

<script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    
    // Star shape with round joins
    ctx.beginPath();
    ctx.moveTo(100, 50);
    ctx.lineTo(120, 100);
    ctx.lineTo(170, 100);
    ctx.lineTo(130, 130);
    ctx.lineTo(150, 180);
    ctx.lineTo(100, 150);
    ctx.lineTo(50, 180);
    ctx.lineTo(70, 130);
    ctx.lineTo(30, 100);
    ctx.lineTo(80, 100);
    ctx.closePath();
    
    ctx.lineJoin = 'round';
    ctx.lineWidth = 8;
    ctx.strokeStyle = 'purple';
    ctx.stroke();
    
    // Zigzag path with bevel joins
    ctx.beginPath();
    ctx.moveTo(200, 50);
    for (let i = 0; i < 5; i++) {
        ctx.lineTo(250 + i * 50, i % 2 ? 200 : 50);
    }
    
    ctx.lineJoin = 'bevel';
    ctx.lineWidth = 8;
    ctx.strokeStyle = 'orange';
    ctx.stroke();
</script>

</body>
</html>

此示例创建了两个复杂的路径来演示 lineJoin 行为。第一个是带有圆角的星形,显示平滑的圆角。

第二个是带有斜角的锯齿形路径,在每次角度变化处显示平角。为了清晰可见,两条路径都使用 8px 的线宽。

不同线宽的 LineJoin

本示例显示了 lineJoin 在不同线宽下的外观。

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Line Width Comparison</title>
</head>
<body>

<canvas id="myCanvas" width="500" height="300"></canvas>

<script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    
    const lineWidths = [2, 5, 10, 15];
    const joinTypes = ['miter', 'round', 'bevel'];
    
    joinTypes.forEach((joinType, j) => {
        lineWidths.forEach((width, i) => {
            ctx.beginPath();
            ctx.moveTo(50 + j * 150, 50 + i * 60);
            ctx.lineTo(100 + j * 150, 100 + i * 60);
            ctx.lineTo(150 + j * 150, 50 + i * 60);
            
            ctx.lineJoin = joinType;
            ctx.lineWidth = width;
            ctx.strokeStyle = `hsl(${j * 120}, 70%, 50%)`;
            ctx.stroke();
            
            // Label
            ctx.fillStyle = 'black';
            ctx.font = '12px Arial';
            ctx.fillText(`${joinType} ${width}px`, 
                30 + j * 150, 45 + i * 60);
        });
    });
</script>

</body>
</html>

此代码创建了一个网格,比较了所有三种连接类型以及四种不同的线宽。每个单元格显示了连接类型在不同比例下的外观。

随着线条变粗,斜接连接会更加明显。圆角连接保持一致的曲率。斜角连接在较大的线宽下显示出更明显的平角。hsl 颜色函数会根据连接类型改变颜色。

实际应用:自定义形状

本示例演示了 lineJoin 在创建自定义形状中的实际应用。

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Custom Shapes with LineJoin</title>
</head>
<body>

<canvas id="myCanvas" width="500" height="300"></canvas>

<script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    
    // Gear with miter joins
    ctx.beginPath();
    drawGear(ctx, 100, 150, 50, 15, 8);
    ctx.lineJoin = 'miter';
    ctx.lineWidth = 4;
    ctx.strokeStyle = 'teal';
    ctx.stroke();
    
    // Gear with round joins
    ctx.beginPath();
    drawGear(ctx, 300, 150, 60, 12, 10);
    ctx.lineJoin = 'round';
    ctx.lineWidth = 6;
    ctx.strokeStyle = 'brown';
    ctx.stroke();
    
    function drawGear(ctx, x, y, radius, teeth, toothLength) {
        const angleStep = (Math.PI * 2) / teeth;
        
        for (let i = 0; i < teeth; i++) {
            const angle1 = i * angleStep;
            const angle2 = angle1 + angleStep * 0.3;
            const angle3 = angle1 + angleStep * 0.7;
            
            ctx.moveTo(
                x + Math.cos(angle1) * radius,
                y + Math.sin(angle1) * radius
            );
            
            ctx.lineTo(
                x + Math.cos(angle2) * (radius + toothLength),
                y + Math.sin(angle2) * (radius + toothLength)
            );
            
            ctx.lineTo(
                x + Math.cos(angle3) * (radius + toothLength),
                y + Math.sin(angle3) * (radius + toothLength)
            );
            
            ctx.lineTo(
                x + Math.cos(angle1 + angleStep) * radius,
                y + Math.sin(angle1 + angleStep) * radius
            );
        }
    }
</script>

</body>
</html>

此示例创建了两个具有不同 lineJoin 样式的齿轮形状。drawGear 函数生成具有指定参数的齿轮路径。

第一个齿轮使用斜接连接来创建锋利的齿,而第二个齿轮使用圆角连接来创建更平滑的齿。这表明 lineJoin 在实际应用中如何影响复杂的自定义形状。

来源

MDN Canvas lineJoin 文档

在本文中,我们通过各种示例深入探讨了 lineJoin 属性。理解这些技术有助于在 Web 应用程序中创建具有精确角样式的高质量图形。

作者

我的名字是 Jan Bodnar,我是一名充满激情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。至今,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出 所有 JS Canvas 教程