canvas绘图学习: 我的第一张Canvas图片

前言

近日开始学习canvas绘图,学习的过程中就发现相关的API繁琐复杂,于是萌生了绘制解析图的想法,既然都是要绘图,那不如就直接用canvas来绘制。这篇博客就记述了我第一次绘制canvas图片的过程,虽然成品比较丑但这个过程是收获颇丰的(づ。◕ᴗᴗ◕。)づ。

一、car方法解析

1.car方法介绍

ctx.car()方法用于绘制一个弧线路径,它有如下的六个参数:

  • x 圆心的x坐标
  • y 圆心的y坐标
  • radius 弧线的半径
  • startAngle 弧线的起始角度(单位为弧度)
  • endAngle 弧线的结束角度(单位为弧度)
  • counterclockwise 表示是否逆时针计算起始角度和结束角度(默认为顺时针)

2.弧线角度问题

(1)角度如何计算?

想要正确的使用car()方法必须要搞清楚的一个问题就是:startAngleendAngle这两个参数的角度是如何计算的?

这里的角度实际上是指从canvas坐标系的x轴正方向开始顺时针计算的角度。

而canvas坐标系是向左为x轴的正方向,向下为y轴的正方向。

canvas绘图学习: 我的第一张Canvas图片

因此在canvas坐标系中的 30°、130°、 230°、 330°,就如下图所示:

canvas绘图学习: 我的第一张Canvas图片

(2)counterclockwise参数与角度的关系

相关资料中对counterclockwise参数的定义是:示是否逆时针计算起始角度和结束角度(默认为顺时针)。

我一开始以为意思是:

  • 在如果counterclockwisefalse(默认情况),startAngleendAngle这两个角度是从x轴正方向开始顺时针计算;
  • 如果counterclockwisetrue,则startAngleendAngle从x轴正方向开始逆时针计算。

canvas绘图学习: 我的第一张Canvas图片

而实际上在我多次使用了arc()方法后发现上面的这种理解其实是错误的counterclockwise参数的实际含义是:

  • 在如果counterclockwisefalse(默认情况),则弧线是从开始角度沿顺时针方向到结束角度;
  • 如果counterclockwisetrue,则弧线是从开始角度沿逆时针方向到结束角度。

canvas绘图学习: 我的第一张Canvas图片

3.原理介绍

实际上car()方法的原理就类似于绘制了一个圆,然后从这个圆上截取出一段弧线。

下面这张图就是我所绘制的“car方法解析图”,在本文的剩余内容中我就将详细的介绍我绘制这张图的过程,以及绘制过程中所遇到的困难。

canvas绘图学习: 我的第一张Canvas图片

4.基于arc封装绘制圆弧的方法

(1)save与restore方法

在阅读一些文章的时候发现他们在封装canvas操作时都会用到ctx.save()ctx.restore()这两个方法,其中ctx.save()的作用是保存canvas的当前状态,ctx.restore()的作用是将canvas恢复到最近的保存状态。因此利用这两个方法我们可以在某个绘制操作开始之前保存之前的canvas状态,在绘制操作结束之后在恢复之前的状态,从而将绘制操作时的canvas状态与之前的canvas状态进行“隔离”。

保存与恢复的canvas状态包括以下的内容:

  1. 当前的变换矩阵。
  2. 当前的剪切区域。
  3. 当前的虚线列表。
  4. 以下属性当前的值:
strokeStyle 描边颜色/画笔颜色
fillStyle 填充颜色
globalAlpha 透明度
lineWidth 线宽
lineCap 线段端点形状
lineJoin 线段交点形状
lineDashOffset 虚线偏移量
shadowOffsetX、shadowOffsetY、shadowBlur、shadowColor 阴影水平偏移距离、阴影垂直偏移距离、阴影模糊度、阴影颜色
globalCompositeOperation 画布的显示模式
font 字体样式(字体粗细、字体大小、字体种类)
textAlign 文本的对齐方式(水平对齐方式)
textBaseline 文本的基线属性(垂直对齐方式)
direction 文本的方向
imageSmoothingEnabled 图片是否平滑

(2)完成封装

// 绘制弧线
/**
 *
 * @param {*} param0
 * @param {*} param0.ctx  canvas上下文对象
 * @param {Array} param0.center 圆心坐标
 * @param {number} param0.radius 圆的半径
 * @param {number} param0.startAngle 弧线的开始角度
 * @param {number} param0.endAngle 弧线的结束角度
 * @param {boolean} param0.counterclockwise 弧线是否通过逆时针绘制
 * @param {number} param0.width 弧线线宽
 * @param {string} param0.color 弧线颜色
 * @param {boolean} param0.isDash 弧线是否使用虚线
 */
function drawArc({
  ctx,
  center,
  radius,
  startAngle,
  endAngle,
  counterclockwise = false,
  width = 5,
  color = 'black',
  isDash = false,
}) {
  ctx.save()

  ctx.lineWidth = width
  ctx.strokeStyle = color
  isDash ? ctx.setLineDash([10, 10]) : ctx.setLineDash([])

  ctx.beginPath()
  ctx.arc(
    ...center,
    radius,
    (startAngle * Math.PI) / 180,
    (endAngle * Math.PI) / 180,
    counterclockwise
  )

  ctx.stroke()

  ctx.restore()
}

二、绘制虚线圆以及圆上的红色弧线

这一部分内容主要使用上一章介绍的 arc() 方法绘制。

1.绘制圆心点

首先绘制圆心点,但是canvas似乎没有绘制点的方法,因此只能用实心圆代替,于是我就封装了这样一个绘制点的方法:

// 绘制点
/**
 *
 * @param {*} param0
 * @param {*} param0.ctx  canvas上下文对象
 * @param {Array} param0.position 点的位置坐标
 * @param {number} param0.pixelSize 点的大小
 * @param {string} param0.color 点的颜色
 */
function drawPoint({ ctx, position, pixelSize = 5, color = 'black' }) {
  ctx.save()
  ctx.setLineDash([])

  ctx.beginPath()
  ctx.arc(...position, pixelSize, 0, 2 * Math.PI, false)
  ctx.fillStyle = color
  ctx.fill()
  ctx.restore()
}

利用这个drawPoint方法我们就可以绘制一个红色的圆心点:

drawPoint({
    ctx,
    position:[900,500],
    pixelSize:8,
    color:'red'
})

canvas绘图学习: 我的第一张Canvas图片

2.绘制虚线圆

接下来就是绘制虚线圆,不过这时我就遇到了第一个难题:如何绘制虚线?

在查阅了一些资料后我选择使用ctx.setLineDash()方法绘制虚线,这个方法在填充线时使用虚线模式,它接收一个数组,数组中的数字用于描述虚线中线段与间距的长度。

canvas绘图学习: 我的第一张Canvas图片

因此利用ctx.setLineDash()ctx.arc()这两个方法就可以封装一个绘制圆的方法,同时这个方法支持绘制虚线圆:

// 绘制圆
/**
 *
 * @param {*} param0
 * @param {*} param0.ctx  canvas上下文对象
 * @param {Array} param0.center 圆心坐标
 * @param {number} param0.radius 圆的半径
 * @param {number} param0.lineWidth 圆边线线宽
 * @param {string} param0.lineColor 圆边线颜色
 * @param {boolean} param0.isDash 圆边线是否使用虚线
 * @param {string} param0.fillColor 圆的填充颜色 (默认透明)
 */
function drawCircle({
  ctx,
  center,
  radius,
  lineWidth = 5,
  lineColor = 'black',
  isDash = false,
  fillColor = 'rgba(255,255,255,0)',
}) {
  ctx.save()

  ctx.lineWidth = lineWidth
  ctx.strokeStyle = lineColor
  isDash ? ctx.setLineDash([10, 10]) : ctx.setLineDash([])
  ctx.fillStyle = fillColor

  ctx.beginPath()
  ctx.arc(...center, radius, (2 * Math.PI) / 180, 0)

  ctx.stroke()
  ctx.fill()

  ctx.restore()
}

然后通过drawCircle()方法就可以绘制虚线圆

// t-绘制虚线圆
drawCircle({
    ctx,
    center:[900,500],
    radius:300,
    isDash:true
})

canvas绘图学习: 我的第一张Canvas图片

3.绘制红色弧线

// t-绘制红色弧线
drawArc({
  ctx,
  center: [900, 500],
  radius: 300,
  startAngle: 240,
  endAngle: 330,
  color: 'red',
})

canvas绘图学习: 我的第一张Canvas图片

三、绘制坐标系x轴与角度相关的箭头线

这几个内容之所以放在一起那时因为它们共同都涉及到一个问题,那就是如何在canvs中绘制箭头?这也是本次绘图过程我所攻坚的一个重点问题。

1.如何绘制箭头线?

想要在在canvas中实现绘制带箭头的直线和弧线是比较复杂的,需要有一定的数学基础。我为此写了这几篇博客,大家可以参考一下:

绘制箭头的数学基础:

canvas绘图学习:坐标正算-CSDN博客

canvas绘图学习:坐标反算-CSDN博客

绘制箭头的具体实现:

canvas绘图学习: 绘制箭头-CSDN博客

2.进行绘制

// t-计算弧线点坐标
const pA = calcCoordinate([900, 500], 300, 240),
  pB = calcCoordinate([900, 500], 300, 330)
// t-绘制从圆心点到弧线端点的箭头线
CanvasArrow.drawLineArrow({
  ctx,
  startPoint: [900, 500],
  endPoint: pA,
})
CanvasArrow.drawLineArrow({
  ctx,
  startPoint: [900, 500],
  endPoint: pB,
})

canvas绘图学习: 我的第一张Canvas图片

//t-绘制x轴
CanvasArrow.drawLineArrow({
  ctx,
  startPoint: [900, 500],
  endPoint: [1400,500],
  style:{
    lineWidth:3
  }
})

// t-绘制角度标识
CanvasArrow.drawArcArrow({
  ctx,
  center: [900, 500],
  radius: 40,
  startAngle: 0,
  endAngle: 240,
})

canvas绘图学习: 我的第一张Canvas图片

四、添加文本

1.绘制文本的方法

canvas中提供了绘制文本的一系列属性和方法。

绘制文本的方法主要是fillText()strokeText(),这两个方法都接收4个参数:要绘制的字符串、x坐标、y坐标和可选的最大像素宽度。它们的区别在于fillText()使用fillStyle绘制文本,而strokeText则使用strokeStyle来绘制文本。

canvas还提供了与文本相关的3个样式属性:

  1. font – 值为一个是一个字符串,字符串中分为三部分,分别用css语法来指定字体样式、大小、字体,例如:"bold 16px Arial"
  2. textAlign – 指定文本的对齐方式,可能的值包括”start”、”end”、”left”、”right”和”center”。
  3. textBaseLine – 指定文本的基线,可能的值包括”top”、”hanging”、”middle”、”alphabetic”、”ideographic”和”bottom”。

我基于上面提到的这些原生属性和方法进行封装,得到了两个添加文本的方法。

//添加文本
/**
 *
 * @param {*} ctx 2d绘图上下文
 * @param {string} text 文本内容
 * @param {*} position 文本添加的位置
 * @param {*} fontWeight  字体的粗细程度
 * @param {*} fontSize 字体大小
 * @param {*} fontFamily 字体种类
 * @param {*} fontColor  文本颜色
 * @param {*} textAlign  文本水平对齐方式
 * @param {*} textBaseLine 文本基线位置
 */
function addText({
  ctx,
  text,
  position,
  fontWeight = 'normal',
  fontSize = '18px',
  fontFamily = 'Arial',
  fontColor = 'black',
  textAlign = 'center',
  textBaseLine = 'middle',
}) {
  ctx.save()

  ctx.font = `${fontWeight} ${fontSize} ${fontFamily}`
  ctx.fillStyle = fontColor
  ctx.textAlign = textAlign
  ctx.textBaseLine = textBaseLine

  ctx.fillText(text, ...position)
}
// 批量添加文字
/**
 *
 * @param {*} ctx canvas上下文
 * @param {Array} texts 文本内容数组 , 数组中的元素为文本内容对象,
 每个文本内容对象都包含两个属性text和position,分别表示文本内容和文本位置
 * @param {Object} textStyle 文本样式对象 , 对象内容参考addText中与样式相关的参数
 * @example
 *  */
function addTexts(ctx, texts, textStyle = {}) {
  texts.forEach((textObj) => {
    addText({
      ctx,
      ...textObj,
      ...textStyle,
    })
  })
}

2.进行文本绘制

addTexts(ctx, [
  {
    text: 'x轴',
    position: [1380, 530],
  },
  {
    text: '开始角度',
    position:[770,380]
  },
  {
    text:'结束角度',
    position:[1100,440]
  }
])

addText({
  ctx,
  text: '绘制出来的弧线',
  position: [940, 180],
  fontWeight:'bold',
  fontSize:'24px'
})

canvas绘图学习: 我的第一张Canvas图片

至此我们的car方法解析图就绘制完成了,虽然感觉有点朴素有点丑,但是重要的是在绘制的过程中我学到了许多知识,不是吗!

五、下载绘制的canvas图片

下载canvas图片主要是分为两步,第一步是将我们绘制的canvas转成一个URL;然后第二步是下载这个URl。

1.canvas转URL

主要是使用canvas元素节点的toDataURL()方法。这个方法返回一个包含canvas图片展示的 data URL。我们可以通过type参数来指定图片的类型(type的属性值为MIME类型的字符串,默认是image/png即PNG格式)

因此canvas转URL的代码就如下所示:

const drawing = document.getElementById('drawing')
const imgUrl = drawing.toDataURL()

2.根据URL下载文件

这部分内容我就不详细论述了,具体方法可以参考我写的这篇文章:

前端文件下载和文件读取方法研究_只有文件路径前端怎么读取文件-CSDN博客

参考资料

  1. 《JavaScript高级程序设计》第18章 动画与canvas图形
  2. 学习 HTML5 Canvas 这一篇文章就够了 | 菜鸟教程
  3. CanvasRenderingContext2D.save() – Web API 接口参考 | MDN
  4. canvas如何绘制虚线_canvas 虚线-CSDN博客
  5. canvas绘制虚线setLineDash-CSDN博客
  6. CanvasRenderingContext2D.setLineDash() – Web API 接口参考 | MDN
  7. 【Java AWT 图形界面编程】在 Canvas 画布中绘制箭头图形 ( 数据准备 | 几个关键的计算公式 | 绘制箭头直线和尾翼 )-腾讯云开发者社区-腾讯云
  8. Canvas学习:绘制箭头_canvas画箭头-CSDN博客
  9. Arrows with Canvas
  10. HTMLCanvasElement.toDataURL() – Web API 接口参考 | MDN
  11. 【canvas】导出图片背景色_canvas 导出图片,背景色设置-CSDN博客

原文链接:https://juejin.cn/post/7349107838931763210 作者:Carlos_sam

(0)
上一篇 2024年3月24日 上午10:38
下一篇 2024年3月24日 上午10:49

相关推荐

发表回复

登录后才能评论