canvas入门,一篇博文带你学会用代码绘画,直击实战案例!

Posted 贪吃ღ大魔王

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了canvas入门,一篇博文带你学会用代码绘画,直击实战案例!相关的知识,希望对你有一定的参考价值。

canvas

简单说明

  1. canvas是html5的一个标签,代表一个画布,可以在上面进行绘画、动画等操作。画布默认大小是300*150。
  2. canvas标签本省只是画布,要实现上面有文字、线条等呈现,需要使用js实现。总之,画布上一切的呈现,都需要使用js来实现。canvas标签本质上就是一张图片,只是一张空白图片。
  3. 创建好的画布,要在上面进行绘画,需要使用js,用js操作canvas标签的 - canvas技术
  4. canvas标签,需要将画布调整大小的时候,加样式只是能改变视觉效果,真实的画布大小并没有改变,只是将画布的图片等比例放大了而已
  5. 调整画布大小,需要设置canvas标签的width属性和height属性
  6. canvas标签是一个双标签,在canvas标签中是不能放内容的,放了也没有用,只有当浏览器不支持canvas标签的时候,内部的内容才会显示
  7. 画布大小不能使用样式控制,用样式调整的是一个可视区域,其实真实的大小,还是一样的,只是在画布上画内容的话,会等比例放大。调整画布大小,需要在标签上直接添加width和height属性。
  8. canvas标签也是可以放文字的,只是当canvas标签不被浏览器支持的时候,会显示,例如ie8。

canvas的简单使用:

  1. 获取canvas元素

    var canvas = document.querySelector('canvas')
    
  2. 获取这个元素的工具箱 - 上下文

工具箱中包含了很多工具:直线工具、弧形工具、文本工具、矩形工具… 我们需要依赖这些工具进行绘画
canvas提供了一个工具箱 - canvas.getContext() - 获取工具箱

语法:

var ctx = canvas.getContext('2d')
canvas现在没有3d的,所有3d效果都是2d模拟出来的 - 要有一个固定的参数 '2d' - 默认返回工具箱

画直线

设置线条的宽度 - 工具箱.lineWidth = 值
值是不需要设置px单位的,默认是px,设置的是一个数字

ctx.lineWidth = 2

先将画笔放到画布上 - 工具箱.moveTo(x轴的坐标,y轴的坐标)

ctx.moveTo(100,100)

通过直线移动画笔 - 选择直线工具,并移动画笔到执行的位置 - 工具箱.lineTo(x轴的坐标,y轴的坐标)

ctx.lineTo(200,100)

默认不显示线,理解成他默认是白色的,需要让他显示出来
描边 - 工具箱.stroke() - 描边默认使用黑色,默认描边使用1px

ctx.stroke()

在这里插入图片描述

但是我们看到的线条,既不是1px的,也不是黑色的?
因为,canvas将线条,拉伸了,其实这个线就是2px的,拉伸以后,颜色就被稀释了,所以看起来没有那么黑
拉伸是因为浏览器不识别0.5的边框,设置2px的线条宽度,就不会被拉伸

所以我们在画布中设置线条的时候,尽量设置双数,不被拉伸,单数会被拉伸

基于这个问题,我们以后在绘图的时候,尽量将像素设置为双数。


使用

画多条线:

可以在一个画布中画多条线,但是直接将画笔以线的形式移动到一个位置的时候,是从前面的结束位置,到新的位置画了一条新的线,所以我们需要重新将画笔移动到第二条线的开始位置,然后再以线的形式移动画笔,再次描边。

但是在描第二条边的时候,会再次描第一条边,所以多次描边,前面的线的颜色会越来越深。

我们通常在绘制的时候,会等所有图案绘制结束之后,描一次边,不会描多次。

如果要设置描边的颜色,可以在描边之前设置描边的颜色:

ctx.strockStyle = '颜色值';

问题:如何给多条线设置不同的宽度和颜色?因为我们在最后描边的时候,发现所有线都是同一个颜色,哪怕给每条线都进行描边,也是一样的结果,说明每条线之间有很大的关联,canvas默认会从上下文上继承颜色和宽度。

我们需要在每次开始绘制的时候,告诉canvas下次的绘制是重新设置,跟上面的没有关系:

ctx.beginPath()

步骤:

  1. 获取canvas元素
var canvas = document.querySelector('canvas')
  1. 获取工具箱
var ctx = canvas.getContext('2d')
  1. 设置线条的宽度
ctx.lineWidth = 10
  1. 把画笔放到画布上
ctx.moveTo(100,100)
  1. 使用直线工具移动画笔
ctx.lineTo(200,100)
  1. 描边
ctx.stroke()
  1. 重新开启一个路径 - 工具箱.beginPath()
ctx.beginPath()
  1. 设置线条的宽度20
ctx.lineWidth = 20
  1. 再画一条线
ctx.moveTo(100,200)
ctx.lineTo(200,200)
ctx.stroke()

此时,上面的线比下面的线颜色要深,因为上面的线被描了两次边

效果:
在这里插入图片描述

画三角形:

三条线回到起点,就是三角形,但是线的结尾和开头部分,进行闭合的时候,其实是缺一个角的。

闭合:

  1. 手动闭合,再次画一次开始的线段

  2. 自动闭合 - 需要在描边之前闭合

    ctx.closePath() // 会自动将开始和结束的边进行闭合
    
  3. 依靠填充的方式闭合:

    ctx.fill() // 填充颜色默认是黑色
    

描边和填充可以一起使用,也可以单独使用,但是都有各自的规则:

描边会把填充的内容扩大

描边的颜色默认是黑色,使用ctx.strokeStyle来设置描边颜色

填充的颜色默认是黑色,使用ctx.fillStyle来设置填充颜色

步骤:

  1. 获取canvas元素
var canvas = document.querySelector('canvas')
  1. 获取工具箱
var ctx = canvas.getContext('2d')
  1. 设置一个线条宽度
ctx.lineWidth = 20
  1. 画三角形需要三条线
ctx.beginPath()
ctx.moveTo(100,100)
ctx.lineTo(200,100)
  1. 第二条
ctx.lineTo(200,200)
  1. 填充
ctx.fill()

效果:
在这里插入图片描述

画填充回形:

需求:

大正方形:200 * 200;小正方形:100 * 100;出现在画布的最中心;线宽2;

获取画布的宽度和高度:

ctx.canvas.width
ctx.canvas.height

通过线段描边,得到两个正方形,填充规则 - 非零填充规则:

在任何一个填充的区域,向外(到外围canvas的区域)拉一条线,数数经过了几个顺时针和逆时针进行计算:

顺时针+1,逆时针-1

最后得到的数字不是0,就填充;是0,就不填充

步骤:

  1. 获取canvas元素,获取工具箱
var canvas = document.querySelector('canvas')

var ctx = canvas.getContext('2d')
  1. 画大正方形
ctx.beginPath()
ctx.moveTo(50,50)
ctx.lineTo(250,50)
ctx.lineTo(250,250)
ctx.lineTo(50,250)
ctx.lineTo(50,50)
  1. 画小正方形
ctx.moveTo(100,100)
ctx.lineTo(100,200)
ctx.lineTo(200,200)
ctx.lineTo(200,100)
ctx.lineTo(100,100)
  1. 描边,填充
ctx.stroke()
 ctx.fill()

效果:
在这里插入图片描述
canvas进行填充的时候,有一个非零填充规则:

我们要填充的时候,可以给每一个要填充的区域开始向外拉一条线 - 计算
这条线一直到画布的最大范围
计算这条线经过的形状的边
每经过一个边,就计算这个边画的时候是使用顺时针画的,还是逆时针画的
最初始拿0计算,经过顺时针+1,经过逆时针的边-1
最后计算得出的结果是0或者不是0
如果不是0的区域就填充,如果是0的区域就不填充

线段两端的样式:

butt - 默认是没有 - 不会超出线段区域
round - 原型,会超出线段区域
square - 矩形形状,会超出线段区域

ctx.lineCap =;
// 获取canvas元素
    var canvas = document.querySelector('canvas')
    // 获取工具箱
    var ctx = canvas.getContext('2d')

    ctx.lineWidth = 20
    ctx.beginPath()
    ctx.moveTo(100, 50)
    ctx.lineTo(200, 50)

    // 线两端样式 - 工具箱.lineCap = 值
    // ctx.lineCap = 'butt'; // 默认值,表示没有帽子
    ctx.lineCap = 'round'; // 圆形的帽子,会加长线
    ctx.lineCap = 'square'; // 圆形的帽子,会加长线
    ctx.stroke()

圆形:
在这里插入图片描述
默认:
在这里插入图片描述

线段和线段交接处的样式:

miter - 默认
round - 圆弧形状
bevel - 平角形状

ctx.lineJoin =;
// 获取canvas元素
var canvas = document.querySelector('canvas')
// 获取工具箱
var ctx = canvas.getContext('2d')

ctx.lineWidth = 20
ctx.moveTo(50,30)
ctx.lineTo(150,100)
ctx.lineTo(250,30)

// ctx.lineJoin - 设置两条线连接的地方的样式
// ctx.lineJoin = 'miter' // 默认是两条线的角度的连接点
// ctx.lineJoin = 'round' // 两条线在连接的时候使用圆形连接
ctx.lineJoin = 'bevel' // 使用平角进行连接

ctx.stroke()

默认是两条线的角度的连接点:
在这里插入图片描述
两条线在连接的时候使用圆形连接:
在这里插入图片描述
使用平角进行连接:
在这里插入图片描述

画虚线:

需要在绘制线条之前,设置线条的样式为虚线:

数组 - 虚线方案,在数组中描述线条和空白的长度,然后不停的重复
两个值,第一个值是线条长度,第二个值是空白长度 - 重复
三个值,第一个值是线条长度,第二个值空白长度,第三个值是线条的长度;接下来是第二个值的空白长度,第二个值是线条的长度,第三个值是空白的长度 - 重复
四个值,第一个值是线条长度,第二个值是空白长度,第三个值是线条的长度,第四个值是空白长度 - 重复
ctx.setLineDash(参数);

获取虚线的方案:

ctx.getLineDash()
// 获取到的是一个数组,数组中记录了一段不重复的虚线方案

总结:
数组中有奇数个元素,那重复的个数就是 2*奇数个
数组中有偶数个元素,那重复的个数就是偶数个
步骤:

  1. 获取canvas元素,获取工具箱
var canvas = document.querySelector('canvas')
// 获取工具箱
var ctx = canvas.getContext('2d')
  1. 设置虚线方案
ctx.setLineDash([5,10,15])
  1. 4个值,实线、空白、实线、空白 - 重复
ctx.lineWidth = 10
ctx.moveTo(100,100)
ctx.lineTo(500,100)

ctx.stroke()

效果:
在这里插入图片描述

利用循环画纯色渐变的线条:

ctx.lineWidth = 10
for(var i=0;i<256;i++){
    ctx.beginPath()
    ctx.moveTo(100+i,100)
    ctx.lineTo(100+i+1,100)
    ctx.strokeStyle = `rgb(255,${255-i},${255-i})`
    ctx.closePath()
    ctx.stroke()
}

在这里插入图片描述

画折线图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1p8Atcoj-1623475077132)(media/1622994662580.png)]

html

<style>
    canvas{
        border:1px solid #000;
    }
</style>
<canvas width=600 height=300></canvas>

js

var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
// 画点的函数
function setPoint(pointData){
    ctx.beginPath()
    ctx.moveTo(pointData.x - 5,pointData.y - 5)
    ctx.lineTo(pointData.x - 5,pointData.y + 5)
    ctx.lineTo(pointData.x + 5,pointData.y + 5)
    ctx.lineTo(pointData.x + 5,pointData.y - 5)
    ctx.stroke()
    ctx.fill()
}
// 画多个点的函数
function setPoints(pointsData){
    pointsData.forEach(v=>{
        setPoint(v)
    })
}
var pointsData = [
    {
        x:20,
        y:280
    },
    {
        x:120,
        y:180
    },
    {
        x:220,
        y:160
    },
    {
        x:320,
        y:50
    },
    {
        x:420,
        y:105
    }
]
setPoints(pointsData)
// 画坐标轴
var space = 20;
var width = ctx.canvas.width
var height = ctx.canvas.height
// x轴
function setAxiosX(){
    var pointX = space;
    var pointY = height - space;
    var endX = space;
    var endY = space;
    ctx.beginPath()
    ctx.moveTo(pointX,pointY)
    ctx.lineTo(endX,endY)
    // 画箭头
    ctx.lineTo(endX-5,endY+10)
    ctx.lineTo(endX+5,endY+10)
    ctx.lineTo(endX,endY)
    ctx.stroke()
    ctx.fill()
}
setAxiosX()
// y轴
function setAxiosY(){
    var pointX = space;
    var pointY = height - space;
    var endX = width-space;
    var endY = height - space;
    ctx.beginPath()
    ctx.moveTo(pointX,pointY)
    ctx.lineTo(endX,endY)
    // 画箭头
    ctx.lineTo(endX-10,endY-5)
    ctx.lineTo(endX-10,endY+5)
    ctx.lineTo(endX,endY)
    ctx.stroke()
    ctx.fill()
}
setAxiosY()

// 点连线
function connectPoints(pointsData){
    for(var i=0;i<pointsData.length-1;i++){
        ctx.moveTo(pointsData[i].x,pointsData[i].y)
        ctx.lineTo(pointsData[i+1].x,pointsData[i+1].y)
        ctx.stroke()
    }
}
connectPoints(pointsData)

面向对象封装折线图:

class LineChart{
    constructor(classname,data){
        // 画布标签获取到
        this.canvas = document.querySelector('.'+classname)
        // 工具箱
        this.ctx = this.canvas.getContext('2d')
        // 定义画布大小
        this.canvasWidth = this.ctx.canvas.width
        this.canvasHeight = this.ctx.canvas.height
        // 定义坐标轴到画布边缘的间隙
        this.space = 20
        // 定义原点
        this.originX = 0
        this.originY = 0
        // 定义箭头三角形的高 和 点 的长度
        this.length = 10
        // 将数据绑定成类的属性
        this.data = data
        // 定义画布中的数据
        this.canvasData = null;
    }
    // 定义一个初始化方法
    init(){
        // 获取原点
        this.getOrigin()
        // 创建坐标轴 - x、y
        this.createAxisX()
        this.createAxisY()
        // 转换数据
        this.convert(this.data)
        // 描每个点
        // this.setPoint(data)
        // 描所有点
        this.setPoints(this.canvasData)
        // 连线
        this.connectLine(this.canvasData)
    }
    connectLine(data){
        for(let i=0;i<data.length-1;i++){
            this.ctx.beginPath()
            // 放画笔
            this.ctx.moveTo(data[i].x,data[i].y)
            // 画线
            this.ctx.lineTo(data[i+1].x,data[i+1].y)
            this.ctx.closePath()
            this.ctx.stroke()
        }
    }
    // 描所有点的方法
    setPoints(data){
        data.forEach(point=>{
            this.setPoint(point)
        })
    }
    // 描一个点的方法
    setPoint(data){
        this.ctx.beginPath()
        // 放画笔位置
        this.ctx.moveTo(data.x-this.length/2,data.y-this.length/2)
        // 画线
        this.ctx.lineTo(data.x+this.length/2,data.y-this.length/2)
        this.ctx.lineTo(data.x+this.length/2,data.y+this.length/2)
        this.ctx.一篇博文带你0基础 Html和css入门

一篇博文带你 jQuery入门,万字肝爆! 建议收藏~

一篇博文:带你30分钟入门ajax入门,直击实战操作

一篇博文:带你30分钟入门ajax入门,直击实战操作

一篇博文:带你30分钟入门ajax入门,直击实战操作

Python二次转码不用愁,一篇博文带你快速搞定!