HTML5画布canvas实战(1)--折线图
画布基本教程
画布基础知识和绘制
Canvas MDN
唠唠叨叨
canvas 是 HTML5 新增的元素,可用于通过使用JavaScript中的脚本来绘制图形。例如,它可以用于绘制图形,制作照片,创建动画,甚至可以进行实时视频处理或渲染。
HTML结构
<style> canvas { border: 1px solid #000; } </style>
<canvas width="600" height="600"></canvas>
绘制网格线
绘制个普通网格线即可(想展示强大的画工也可以绘制个蜘蛛网出来)
// 获取画布,渲染上下文,绘制工具箱
let myCan = document.querySelector('canvas')
let cxt = myCan.getContext('2d')
// 设置网格的大小
let gridSize = 10
// 获取画布的宽高
let w = cxt.canvas.width
let h = cxt.canvas.height
// 计算出每一行能放多少网格线
let lineTotalX = Math.floor(w / gridSize)
let lineTotalY = Math.floor(h / gridSize)
// 先将X轴的线画出来(记得要去掉1px的不饱和填充)
for (var i = 0; i < lineTotalX;i++){
cxt.moveTo(i*gridSize - 0.5,0)
cxt.lineTo(i*gridSize - 0.5,h)
cxt.strokeStyle = '#eee'
cxt.stroke()
}
for (var i = 0; i < lineTotalY;i++){
cxt.moveTo(0,i*gridSize - 0.5)
cxt.lineTo(w,i*gridSize - 0.5)
cxt.strokeStyle = '#eee'
cxt.stroke()
}
绘制坐标
新建一个html文件,重新绘制
let myCan = document.querySelector('canvas')
let cxt = myCan.getContext('2d')
// 获取画布的宽高
let w = cxt.canvas.width
let h = cxt.canvas.height
// 设置坐标系原点的空间
let space = 20
// 初始化三角形的长度(高)
let arrowSize = 10
// 设置原点坐标x,y
let x = space
let y = h - space
// 绘制X轴
cxt.beginPath()
cxt.moveTo(x,y)
cxt.lineTo(w - space,y)
// 绘制三角形
cxt.lineTo(w - space - arrowSize,y + arrowSize/2)
cxt.lineTo(w - space - arrowSize,y - arrowSize/2)
cxt.lineTo(w - space,y)
cxt.fill()
cxt.stroke()
// 绘制y轴
cxt.beginPath()
cxt.moveTo(x,y)
cxt.lineTo(x,space)
// 绘制三角形
cxt.lineTo(x-arrowSize/2,space+arrowSize)
cxt.lineTo(x+arrowSize/2,space+arrowSize)
cxt.lineTo(x,space)
cxt.fill()
cxt.stroke()
绘制点
var myCan = document.querySelector('canvas')
var cxt = myCan.getContext('2d')
let coordinate = {
x:100,
y:100
}
// 绘制一个大小为10的正方形
let dottedSize = 10
cxt.beginPath()
cxt.moveTo(coordinate.x - dottedSize/2,coordinate.y - dottedSize/2)
cxt.lineTo(coordinate.x + dottedSize/2,coordinate.y - dottedSize/2)
cxt.lineTo(coordinate.x + dottedSize/2,coordinate.y + dottedSize/2)
cxt.lineTo(coordinate.x - dottedSize/2,coordinate.y + dottedSize/2)
cxt.closePath()
cxt.fill()
构造函数实现折线图
一大波代码即将来袭…一步一步重新实现折线图(根据上面的步骤来初始化我们的变量)…接下来要运用我的不锈钢脑子思考…写下我的思路
初始化一些必要的变量(设置网格的大小、坐标系原点、点的大小,获取画布宽高…),为了方便我把思路写在代码注释里面
function LineChart(cxt) {
this.canvas = document.querySelector('canvas')
// 1.1 获取渲染上下文,绘制工具箱
this.cxt = cxt || this.canvas.getContext('2d')
// 1.2 设置网格的大小
this.gridSize = 10
// 1.3 设置网格坐标系的原点距离
this.space = 20
// 1.4 设置网格的坐标系的箭头长度
this.arrowSize = 10
// 1.5 设置点的大小
this.dottedSize = 6
// 1.6 获取画布的宽高
this.w = this.cxt.canvas.width
this.h = this.cxt.canvas.height
// 1.7 设置原点x,y坐标
this.x = this.space
this.y = this.h - this.space
}
使用构造函数的原型来实现网格,坐标系,绘制点
实现网格,前面定义了网格的距离gridSize,根据画布的宽高来画网格即可(注意加上0.5,否则线会看起来模糊,是因为画布的对齐点是1px绘制线的中线,计算机会帮我们延展,填充更浅的颜色)
LineChart.prototype.gridding = function () {
// 3.1 使用for循环先绘制竖线
for (var i = 1;i<this.w/this.gridSize;i++){
this.cxt.beginPath()
this.cxt.moveTo(this.gridSize*i + 0.5,0)
this.cxt.lineTo(this.gridSize*i + 0.5,this.h)
// 因为线会有1px不饱和显示,因为中线是基于对齐点对齐,所以+0.5;可以看上面的参考文档
this.cxt.strokeStyle = '#eee'
this.cxt.stroke()
}
// 3.2 画横线
for (var i = 1;i<this.h/this.gridSize;i++){
this.cxt.beginPath()
this.cxt.moveTo(0,this.gridSize*i + 0.5)
this.cxt.lineTo(this.w,this.gridSize*i + 0.5)
this.cxt.strokeStyle = '#eee'
this.cxt.stroke()
}
}
测试
let lineChart = new LineChart()
lineChart.gridding()
绘制x,y轴
LineChart.prototype.coordinate = function(){
// 绘制x轴的坐标系
this.cxt.beginPath()
this.cxt.moveTo(this.x,this.y + 0.5)
this.cxt.lineTo(this.w - this.space,this.y + 0.5)
// 绘制x轴的三角形
this.cxt.lineTo(this.w -this.space - this.arrowSize,this.y + this.arrowSize/2)
this.cxt.lineTo(this.w -this.space - this.arrowSize,this.y - this.arrowSize/2)
this.cxt.lineTo(this.w - this.space,this.y)
// 绘制y轴的坐标系
// this.cxt.beginPath()
this.cxt.moveTo(this.x+0.5,this.y)
this.cxt.lineTo(this.x+0.5,this.space)
this.cxt.lineTo(this.x+this.arrowSize/2,this.space+this.arrowSize)
this.cxt.lineTo(this.x-this.arrowSize/2,this.space+this.arrowSize)
this.cxt.lineTo(this.x+0.5,this.space)
this.cxt.fill()
this.cxt.strokeStyle = '#000'
this.cxt.stroke()
}
测试
lineChart.coordinate()
初始化一个点,到后面调用即可(其实就是一个边长为dottedSize,中心为坐标的正方形)
LineChart.prototype.drawDotted = function(x,y){
this.cxt.beginPath()
this.cxt.moveTo(x-this.dottedSize/2,y-this.dottedSize/2)
this.cxt.lineTo(x+this.dottedSize/2,y-this.dottedSize/2)
this.cxt.lineTo(x+this.dottedSize/2,y+this.dottedSize/2)
this.cxt.lineTo(x-this.dottedSize/2,y+this.dottedSize/2)
this.cxt.closePath()
this.cxt.fill()
}
绘制点(因为画布原来的坐标原点是左上角,现在我们的原点是我们的坐标系的原点,所以需要换算完在绘制点)
LineChart.prototype.line = function(data){
if(!data.length){
return
}
data.forEach(function (item,i) {
item.x += this.x
item.y = this.y - item.y
this.drawDotted(item.x,item.y)
}.bind(this))//使得this指向我们的构造函数
}
测试
lineChart.line([{ x:111,y:80}])
根据点画折线
LineChart.prototype.ligature = function (data) {
console.log(data)
for (var i = 1;i<data.length;i++){
this.cxt.beginPath()
this.cxt.moveTo(data[i-1].x,data[i-1].y)
this.cxt.lineTo(data[i].x,data[i].y)
this.cxt.stroke()
}
}
总结
- 后面有整体的代码
- 将所有的量放在构造函数里面,方便我们维护,如果需要修改的在里面一改即可
- 我们有时候需要对数据进行冒泡排序,才能使得折线从左往右排序
- 重新绘制时需要使用beginPath(),这样才不会让线粗细
其实文章基本都是画线,主要是与画布坐标换算的问题
function LineChart(cxt) {
this.canvas = document.querySelector('canvas')
this.cxt = cxt || this.canvas.getContext('2d')
this.gridSize = 10
this.space = 20
this.arrowSize = 10
this.dottedSize = 6
this.w = this.cxt.canvas.width
this.h = this.cxt.canvas.height
this.x = this.space
this.y = this.h - this.space
}
LineChart.prototype.gridding = function () {
for (var i = 1;i<this.w/this.gridSize;i++){
this.cxt.beginPath()
this.cxt.moveTo(this.gridSize*i + 0.5,0)
this.cxt.lineTo(this.gridSize*i + 0.5,this.h)
this.cxt.strokeStyle = '#eee'
this.cxt.stroke()
}
for (var i = 1;i<this.h/this.gridSize;i++){
this.cxt.beginPath()
this.cxt.moveTo(0,this.gridSize*i + 0.5)
this.cxt.lineTo(this.w,this.gridSize*i + 0.5)
this.cxt.strokeStyle = '#eee'
this.cxt.stroke()
}
}
LineChart.prototype.coordinate = function(){
this.cxt.beginPath()
this.cxt.moveTo(this.x,this.y + 0.5)
this.cxt.lineTo(this.w - this.space,this.y + 0.5)
this.cxt.lineTo(this.w -this.space - this.arrowSize,this.y + this.arrowSize/2)
this.cxt.lineTo(this.w -this.space - this.arrowSize,this.y - this.arrowSize/2)
this.cxt.lineTo(this.w - this.space,this.y)
this.cxt.moveTo(this.x+0.5,this.y)
this.cxt.lineTo(this.x+0.5,this.space)
this.cxt.lineTo(this.x+this.arrowSize/2,this.space+this.arrowSize)
this.cxt.lineTo(this.x-this.arrowSize/2,this.space+this.arrowSize)
this.cxt.lineTo(this.x+0.5,this.space)
this.cxt.fill()
this.cxt.strokeStyle = '#000'
this.cxt.stroke()
}
// 绘制点
LineChart.prototype.drawDotted = function(x,y){
this.cxt.beginPath()
this.cxt.moveTo(x-this.dottedSize/2,y-this.dottedSize/2)
this.cxt.lineTo(x+this.dottedSize/2,y-this.dottedSize/2)
this.cxt.lineTo(x+this.dottedSize/2,y+this.dottedSize/2)
this.cxt.lineTo(x-this.dottedSize/2,y+this.dottedSize/2)
this.cxt.closePath()
this.cxt.fill()
}
LineChart.prototype.line = function(data){
if(!data.length){
return
}
data.forEach(function (item,i) {
item.x += this.x
item.y = this.y - item.y
this.drawDotted(item.x,item.y)
}.bind(this))
}
LineChart.prototype.ligature = function (data) {
console.log(data)
for (var i = 1;i<data.length;i++){
this.cxt.beginPath()
this.cxt.moveTo(data[i-1].x,data[i-1].y)
this.cxt.lineTo(data[i].x,data[i].y)
this.cxt.stroke()
}
}
LineChart.prototype.init = function (data) {
this.gridding()
this.coordinate()
this.line(data)
this.ligature(data)
}
var analogData = [
{
x: 50,
y: 120
},
{
x: 70,
y: 160
},
{
x: 90,
y: 240
},
{
x: 110,
y: 20
},
{
x: 300,
y: 80
}
];
let lineChart = new LineChart()
lineChart.init(analogData)
还没有评论,来说两句吧...