针对 HTML5 Canvas开发详解(基础一)的基础知识,写了一些实战案例,本节的案例代码是写在vue搭建的项目中的,引用了element-ui的按钮组件,如果大家要运行本节代码,以组件的形式引到自有vue项目的某个父组件即可。
案例效果:
案例代码:
my-canvas.vue
<template>
<div class="container">
<div class="opt-btn">
<el-button type="primary" size="mini" v-for="(item, index) in optBtnData" :key="index" @click="item.clickBtnFunc">{{item.text}}</el-button>
</div>
<canvas ref="myCanvas" class="my-canvas" width="300" height="300"></canvas>
</div>
</template>
<script>
export default {
data(){
return {
timer: null,//定时器
cnvObj: null,//canvas对象
cxtObj: null,//上下文环境对象
optBtnData: [// 操作按钮数据
{text: '绘制箭头', clickBtnFunc: () => {this.drawArrows(this.arrowsData, this.cxtObj)}},
{text: '绘制正多边形', clickBtnFunc: () => {this.drawEqPolygon(3)}},
{text: '绘制多角星', clickBtnFunc: () => {this.drawPolyStar(5)}},
{text: '绘制方格调色板', clickBtnFunc: () => {this.drawCheckPallet()}},
{text: '绘制渐变调色板', clickBtnFunc: () => {this.drawGradualChangePallet()}},
{text: '绘制圆角矩形', clickBtnFunc: () => {this.drawCircleAngleRectangle()}},
{text: '绘制气泡', clickBtnFunc: () => {this.drawBubble(this.cxtObj)}},
{text: '绘制心形', clickBtnFunc: () => {this.drawHeart(this.cxtObj)}},
{text: '绘制N叶草', clickBtnFunc: () => {this.drawLeaf(4)}},
{text: '绘制扇形', clickBtnFunc: () => {this.drawSector()}}
],
arrowsData: {//绘制箭头数据
moveToObj: {x: 40, y: 60},
lineToArr: [
{x: 100, y: 60}, {x: 100, y: 30}, {x: 150, y: 75},
{x: 100, y: 120}, {x: 100, y: 90}, {x: 40, y: 90}
]
},
bubbleData: {//绘制气泡数据
moveToObj: {x: 75, y: 25},
quadCurveToArr: [
{cx: 25, cy: 25, x: 25, y: 62},
{cx: 25, cy: 100, x: 50, y: 100},
{cx: 50, cy: 120, x: 30, y: 125},
{cx: 60, cy: 120, x: 65, y: 100},
{cx: 125, cy: 100, x: 125, y: 62},
{cx: 125, cy: 25, x: 75, y: 25}
]
},
heartData: {//绘制心形数据
moveToObj: {x: 75, y: 40},
bezierCurveToArr: [
{cx1: 75, cy1: 37, cx2: 70, cy2: 25, x: 50, y: 25},
{cx1: 20, cy1: 25, cx2: 20, cy2: 62.5, x: 20, y: 62.5},
{cx1: 20, cy1: 80, cx2: 40, cy2: 102, x: 75, y: 120},
{cx1: 110, cy1: 102, cx2: 130, cy2: 80, x: 130, y: 62.5},
{cx1: 130, cy1: 62.5, cx2: 130, cy2: 25, x: 100, y: 25},
{cx1: 85, cy1: 25, cx2: 75, cy2: 37, x: 75, y: 40}
]
}
}
},
mounted(){
this.initData();
},
methods: {
//初始化数据
initData(){
this.cnvObj = this.$refs.myCanvas;
this.cxtObj = this.cnvObj.getContext('2d');
},
//清空画布
clearCanvas(){
this.cxtObj.clearRect(0, 0, this.cnvObj.width, this.cnvObj.height);
this.cxtObj.beginPath();
},
//清除定时器
clearTimer(){
clearInterval(this.timer);
},
//绘制箭头
drawArrows(arrowsData, cxt){
this.clearTimer();
this.clearCanvas();
let moveToObj = arrowsData.moveToObj;
let lineToArr = arrowsData.lineToArr;
this.cxtObj.moveTo(moveToObj.x, moveToObj.y);
lineToArr.forEach(val => cxt.lineTo(val.x, val.y));
cxt.closePath();
cxt.stroke();
},
/*
* 绘制正多边形
* n:表示n边
* dx、dy:表示n边形中心坐标
* l:表示边长
* */
drawEqPolygon(n){
this.clearTimer();
this.timer = setInterval(() => {
this.clearCanvas();
this._drawEqPolygon(this.cxtObj, n++, this.cnvObj.width/2, this.cnvObj.height/2, 60);
if(n > 10){
this.clearTimer();
}
}, 500);
},
_drawEqPolygon(cxt, n, dx, dy, l){
let degree = (2 * Math.PI) / n;
for(let i = 0; i < n; i++){
let x = Math.cos(i * degree);
let y = Math.sin(i * degree);
cxt.lineTo(x * l + dx, y * l + dy);
}
cxt.closePath();
cxt.stroke();
},
/*
* 绘制多角星
* n:表示n角
* dx、dy:表示n角星中心坐标
* r1、 r2 表示内半径和外半径
* */
drawPolyStar(n){
this.clearTimer();
this.timer = setInterval(() => {
this.clearCanvas();
this._drawPolyStar(this.cxtObj, n++, this.cnvObj.width/2, this.cnvObj.height/2, 15, 50);
if(n > 10){
clearInterval(this.timer)
}
}, 500);
},
_drawPolyStar(cxt, n, dx, dy, r1, r2){
for(let i = 0; i < n; i++){
let p1 = (r1 + i * 360 / n) * Math.PI / 180;
let p2 = (r2 + i * 360 / n) * Math.PI / 180;
cxt.lineTo(Math.cos(p1) * r1 + dx, -Math.sin(p1) * r1 + dy);
cxt.lineTo(Math.cos(p2) * r2 + dx, -Math.sin(p2) * r2 + dy);
}
cxt.closePath();
cxt.stroke();
},
/*
* 绘制方格调色板
* n:表示n行n列
* blue:表示b的值,0-255
* w:表示每格的宽度
* */
drawCheckPallet(){
this.clearTimer();
this.clearCanvas();
this._drawCheckPallet(this.cxtObj, 60, 120, 5);
},
_drawCheckPallet(cxt, n, blue, w){
for(let i = 0; i < n; i++){
for(let j = 0; j < n; j++){
let r = Math.floor(255 - 255 / n * i);
let g = Math.floor(255 - 255 / n * j);
let b = Math.floor(blue);
cxt.fillStyle = `rgb(${r}, ${g}, ${b})`;
cxt.fillRect(j * w, i * w, w, w);
}
}
},
//绘制渐变调色板
drawGradualChangePallet(){
this.clearTimer();
this.clearCanvas();
this._drawGradualChangePallet(this.cnvObj, this.cxtObj, 160, 3, 10);
},
_drawGradualChangePallet(cnv, cxt, n, w, step){
let r = 255, g = 0, b = 0;
let n_8 = n / 8;
let n_4 = n / 4;
let n_2 = n / 2;
for(let i = 0; i < n; i++){
if(i < n_8){
g += step;
} else if(i > n_8 && i < n_4){
r -= step;
} else if(i > n_4 && i < n_2){
g -= step;
b += step;
} else if(i >= n_2 && i < n){
r += step;
}else {
b -= step;
}
cxt.fillStyle = `rgb(${r}, ${g}, ${b})`;
cxt.fillRect(w * i, 0, w, cnv.height);
}
},
/*
* 绘制圆角矩形
* width、height:分别表示宽和高
* r:表示圆角半径
* offsetX、 offsetY:分别表示左上角顶点坐标
* */
drawCircleAngleRectangle(){
this.clearTimer();
this.clearCanvas();
this._drawCircleAngleRectangle(this.cxtObj, 100, 100, 20, 20, 20);
},
_drawCircleAngleRectangle(cxt, width, height, r, offsetX, offsetY){
cxt.moveTo(offsetX + r, offsetY);
cxt.lineTo(offsetX + width - r, offsetY);
cxt.arcTo(offsetX + width, offsetY, offsetY + width, offsetY + r, r);
cxt.lineTo(offsetX + width, offsetY + height - r);
cxt.arcTo(offsetX + width, offsetY + height, offsetX + width -r, offsetY + height, r);
cxt.lineTo(offsetX + r, offsetY + height);
cxt.arcTo(offsetX, offsetY + height, offsetX, offsetY + height - r, r);
cxt.lineTo(offsetX, offsetY + r);
cxt.arcTo(offsetX, offsetY, offsetX + r, offsetY, r);
cxt.closePath();
cxt.stroke();
},
//绘制气泡
drawBubble(cxt){
this.clearTimer();
this.clearCanvas();
let moveToObj = this.bubbleData.moveToObj;
let quadCurveToArr = this.bubbleData.quadCurveToArr;
cxt.moveTo(moveToObj.x, moveToObj.y);
quadCurveToArr.forEach(val => cxt.quadraticCurveTo(val.cx, val.cy, val.x, val.y))
cxt.stroke();
},
//绘制心形
drawHeart(cxt){
this.clearTimer();
this.clearCanvas();
let moveToObj = this.heartData.moveToObj;
let bezierCurveToArr = this.heartData.bezierCurveToArr;
cxt.moveTo(moveToObj.x, moveToObj.y);
bezierCurveToArr.forEach(val => cxt.bezierCurveTo(val.cx1, val.cy1, val.cx2, val.cy2, val.x, val.y));
cxt.fillStyle = 'red';
cxt.fill();
},
/*
* 绘制N叶草
* n:表示n片叶子
* dx、dy:表示叶子中心位置的坐标
* size:表示叶子的大小
* length:表示叶子的长度
* */
drawLeaf(n){
this.clearTimer();
this.timer = setInterval(() => {
this.clearCanvas();
this._drawLeaf(this.cxtObj, n++, this.cnvObj.width/2, this.cnvObj.height/2, 20, 80);
if(n > 10){
clearInterval(this.timer)
}
}, 500);
},
_drawLeaf(cxt, n, dx, dy, size, length){
cxt.moveTo(dx, dy + size);
let degree = 2 * Math.PI / n;
for(let i = 1; i < n + 1; i++){
//计算控制点坐标
let cx1 = Math.sin((i - 1) * degree) * length + dx;
let cy1 = Math.cos((i - 1) * degree) * length + dy;
let cx2 = Math.sin(i * degree) * length + dx;
let cy2 = Math.cos(i * degree) * length + dy;
//计算结束点的坐标
let x = Math.sin(i * degree) * size + dx;
let y = Math.cos(i * degree) * size + dy;
cxt.bezierCurveTo(cx1, cy1, cx2, cy2, x, y);
}
cxt.closePath();
cxt.fillStyle = '#0f9';
cxt.fill();
},
//绘制扇形
drawSector(){
this.clearTimer();
this.clearCanvas();
this._drawSector(this.cxtObj, this.cnvObj.width/2, this.cnvObj.height/2, 50, 30, 120);
},
_drawSector(cxt, x, y, r, angle1, angle2){
cxt.moveTo(x, y);
cxt.arc(x, y, r, angle1 * Math.PI/180, angle2 * Math.PI/180, true);
cxt.closePath();
cxt.stroke();
}
}
}
</script>
<style scoped>
.opt-btn {
width: 400px;
}
.el-button {
margin: 0 0 10px 10px;
}
.my-canvas {
border: 1px dashed #ccc;
}
</style>