应用样式和颜色

绘制形状一章中,我们只使用了默认的线条和填充样式。在这里,我们将探索可用的 canvas 选项,让我们的绘图更具吸引力。您将学习如何为绘图添加不同的颜色、线条样式、渐变、图案和阴影。

注意:屏幕阅读器无法访问 Canvas 内容。如果 Canvas 纯粹是装饰性的,请在 <canvas> 开始标签中包含 role="presentation"。否则,请直接在 Canvas 元素上包含描述性文本作为 aria-label 属性的值,或者在开始和结束 Canvas 标签内包含回退内容。Canvas 内容不是 DOM 的一部分,但嵌套的回退内容是。

颜色

到目前为止,我们只看到了绘图上下文的方法。如果我们要为形状应用颜色,可以使用两个重要的属性:fillStylestrokeStyle

fillStyle = color

设置填充形状时使用的样式。

strokeStyle = color

设置形状轮廓的样式。

color 是一个字符串,表示 CSS <color>、渐变对象或图案对象。我们稍后会介绍渐变和图案对象。默认情况下,描边和填充颜色都设置为黑色(CSS 颜色值 #000000)。

注意:当您设置 strokeStyle 和/或 fillStyle 属性时,新值将成为此后所有绘制形状的默认值。对于每种需要不同颜色的形状,您都需要重新分配 fillStylestrokeStyle 属性。

根据规范,您可以输入的有效字符串应该是 CSS <color> 值。以下每个示例都描述了相同的颜色。

js
// these all set the fillStyle to 'orange'

ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255 165 0)";
ctx.fillStyle = "rgb(255 165 0 / 100%)";

一个 fillStyle 示例

在此示例中,我们再次使用两个 for 循环绘制一个不同颜色的矩形网格。生成的图像应该类似于屏幕截图。这里没有发生什么特别的事情。我们使用两个变量 ij 为每个正方形生成唯一的 RGB 颜色,并且只修改红色和绿色值。蓝色通道有一个固定值。通过修改通道,您可以生成各种调色板。通过增加步长,您可以实现类似 Photoshop 使用的调色板的效果。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");
  for (let i = 0; i < 6; i++) {
    for (let j = 0; j < 6; j++) {
      ctx.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)} ${Math.floor(
        255 - 42.5 * j,
      )} 0)`;
      ctx.fillRect(j * 25, i * 25, 25, 25);
    }
  }
}

结果如下所示

一个 strokeStyle 示例

此示例与上述示例类似,但使用 strokeStyle 属性更改形状轮廓的颜色。我们使用 arc() 方法绘制圆形而不是正方形。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");
  for (let i = 0; i < 6; i++) {
    for (let j = 0; j < 6; j++) {
      ctx.strokeStyle = `rgb(0 ${Math.floor(255 - 42.5 * i)} ${Math.floor(
        255 - 42.5 * j,
      )})`;
      ctx.beginPath();
      ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, 2 * Math.PI, true);
      ctx.stroke();
    }
  }
}

结果如下所示

透明度

除了在 canvas 上绘制不透明形状外,我们还可以绘制半透明(或半透明)形状。这可以通过设置 globalAlpha 属性或为描边和/或填充样式指定半透明颜色来实现。

globalAlpha = transparencyValue

将指定的透明度值应用于画布上绘制的所有未来形状。该值必须介于 0.0(完全透明)到 1.0(完全不透明)之间。该值默认情况下为 1.0(完全不透明)。

如果您想在画布上绘制许多具有相似透明度的形状,globalAlpha 属性会很有用,但通常在设置颜色时为单个形状设置透明度会更有用。

因为 strokeStylefillStyle 属性接受 CSS rgb 颜色值,所以我们可以使用以下符号为它们分配透明颜色。

js
// Assigning transparent colors to stroke and fill style

ctx.strokeStyle = "rgb(255 0 0 / 50%)";
ctx.fillStyle = "rgb(255 0 0 / 50%)";

rgb() 函数有一个可选的额外参数。最后一个参数设置此特定颜色的透明度值。有效范围指定为 0%(完全透明)到 100%(完全不透明)之间的百分比,或 0.0(相当于 0%)到 1.0(相当于 100%)之间的数字。

一个 globalAlpha 示例

在此示例中,我们将绘制一个由四种不同颜色正方形组成的背景。在此之上,我们将绘制一组半透明圆形。globalAlpha 属性设置为 0.2,此后所有形状都将使用此值。for 循环中的每一步都绘制一组半径递增的圆形。最终结果是径向渐变。通过相互叠加越来越多的圆形,我们有效地降低了已绘制圆形的透明度。通过增加步数并实际绘制更多圆形,背景将从图像中心完全消失。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");
  // draw background
  ctx.fillStyle = "#ffdd00";
  ctx.fillRect(0, 0, 75, 75);
  ctx.fillStyle = "#66cc00";
  ctx.fillRect(75, 0, 75, 75);
  ctx.fillStyle = "#0099ff";
  ctx.fillRect(0, 75, 75, 75);
  ctx.fillStyle = "#ff3300";
  ctx.fillRect(75, 75, 75, 75);
  ctx.fillStyle = "white";

  // set transparency value
  ctx.globalAlpha = 0.2;

  // Draw semi transparent circles
  for (let i = 0; i < 7; i++) {
    ctx.beginPath();
    ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true);
    ctx.fill();
  }
}

使用带有 alpha 透明度的 rgb() 示例

在第二个示例中,我们执行与上述示例类似的操作,但不是相互绘制圆形,而是绘制了不透明度递增的小矩形。使用 rgb() 可以为您提供更多的控制和灵活性,因为我们可以单独设置填充和描边样式。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");

  // Draw background
  ctx.fillStyle = "rgb(255 221 0)";
  ctx.fillRect(0, 0, 150, 37.5);
  ctx.fillStyle = "rgb(102 204 0)";
  ctx.fillRect(0, 37.5, 150, 37.5);
  ctx.fillStyle = "rgb(0 153 255)";
  ctx.fillRect(0, 75, 150, 37.5);
  ctx.fillStyle = "rgb(255 51 0)";
  ctx.fillRect(0, 112.5, 150, 37.5);

  // Draw semi transparent rectangles
  for (let i = 0; i < 10; i++) {
    ctx.fillStyle = `rgb(255 255 255 / ${(i + 1) / 10})`;
    for (let j = 0; j < 4; j++) {
      ctx.fillRect(5 + i * 14, 5 + j * 37.5, 14, 27.5);
    }
  }
}

线条样式

有几个属性允许我们设置线条样式。

lineWidth = value

设置未来绘制的线条宽度。

lineCap = type

设置线条末端的显示方式。

lineJoin = type

设置线条连接处的“拐角”显示方式。

miterLimit = value

建立两条线以锐角连接时斜接的限制,让您可以控制连接处的厚度。

getLineDash()

返回当前线条虚线图案数组,其中包含偶数个非负数。

setLineDash(segments)

设置当前线条虚线图案。

lineDashOffset = value

指定虚线数组在一条线上开始的位置。

通过查看下面的示例,您将更好地了解这些功能。

一个 lineWidth 示例

此属性设置当前线条粗细。值必须为正数。默认情况下,此值设置为 1.0 单位。

线宽是描边在给定路径上居中的厚度。换句话说,绘制区域延伸到路径两侧一半的线宽。由于 canvas 坐标不直接引用像素,因此必须特别注意才能获得清晰的水平和垂直线。

在下面的示例中,绘制了 10 条具有递增线宽的直线。最左边的线宽为 1.0 单位。然而,由于路径的位置,最左边和所有其他奇数宽度厚度线看起来不清晰。

js
function draw() {
  const ctx = document.getElementById("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();
  }
}

注意:如果您想知道线条边缘附近显示为灰色而不是黑色,请查看上一章中的看到模糊的边缘?部分。

一个 lineCap 示例

lineCap 属性决定了每条线的端点如何绘制。此属性有三个可能的值:buttroundsquare。默认情况下,此属性设置为 butt

butt(平头)

线条的末端在端点处呈方形。

round

线条的末端是圆形的。

square

线条的末端通过添加一个宽度相等且高度为线条粗细一半的盒子而呈方形。

只有路径的起始和最终端点受到影响:如果路径用 closePath() 关闭,则没有起始和最终端点;相反,路径中的所有端点都使用 lineJoin 样式的当前设置连接到其连接的前一个和下一个段。

在此示例中,我们将绘制三条线,每条线的 lineCap 属性值不同。我还添加了两个参考线,以查看三者之间的确切差异。每条线都精确地在这些参考线上开始和结束。

左边的线使用默认的 butt 选项。您会注意到它与参考线完全齐平绘制。第二条设置为使用 round 选项。这会在末端添加一个半圆形,其半径是线宽的一半。右边的线使用 square 选项。这会添加一个宽度相等且高度为线宽一半的盒子。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");

  // Draw guides
  ctx.strokeStyle = "#0099ff";
  ctx.beginPath();
  ctx.moveTo(10, 10);
  ctx.lineTo(140, 10);
  ctx.moveTo(10, 140);
  ctx.lineTo(140, 140);
  ctx.stroke();

  // Draw lines
  ctx.strokeStyle = "black";
  ["butt", "round", "square"].forEach((lineCap, i) => {
    ctx.lineWidth = 15;
    ctx.lineCap = lineCap;
    ctx.beginPath();
    ctx.moveTo(25 + i * 50, 10);
    ctx.lineTo(25 + i * 50, 140);
    ctx.stroke();
  });
}

一个 lineJoin 示例

lineJoin 属性决定了形状中两个非零长度的连接段(直线、弧或曲线)如何连接在一起(零长度的退化段,其指定的端点和控制点完全在同一位置,将被跳过)。

此属性有三个可能的值:roundbevelmiter。默认情况下,此属性设置为 miter。请注意,如果两个连接的段方向相同,则 lineJoin 设置无效,因为在这种情况下不会添加连接区域。

round

通过填充以连接段的公共端点为中心的新圆盘扇区,使形状的角变圆。这些圆角的半径等于线宽的一半。

bevel

填充连接段的公共端点与每个段的独立外部矩形角之间的附加三角形区域。

miter

连接的段通过延伸其外部边缘连接到单个点来连接,从而填充一个额外的菱形区域。此设置受下面解释的 miterLimit 属性的影响。

下面的示例绘制了三个不同的路径,演示了这三个 lineJoin 属性设置;输出如上所示。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");
  ctx.lineWidth = 10;
  ["round", "bevel", "miter"].forEach((lineJoin, i) => {
    ctx.lineJoin = lineJoin;
    ctx.beginPath();
    ctx.moveTo(-5, 5 + i * 40);
    ctx.lineTo(35, 45 + i * 40);
    ctx.lineTo(75, 5 + i * 40);
    ctx.lineTo(115, 45 + i * 40);
    ctx.lineTo(155, 5 + i * 40);
    ctx.stroke();
  });
}

miterLimit 属性的演示

正如您在前面的示例中看到的,当使用 miter 选项连接两条线时,两条连接线的外部边缘会延伸到它们相遇的点。对于彼此之间角度较大的线,这个点距离内部连接点不远。然而,随着每条线之间角度的减小,这些点之间的距离(斜接长度)呈指数级增长。

miterLimit 属性决定了外部连接点可以距离内部连接点多远。如果两条线超过此值,则会绘制一个斜角连接。请注意,最大斜接长度是当前坐标系中测量的线宽与此 miterLimit 属性值(其默认值为 HTML <canvas> 中的 10.0)的乘积,因此 miterLimit 可以独立于当前显示比例或路径的任何仿射变换进行设置:它只影响线边缘的有效渲染形状。

更准确地说,斜接限制是延伸长度(在 HTML canvas 中,它测量的是线连接边缘的外部角与路径中指定连接段的公共端点之间的距离)与线宽的一半的最大允许比率。它也可以等效地定义为边缘内部和外部连接点之间的距离与总线宽的最大允许比率。它等于连接段的最小内角的一半的余割,低于该角度将不渲染斜接连接,而只渲染斜角连接。

  • miterLimit = max miterLength / lineWidth = 1 / sin ( min θ / 2 )
  • 默认的斜接限制 10.0 将剥离所有小于约 11 度的锐角斜接。
  • 斜接限制等于 √2 ≈ 1.4142136(向上取整)将剥离所有锐角的斜接,仅保留钝角或直角的斜接。
  • 斜接限制等于 1.0 是有效的,但将禁用所有斜接。
  • 斜接限制值小于 1.0 是无效的。

这是一个小演示,您可以在其中动态设置 miterLimit 并查看其如何影响画布上的形状。蓝色线条显示了之字形图案中每条线的起点和终点。

如果您在此演示中将 miterLimit 值指定为低于 4.2,则所有可见角都不会与斜接延伸连接,而只会与蓝色线条附近的小斜角连接;如果 miterLimit 大于 10,则此演示中的大多数角都应与远离蓝色线条的斜接连接,并且其高度在角之间从左到右递减,因为它们以递增的角度连接;对于中间值,左侧的角将仅与蓝色线条附近的斜角连接,而右侧的角将与斜接延伸连接(高度也递减)。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");

  // Clear canvas
  ctx.clearRect(0, 0, 150, 150);

  // Draw guides
  ctx.strokeStyle = "#0099ff";
  ctx.lineWidth = 2;
  ctx.strokeRect(-5, 50, 160, 50);

  // Set line styles
  ctx.strokeStyle = "black";
  ctx.lineWidth = 10;

  // check input
  if (document.getElementById("miterLimit").checkValidity()) {
    ctx.miterLimit = parseFloat(document.getElementById("miterLimit").value);
  }

  // Draw lines
  ctx.beginPath();
  ctx.moveTo(0, 100);
  for (let i = 0; i < 24; i++) {
    const dy = i % 2 === 0 ? 25 : -25;
    ctx.lineTo(i ** 1.5 * 2, 75 + dy);
  }
  ctx.stroke();
  return false;
}

使用虚线

setLineDash 方法和 lineDashOffset 属性指定线条的虚线图案。setLineDash 方法接受一个数字列表,该列表指定交替绘制线条和间隙的距离,lineDashOffset 属性设置图案开始的偏移量。

在此示例中,我们正在创建行进蚂蚁效果。这是一种常用于计算机图形程序选择工具的动画技术。它通过对边框进行动画处理来帮助用户区分选择边框和图像背景。在本教程的后面部分,您可以学习如何实现此效果和其他基本动画

js
const ctx = document.getElementById("canvas").getContext("2d");
let offset = 0;

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.setLineDash([4, 2]);
  ctx.lineDashOffset = -offset;
  ctx.strokeRect(10, 10, 100, 100);
}

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

march();

渐变

就像任何普通的绘图程序一样,我们可以使用线性、径向和锥形渐变填充和描边形状。我们使用以下方法之一创建 CanvasGradient 对象。然后我们可以将此对象分配给 fillStylestrokeStyle 属性。

createLinearGradient(x1, y1, x2, y2)

创建一个线性渐变对象,起点为 (x1, y1),终点为 (x2, y2)。

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

创建径向渐变。参数表示两个圆形,一个圆心在 (x1, y1),半径为 r1,另一个圆心在 (x2, y2),半径为 r2

createConicGradient(angle, x, y)

创建一个锥形渐变对象,其起始角度为 angle 弧度,位置为 (x, y)。

例如

js
const lineargradient = ctx.createLinearGradient(0, 0, 150, 150);
const radialgradient = ctx.createRadialGradient(75, 75, 0, 75, 75, 100);

创建 CanvasGradient 对象后,我们可以使用 addColorStop() 方法为其分配颜色。

gradient.addColorStop(position, color)

gradient 对象上创建一个新的颜色停止点。position 是介于 0.0 和 1.0 之间的一个数字,定义了颜色在渐变中的相对位置,color 参数必须是一个表示 CSS <color> 的字符串,指示渐变在过渡中的该偏移量处应达到的颜色。

您可以根据需要向渐变添加任意数量的颜色停止点。下面是一个从白色到黑色的非常简单的线性渐变。

js
const lineargradient = ctx.createLinearGradient(0, 0, 150, 150);
lineargradient.addColorStop(0, "white");
lineargradient.addColorStop(1, "black");

一个 createLinearGradient 示例

在此示例中,我们将创建两种不同的渐变。如您所见,strokeStylefillStyle 属性都可以接受 canvasGradient 对象作为有效输入。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");

  // Create gradients
  const linGrad = ctx.createLinearGradient(0, 0, 0, 150);
  linGrad.addColorStop(0, "#00ABEB");
  linGrad.addColorStop(0.5, "white");
  linGrad.addColorStop(0.5, "#26C000");
  linGrad.addColorStop(1, "white");

  const linGrad2 = ctx.createLinearGradient(0, 50, 0, 95);
  linGrad2.addColorStop(0.5, "black");
  linGrad2.addColorStop(1, "transparent");

  // assign gradients to fill and stroke styles
  ctx.fillStyle = linGrad;
  ctx.strokeStyle = linGrad2;

  // draw shapes
  ctx.fillRect(10, 10, 130, 130);
  ctx.strokeRect(50, 50, 50, 50);
}

第一个是背景渐变。如您所见,我们在同一位置分配了两种颜色。这样做是为了实现非常尖锐的颜色过渡——在此示例中是从白色到绿色。通常,您定义颜色停止点的顺序无关紧要,但在这种特殊情况下,它确实很重要。如果您按照它们应该出现的顺序保留分配,这将不是问题。

在第二个渐变中,我们没有分配起始颜色(在 0.0 位置),因为它不是严格必需的,因为它会自动采用下一个颜色停止点的颜色。因此,在 0.5 位置分配黑色颜色会自动使渐变从开始到此停止点变为黑色。

一个 createRadialGradient 示例

在此示例中,我们将定义四种不同的径向渐变。因为我们可以控制渐变的起始点和结束点,所以我们可以实现比我们通常在例如 Photoshop 中看到的“经典”径向渐变(即,具有单个中心点且渐变以圆形向外扩展的渐变)更复杂的效果。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");

  // Create gradients
  const radGrad = ctx.createRadialGradient(45, 45, 10, 52, 50, 30);
  radGrad.addColorStop(0, "#A7D30C");
  radGrad.addColorStop(0.9, "#019F62");
  radGrad.addColorStop(1, "transparent");

  const radGrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50);
  radGrad2.addColorStop(0, "#FF5F98");
  radGrad2.addColorStop(0.75, "#FF0188");
  radGrad2.addColorStop(1, "transparent");

  const radGrad3 = ctx.createRadialGradient(95, 15, 15, 102, 20, 40);
  radGrad3.addColorStop(0, "#00C9FF");
  radGrad3.addColorStop(0.8, "#00B5E2");
  radGrad3.addColorStop(1, "transparent");

  const radGrad4 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90);
  radGrad4.addColorStop(0, "#F4F201");
  radGrad4.addColorStop(0.8, "#E4C700");
  radGrad4.addColorStop(1, "transparent");

  // draw shapes
  ctx.fillStyle = radGrad4;
  ctx.fillRect(0, 0, 150, 150);
  ctx.fillStyle = radGrad3;
  ctx.fillRect(0, 0, 150, 150);
  ctx.fillStyle = radGrad2;
  ctx.fillRect(0, 0, 150, 150);
  ctx.fillStyle = radGrad;
  ctx.fillRect(0, 0, 150, 150);
}

在这种情况下,我们将起点稍微偏移了终点,以实现球形 3D 效果。最好尽量避免内部和外部圆形重叠,因为这会导致难以预测的奇怪效果。

四个渐变中的最后一个颜色停止点使用了完全透明的颜色。如果您希望从此处到上一个颜色停止点有良好的过渡,则两种颜色应相等。这在代码中不是很明显,因为它使用了两种不同的 CSS 颜色方法作为演示,但在第一个渐变中 #019F62 = rgb(1 159 98 / 100%)

一个 createConicGradient 示例

在此示例中,我们将定义两个不同的锥形渐变。锥形渐变与径向渐变不同,因为它不是创建圆形,而是围绕一个点旋转。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");

  // Create gradients
  const conicGrad1 = ctx.createConicGradient(2, 62, 75);
  conicGrad1.addColorStop(0, "#A7D30C");
  conicGrad1.addColorStop(1, "white");

  const conicGrad2 = ctx.createConicGradient(0, 187, 75);
  // we multiply our values by Math.PI/180 to convert degrees to radians
  conicGrad2.addColorStop(0, "black");
  conicGrad2.addColorStop(0.25, "black");
  conicGrad2.addColorStop(0.25, "white");
  conicGrad2.addColorStop(0.5, "white");
  conicGrad2.addColorStop(0.5, "black");
  conicGrad2.addColorStop(0.75, "black");
  conicGrad2.addColorStop(0.75, "white");
  conicGrad2.addColorStop(1, "white");

  // draw shapes
  ctx.fillStyle = conicGrad1;
  ctx.fillRect(12, 25, 100, 100);
  ctx.fillStyle = conicGrad2;
  ctx.fillRect(137, 25, 100, 100);
}

第一个渐变位于第一个矩形的中心,并将一个绿色色标从开始移动到结束处的白色色标。角度从 2 弧度开始,由于起始/结束线指向东南方向,因此引人注目。

第二个渐变也位于第二个矩形的中心。这个渐变有多个色标,在旋转的每个四分之一处从黑色交替到白色。这给了我们棋盘格效果。

图案

在上一页的一个示例中,我们使用一系列循环来创建图像图案。然而,有一个更简单的方法:createPattern() 方法。

createPattern(image, type)

创建并返回一个新的画布图案对象。image 是图像的来源(即 HTMLImageElementSVGImageElement、另一个 HTMLCanvasElementOffscreenCanvasHTMLVideoElementVideoFrame,或 ImageBitmap)。type 是一个字符串,指示如何使用图像。

该类型指定如何使用图像来创建图案,并且必须是以下字符串值之一

repeat

图像在垂直和水平方向上平铺。

repeat-x

图像水平平铺,但不垂直平铺。

repeat-y

图像垂直平铺,但不水平平铺。

no-repeat

不平铺图像。它只使用一次。

我们使用此方法创建 CanvasPattern 对象,它与我们上面看到的渐变方法非常相似。一旦创建了图案,我们就可以将其分配给 fillStylestrokeStyle 属性。例如

js
const img = new Image();
img.src = "some-image.png";
const pattern = ctx.createPattern(img, "repeat");

注意:drawImage() 方法一样,您必须确保在调用此方法之前加载了所使用的图像,否则图案可能会绘制不正确。

一个 createPattern 示例

在这个最后一个示例中,我们将创建一个图案并将其分配给 fillStyle 属性。唯一值得注意的是图像的 onload 处理程序的使用。这是为了确保图像在分配给图案之前已加载。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");

  // create new image object to use as pattern
  const img = new Image();
  img.src = "canvas_create_pattern.png";
  img.onload = () => {
    // create pattern
    const pattern = ctx.createPattern(img, "repeat");
    ctx.fillStyle = pattern;
    ctx.fillRect(0, 0, 150, 150);
  };
}

阴影

使用阴影只需四个属性

shadowOffsetX = float

表示阴影应从对象水平延伸的距离。此值不受变换矩阵影响。默认值为 0。

shadowOffsetY = float

表示阴影应从对象垂直延伸的距离。此值不受变换矩阵影响。默认值为 0。

shadowBlur = float

表示模糊效果的大小;此值不对应于像素数量,也不受当前变换矩阵影响。默认值为 0。

shadowColor = color

表示阴影效果颜色的标准 CSS 颜色值;默认情况下,它是完全透明的黑色。

shadowOffsetXshadowOffsetY 属性指示阴影应从对象在 X 和 Y 方向上延伸的距离;这些值不受当前变换矩阵的影响。使用负值使阴影向上或向左延伸,使用正值使阴影向下或向右延伸。它们都默认为 0。

shadowBlur 属性指示模糊效果的大小;此值不对应于像素数量,也不受当前变换矩阵的影响。默认值为 0。

shadowColor 属性是一个标准的 CSS 颜色值,指示阴影效果的颜色;默认情况下,它是完全透明的黑色。

注意:阴影仅针对 source-over 合成操作绘制。

一个带阴影文本的例子

此示例绘制带有阴影效果的文本字符串。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");

  ctx.shadowOffsetX = 2;
  ctx.shadowOffsetY = 2;
  ctx.shadowBlur = 2;
  ctx.shadowColor = "rgb(0 0 0 / 50%)";

  ctx.font = "20px Times New Roman";
  ctx.fillStyle = "Black";
  ctx.fillText("Sample String", 5, 30);
}

我们将在下一章关于绘制文本中介绍 font 属性和 fillText 方法。

Canvas 填充规则

使用 fill(或 clipisPointInPath)时,您可以选择提供一个填充规则算法来确定一个点是在路径内部还是外部,从而决定是否填充。这在路径自身相交或嵌套时非常有用。

有两种可能的值

nonzero

非零缠绕规则,这是默认规则。

evenodd

奇偶缠绕规则

在此示例中,我们使用 evenodd 规则。

js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");
  ctx.beginPath();
  ctx.arc(50, 50, 30, 0, Math.PI * 2, true);
  ctx.arc(50, 50, 15, 0, Math.PI * 2, true);
  ctx.fill("evenodd");
}