Canvas 03

颜色

在前面的章节例子中,我们都是使用默认的线条和填充样式。

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

color可以是css的颜色值,渐变对象和图案对象。 fillStyle属性例子

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例子

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();
    }
  }
}

透明度

  • 通过设置globalAlpha属性控制绘制图形透明度。globalAlpha = transparencyValue
  • 当然我们也可以通过设置fillStyle和strokeStyle的对应颜色值透明度效果。
ctx.strokeStyle = "rgb(255 0 0 / 50%)";
ctx.fillStyle = "rgb(255 0 0 / 50%)";

globalAlpha 示例 通过透明圆不断叠加,原先的源泉透明度会降低很多

function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");
  // 画背景
  ctx.fillStyle = "#FD0";
  ctx.fillRect(0, 0, 75, 75);
  ctx.fillStyle = "#6C0";
  ctx.fillRect(75, 0, 75, 75);
  ctx.fillStyle = "#09F";
  ctx.fillRect(0, 75, 75, 75);
  ctx.fillStyle = "#F30";
  ctx.fillRect(75, 75, 75, 75);
  ctx.fillStyle = "#FFF";

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

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

rgba()示例

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

  // 画背景
  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);

  // 画半透明矩形
  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 设置线条末端样式 butt round square
  • lineJoin = type 设置线条连接处样式 round bevel(三角) miter
  • getLineDash() 返回一个包含当前虚线样式,长度为非负偶数的数组。
  • lineDashOffset = value 设置虚线样式的起始偏移量。

lineWidth 属性的示例

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();
  }
}

我们会发现所有宽度为奇数的线条并没有精确的呈现,因为路径定位问题导致。 如果线条或者图形刚好落在画布的边界时,图像就会有清晰的边缘。 假设我们要绘制一条3,1到3,5的宽度为1.0的线条,实际canvas绘制的时候填充沿着中心线两边延伸一半。另外半个像素会以近似的方式进行渲染,意味这些像素只是部分着色,导致宽度为1.0的线条不准确的原因。 我们可以在宽1的线条,设置3.5,1和3.5,5两个连接点绘制线条,使其边缘正好落在像素边界上,保证填充准确的宽为1.0的线条。 像素网格和路径的位置关系,可以确保我们在缩放和变形时图像效果保持一致。 lineCap属性示例 默认butt

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

  // 创建路径
  ctx.strokeStyle = "#09f";
  ctx.beginPath();
  ctx.moveTo(10, 10);
  ctx.lineTo(140, 10);
  ctx.moveTo(10, 140);
  ctx.lineTo(140, 140);
  ctx.stroke();

  // 画线条
  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 属性的示例 默认是miter

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();
  });
}

miter会在两个线段连接处进行延伸至相交,效果收到miterLimit限制 miterLimit 属性示例 设置外延交点和连接点的最大距离,如果距离大于设置值,连接效果就会变成bevel。

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

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

  // 绘制参考线
  ctx.strokeStyle = "#09f";
  ctx.lineWidth = 2;
  ctx.strokeRect(-5, 50, 160, 50);

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

  // 检查输入
  if (document.getElementById("miterLimit").checkValidity()) {
    ctx.miterLimit = parseFloat(document.getElementById("miterLimit").value);
  }

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

虚线使用

使用setLineDash方法和lineDashOffset属性来控制我们绘制虚线的样式。 例子

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对象,然后赋值给图形的fillStyle或strokeStyle. createLinearGradient(x1, y1, x2, y2) createRadialGradient(x1, y1, r1, x2, y2, r2) 初始化出我们的canvasGradient对象后可以使用方法gradient.addColorStop(position, color)进行渐变上色。

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

createLinearGradient示例

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

  // 创建渐变
  const linGrad = ctx.createLinearGradient(0, 0, 0, 150);
  linGrad.addColorStop(0, "#00ABEB");
  linGrad.addColorStop(0.5, "#fff");
  linGrad.addColorStop(0.5, "#26C000");
  linGrad.addColorStop(1, "#fff");

  const linGrad2 = ctx.createLinearGradient(0, 50, 0, 95);
  linGrad2.addColorStop(0.5, "#000");
  linGrad2.addColorStop(1, "rgb(0 0 0 / 0%)");

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

  // 画图形
  ctx.fillRect(10, 10, 130, 130);
  ctx.strokeRect(50, 50, 50, 50);
}

createRadialGradient示例

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

  // 创建渐变
  var radgrad = ctx.createRadialGradient(45, 45, 10, 52, 50, 30);
  radgrad.addColorStop(0, "#A7D30C");
  radgrad.addColorStop(0.9, "#019F62");
  radgrad.addColorStop(1, "rgba(1,159,98,0)");

  var radgrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50);
  radgrad2.addColorStop(0, "#FF5F98");
  radgrad2.addColorStop(0.75, "#FF0188");
  radgrad2.addColorStop(1, "rgba(255,1,136,0)");

  var radgrad3 = ctx.createRadialGradient(95, 15, 15, 102, 20, 40);
  radgrad3.addColorStop(0, "#00C9FF");
  radgrad3.addColorStop(0.8, "#00B5E2");
  radgrad3.addColorStop(1, "rgba(0,201,255,0)");

  var radgrad4 = ctx.createRadialGradient(0, 150, 50, 0, 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, 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);
}

图案样式

createPattern(image, type) image表示Image对象的引用或者canvas对象,部分浏览器存在兼容问题可能不支持canva对象。 type的可选值 repeat repeat-x repeat-y no-repeat

我们在设置图案样式前,需要确保图像已经加载完成后再使用。 createPattern示例

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

  // 创建新 image 对象,用作图案
  const img = new Image();
  img.src = "canvas_create_pattern.png";
  img.onload = () => {
    // 创建图案
    const pattern = ctx.createPattern(img, "repeat");
    ctx.fillStyle = pattern;
    ctx.fillRect(0, 0, 150, 150);
  };
}

阴影

  • shadowOffsetX = float 正值阴影向右延伸
  • shadowOffsetY = float 正值阴影向下延伸
  • shadowBlur = float 阴影的模糊程度
  • shadowColor = color

文字阴影示例

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);
}

canvas填充规则

你可以选择一个填充规则,从而根据某个路径与某个路径里面或外面是否被填充,这对于自己与自己路径相交或者路径被嵌套时使用。 例如同心圆的绘制。 可选的两个值 nonzero (默认)、evenodd(奇偶环绕规则) 示例(中间镂空的同心圆)

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");
}

写在最后

未完待续… 希望大家好好生活,健康快乐。