canvas 组件基础学习

canvas组件

1.了解canvas

​ 1.1 canvas就是在HTML上可供js实现绘图的区域,通过标签来在HTML定义

​ 1.2 画布具体的大小需要直接用width和heigh来定义,并且不需要带单位

<canves id='canvas' width='600' heigt='600'>
	// ie9 以下不兼容,将不支持的提示语写在标签内部
	浏览器不支持canvas,请更换浏览器
</canves>

​ 1.3 canvas本质就是一张空白的图片,只不过内容需要我们自己画上去

2.初体验canvas

​ 2.1 想要使用canvas进行绘制,需要先获取到canvas工具箱

const canvas = document.querySelector('#canvas')
// 获取工具箱,选择2d模式
const ctx = canvas.getContext('2d')

​ 2.2 绘画基本逻辑:通过api和xy坐标实现绘制,比如要在画布上画一条线,看图
请添加图片描述

![请添加图片描述](https://img-blog.csdnimg.cn/58d2d247911241b2b3d5fa4bc181a0a5.png

  // 把笔尖移动到位置
  ctx.moveTo(100, 100)
  // 以线的形式移动到位置,如果只执行这一步是看不到线的,他仅仅是移动的轨迹,并没有画上去
  ctx.lineTo(600, 100)
  // 设置线宽,不需要单位,如果不写线的颜色默认是黑色,1px宽,会产生绘制线颜色和宽度的问题
  ctx.lineWidth = 2
  // 对走过的轨迹描边,此时才会对走过的轨迹进行绘制,才能在页面上看到
  ctx.stroke() // 对于线叫描边,对于面叫填充

​ 2.3 绘制线颜色和宽度的问题

请添加图片描述

​ 出现该问题是因为canvas的绘制机制,即canvas绘制的线是通过中心对齐的方式对齐坐标,所以当线宽只有1px是从坐标的x.5到x.5进行绘制,但是浏览器会 把1px以下的线当1px显示,所以canvas绘制1px线实际为2px,对应颜色也被其拉伸。
请添加图片描述

​ 提示:所以当对绘制图像有要求就应该以2px及以上绘制

3.重新绘制轨迹(绘制三条平行线)

  // 绘制第一条
  ctx.moveTo(100, 100)
  ctx.lineTo(300, 100)
  ctx.lineWidth = 10
  // 设置线的颜色
  ctx.strokeStyle = 'red'
  ctx.stroke()
  
  // 绘制第二条
  // ctx.beginPath() 绘制一条新轨迹
  ctx.moveTo(100, 150)
  ctx.lineTo(300, 150)
  ctx.lineWidth = 6
  ctx.strokeStyle = 'yellow'
  ctx.stroke()
  
  // 绘制第三条
  // ctx.beginPath()
  ctx.moveTo(100, 200)
  ctx.lineTo(300, 200)
  ctx.lineWidth = 2
  ctx.strokeStyle = 'blue'
  ctx.stroke()

​ 执行上述代码后会发现会是以下效果:

​ 红色的线被绘制了一次,黄色的线绘制了两次,蓝色的线绘制了三次,并且每次绘制都会在上次的线上再绘制一次,这显然不是我们想要的。出现这个原因是 因为我们每次绘制的笔并没有断掉或者说重新开始,三次绘制其实都是一次绘制的结果。要想实现三条互不相干的线还要重新开始笔迹。解开代码中注释内容 ctx.beginPath()即可
请添加图片描述

4.自动闭合和填充(绘制三角形)

  ctx.moveTo(100, 100)
  ctx.lineTo(300, 100)
  ctx.lineTo(250, 200)
  ctx.lineWidth = 8
  ctx.strokeStyle = 'black'
  // ctx.closePath() 自动闭合
  ctx.stroke()

​ 4.1 执行上述代码后会发现会是以下效果:此时需要对缺角部分进行闭合,解开注释代码 ctx.closePath() 即可
请添加图片描述
​ 4.2 或者可以通过填充实现

  ctx.moveTo(100, 100)
  ctx.lineTo(300, 100)
  ctx.lineTo(250, 200)
  // 填充方式必须返回起点
  ctx.lineTo(100, 100)
  ctx.lineWidth = 8
  ctx.strokeStyle = 'black'
  // 填充三条线形成的区域
  ctx.fill()

请添加图片描述

5.非零填充规则(绘制回型填充)

​ 5.1 绘制回型区域

  // 第一个正方形顺时针
  ctx.moveTo(x - 200, y - 200)
  ctx.lineTo(x + 200, y - 200)
  ctx.lineTo(x + 200, y + 200)
  ctx.lineTo(x - 200, y + 200)
  ctx.lineWidth = 4

  // 第二个正方形逆时针
  ctx.moveTo(x - 100, y - 100)
  ctx.lineTo(x - 100, y + 100)
  ctx.lineTo(x + 100, y + 100)
  ctx.lineTo(x + 100, y - 100)

  // 第三个正方形顺时针
  ctx.moveTo(x + 50, y + 50)
  ctx.lineTo(x + 150, y + 50)
  ctx.lineTo(x + 150, y + 150)
  ctx.lineTo(x + 50, y + 150)

  ctx.fill()

​ 5.2 在浏览器上执行5.1代码后可以看到效果,那为什么会这样填充,为什么需要区分顺逆方向呢?其实填充区域是有对应规范的,这就是非零填充原则,具体原理如图;
请添加图片描述

6. 绘制线样式

​ 6.1 线段两端的样式

ctx.lineCap = 'butt' // 默认两端没有样式
ctx.lineCap = 'round' // 默认两端为圆形
ctx.lineCap = 'square' // 默认两端为方形

​ 6.2 线段拐点样式(线段拐弯时候的样式)

ctx.lineJoin = 'miter' // 默认跟随线段样式
ctx.lineJoin = 'round' // 圆角拐点样式
ctx.lineJoin = 'bevel' // 平角拐点样式

​ 6.3 绘制虚线

// 绘制2个像素实线,3个像素空白,循环往复直至线段结束
ctx.setLineDash([2,3])

// 绘制2个像素实线,3个像素空白,4个像素实线,5个像素空白,6个像素实线,2个像素空白... 循环往复直至线段结束
ctx.setLineDash([2,3,4,5,6])

​ 6.4 绘制渐变线

​ canvas本身具有的绘制渐变色只针对面,对于线无法操作(最新版本已经支持),所以需要换个思路来绘制线。

​ 思路:将要画的线分成多个1像素线组合起来,通过rgb颜色绘制每1像素长度

  // 绘制条长度300的由红色渐变到蓝色的线,将其分成300个1px线组成的线
  for (let i = 0; i <= 300; i++) {
    ctx.beginPath()
    ctx.moveTo(100 + i, 100)
    ctx.lineTo(100 + i + 1, 100)
    ctx.lineWidth = 10
    // 红色是(255,0,0) 蓝色是(0,0,255) 
    // https://www.zxgj.cn/g/yansezhi 一个免费的在线查颜色工具
    ctx.strokeStyle = `rgb(${255 - i},0,${i})`
    ctx.stroke()
  }

​ 效果图:请添加图片描述

7.折线图的绘制(案例1)

​ 折线图的使用es6Class进行书写,此处只是个简易的模型,如需要更多自定义样式需要自己再封装下;比如坐标轴带刻度,点样式修改,显示点参数,颜色…
请添加图片描述

  const canvas = document.querySelector('#canvas')
  
  // 绘制参数
  const config = {
    axis: {
      x: 20,
      y: 20,
      width: 2,
      color: 'black'
    },
    points: [
      { x: 100, y: 100 },
      { x: 200, y: 150 },
      { x: 300, y: 100 },
      { x: 400, y: 200 },
      { x: 500, y: 170 }
    ]
  }

  class LineChart {
    constructor(el, config) {
      this.init(el)
      this.setAxis(this.ctx, config.axis)
      this.drawPoint(this.ctx, this.zero, config.points)
    }

    init(el) {
      this.ctx = el.getContext('2d')
      this.width = this.ctx.canvas.width
      this.height = this.ctx.canvas.height
    }
	
    // 绘制坐标轴
    setAxis(ctx, axis) {
      const zeroX = axis.x
      const zeroY = this.height - axis.y
      this.zero = {
        zeroX,
        zeroY
      }
      ctx.moveTo(zeroX, zeroY)
      ctx.lineTo(this.width - zeroX, zeroY)
      ctx.moveTo(zeroX, zeroY)
      ctx.lineTo(zeroX, axis.y)
      ctx.lineCap = 'round'
      ctx.lineWidth = axis.width
      ctx.strokeStyle = axis.color

      ctx.stroke()
      ctx.beginPath()
      ctx.moveTo(this.width - zeroX, zeroY)
      ctx.lineTo(this.width - zeroX - 10, zeroY + 5)
      ctx.lineTo(this.width - zeroX - 10, zeroY - 5)
      ctx.closePath()
      ctx.fill()
        
      ctx.beginPath()
      ctx.moveTo(axis.x, axis.y)
      ctx.lineTo(axis.x + 5, axis.y + 10)
      ctx.lineTo(axis.x - 5, axis.y + 10)
      ctx.closePath()
      ctx.fill()
    }

    // 绘制折现
    drawPoint(ctx, zero, points) {
      points.forEach(p => {
        ctx.beginPath()
        ctx.moveTo(p.x - 5, p.y - 5)
        ctx.lineTo(p.x + 5, p.y - 5)
        ctx.lineTo(p.x + 5, p.y + 5)
        ctx.lineTo(p.x - 5, p.y + 5)
        ctx.closePath()
        ctx.fill()
      });

      console.log(zero)
      ctx.beginPath()
      ctx.moveTo(zero.zeroX, zero.zeroY)
      points.forEach(p => {
        console.log(p)
        ctx.lineTo(p.x, p.y)
      })
      ctx.stroke()
    }
  }

  const temp = new LineChart(canvas, config)

8.矩形的绘制

​ 8.1 矩形

// x,y为原点坐标,width,height是矩形宽高

// 方法一:划线描边填充
ctx.rect(100, 100, 200, 200)
ctx.stroke()
ctx.fill()

// 方法二:直接描边矩形
ctx.strokeRect(100, 100, 200, 200)

// 方法三:直接填充矩形
ctx.fillRect(100, 100, 200, 200)

// 清楚矩形区域
ctx.clearRect(100, 100,50,50)

​ 8.2 矩形渐变(渐变方案)

// 获取渐变方案  (起始x,起始y,终点x,终点y)
const linearGradient = ctx.createLinearGradient(100, 100, 400, 100)
// 设置渐变方案 起始颜色为red从0%处开始渐变
linearGradient.addColorStop(0, 'red')
// 设置渐变方案 结束颜色为skyblue从100%处开始完成
linearGradient.addColorStop(1, 'skyblue')

// 将渐变方案赋值给填充样式
ctx.fillStyle = linearGradient
ctx.fillRect(100, 200, 300, 100)

// 将渐变方案赋值给描边(可以看出当前也已经支持线描边了)
ctx.moveTo(100, 400)
ctx.lineTo(400, 400)
ctx.lineWidth = 10
ctx.strokeStyle = linearGradient
ctx.stroke()

9.曲线绘制

​ 9.1 圆弧

// 圆心
const circleX = 100
const circleY = 100

// 半径
const radius = 50

// 开始弧度
const startArc = 0

// 结束弧度 计算公式 a=π/180*n   n为圆心角
const endArc = Math.PI / 180 * 60

// 绘制方向
const direction = false

ctx.arc(circleX, circleY, radius, startArc, endArc, direction)
ctx.stroke()

// 如果需要填充扇形区域
ctx.moveTo(circleX, circleY)
ctx.closePath()
ctx.arc(circleX, circleY, radius, startArc, endArc, direction)
ctx.fill()

​ 9.2 饼图
请添加图片描述

  const data = [80, 65, 98, 50, 60, 90, 55, 32]
  const total = 80 + 65 + 98 + 50 + 60 + 90 + 55 + 32
  let startArc = 0
  data.forEach(t => {
    ctx.beginPath()
    ctx.moveTo(circleX, circleY)
    const endArc = startArc + Math.PI * 2 * (t / total)
    ctx.arc(300, 300, 100, startArc, endArc, false)
    ctx.fillStyle = randomColor()
    startArc = endArc
    ctx.fill()
  })

  // 随机颜色
  function randomColor() {
    const r = Math.floor(Math.random() * 256)
    const g = Math.floor(Math.random() * 256)
    const b = Math.floor(Math.random() * 256)
    return `rgb(${r},${g},${b})`
  }

10.绘制文本

效果图:
请添加图片描述

  // 显示文本
  const text = 'i am so niu bi'

  // 字体直接写电脑里的字体名字
  ctx.font = '30px 楷书'

  // 可选 bottom/top/middle
  ctx.textBaseline = 'bottom'

  // 对齐方式 left/middle/right
  ctx.textAlign = 'left'

  // 字体颜色也接受渐变方案
  const liner = ctx.createLinearGradient(100, 100, 200, 100)
  liner.addColorStop(0, 'red')
  liner.addColorStop(1, 'blue')
  ctx.fillStyle = liner

  // ctx.strokeText(text, 100, 100)
  ctx.fillText(text, 100, 100)

11.绘制图片

​ 11.1 canvas也可以将js创建的img或者html创建的img元素内容绘制到canvas画布上,以创建元素举例

// 创建图形元素
const img = new Image()
img.src = 'meinv.jpeg'

// 由于img的src是一个异步状态,此处使用回调来绘制
img.onload = function () {
    // drawImage一共9个参数
    // 前五个分别是 图片源,绘制起点xy坐标,绘制图片缩放width/height
    // 后四个分别是截图用,截图起点xy坐标,截图宽高width/height
	ctx.drawImage(img, 100, 100, 300, 300, 100, 100, 100, 100)
}

END

就这些了,canvas还有其他功能比如状态,变形,合成,遮罩,和动画本文为涉及,如有需要请参考 做人要厚道2013 的这篇文章 https://blog.csdn.net/u012468376/article/details/73350998 里边写的很详细详细详细!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值