记录一波n年前通过canvas实现的一些小demo
1.实现一个时钟
效果如下:
1.1 先实现一个基本的表盘
1.1.1 绘制表盘上的时刻数字
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="move.js"></script>
<style type="text/css" media="all">
div {
text-align: center;
margin-top: 250px;
position: absolute;
}
#clock {
border: 0px solid #ccc;
}
</style>
</head>
<body>
<div id='box'>
<canvas id="clock" height="200px" width="200px"></canvas>
</div>
</body>
<script>
const dom = document.getElementById('clock');
const ctx = dom.getContext('2d');
const width = ctx.canvas.width;
const height = ctx.canvas.height;
const r = width / 2;
// 绘制表盘
const drawBackGround = () => {
ctx.save();
ctx.translate(r, r);// translate() 方法重新映射画布上的 (0,0) 位置。
ctx.beginPath();
ctx.lineWidth = 6;
ctx.arc(0, 0, r - 3, 0, 2 * Math.PI);//先绘制一个圆圈
// 半径为r-lineWidth的一半
ctx.stroke();
ctx.font = '20px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const hourNum = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2];
//绘制每一个时刻对应的数字
hourNum.forEach(function (number, i) {
const rad = 2 * Math.PI / 12 * i;// 对应的弧度
const x = Math.cos(rad) * (r - 20);
const y = Math.sin(rad) * (r - 20);
ctx.fillText(number, x, y);
});
}
drawBackGround()
</script>
</html>
得到效果如下:
1.1.2 绘制表盘上的分对应小点
// 绘制表盘
const drawBackGround = () => {
ctx.save();
ctx.translate(r, r);// translate() 方法重新映射画布上的 (0,0) 位置。
ctx.beginPath();
ctx.lineWidth = 6;
//
ctx.arc(0, 0, r - 3, 0, 2 * Math.PI);
// 半径为r-lineWidth的一半
ctx.stroke();
ctx.font = '20px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const hourNum = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2];
hourNum.forEach(function (number, i) {
const rad = 2 * Math.PI / 12 * i;// 对应的弧度
const x = Math.cos(rad) * (r - 20);
const y = Math.sin(rad) * (r - 20);
ctx.fillText(number, x, y);
});
for (i = 0; i < 60; i++) {
const rad = 2 * Math.PI / 60 * i;
const x = Math.cos(rad) * (r - 10);
const y = Math.sin(rad) * (r - 10);
ctx.beginPath();
ctx.fillStyle = 'gray';
ctx.arc(x, y, 1, 0, 2 * Math.PI);
ctx.fill();
}
}
得到效果如下:
但是观察发现,整5分的时候圆点会比较突出,改写代码如下
// 绘制表盘
const drawBackGround = () => {
ctx.save();
ctx.translate(r, r);// translate() 方法重新映射画布上的 (0,0) 位置。
ctx.beginPath();
ctx.lineWidth = 6;
//
ctx.arc(0, 0, r - 3, 0, 2 * Math.PI);
// 半径为r-lineWidth的一半
ctx.stroke();
ctx.font = '20px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const hourNum = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2];
hourNum.forEach(function (number, i) {
const rad = 2 * Math.PI / 12 * i;// 对应的弧度
const x = Math.cos(rad) * (r - 20);
const y = Math.sin(rad) * (r - 20);
ctx.fillText(number, x, y);
});
for (i = 0; i < 60; i++) {
const rad = 2 * Math.PI / 60 * i;
const x = Math.cos(rad) * (r - 10);
const y = Math.sin(rad) * (r - 10);
ctx.beginPath();
if (i % 5 === 0) {
ctx.fillStyle = 'black';
ctx.arc(x, y, 2, 0, 2 * Math.PI);
}
else {
ctx.fillStyle = 'gray';
ctx.arc(x, y, 1, 0, 2 * Math.PI);
}
ctx.fill();
}
}
此时表盘比较像那么回事了
1.2 实现时针、分针、秒针
1.2.1 实现时针
因为分钟会影响小时的旋转度数,所以需要通过小时+分钟才能确定时针的位置
const drawHour = (hour, Minutes) => {
ctx.save();// 因为旋转会导致下一次还是处于旋转状态,所以保存当前状态然后在清楚状态
ctx.beginPath();
const rad = 2 * Math.PI / 12 * hour;
const mrad = 2 * Math.PI / 12 / 60 * Minutes;
ctx.rotate(rad + mrad);
ctx.lineCap = "round";
ctx.lineWidth = 5;
ctx.moveTo(0, 10);
ctx.lineTo(0, -r / 2);
ctx.stroke();
ctx.restore();
}
1.2.2 实现分针
const drawMinutes = (Minutes) => {
ctx.save();
ctx.beginPath();
const rad = 2 * Math.PI / 60 * Minutes;// 乘值不同小时12分钟60
ctx.rotate(rad);
ctx.lineWidth = 3;
ctx.moveTo(0, 10);
ctx.lineTo(0, -r + 30);
ctx.stroke();
ctx.restore();
}
1.2.3 实现秒针
const drawSeconds = (seconds) => {
ctx.save();
ctx.beginPath();
ctx.fillStyle = 'red';
const rad = 2 * Math.PI / 60 * seconds;// 乘值不同小时12分钟60
ctx.rotate(rad);
ctx.moveTo(-2, 20);
ctx.lineTo(2, 20);
ctx.lineTo(1, -r + 18);
ctx.lineTo(-1, -r + 18)
ctx.fill();
ctx.restore();
}
通过draw方法获取时间后绘制时针分针以及秒针
const draw = () => {
drawBackGround();
const date = new Date();
const h = date.getHours();
const m = date.getMinutes();
const s = date.getSeconds();
drawHour(h, m);
drawMinutes(m);
drawSeconds(s);
}
draw();
得到效果如下:
但是一般情况,中心会有一个白色圆点,添加drawArc方法:
const drawArc = () => {
ctx.beginPath();
ctx.fillStyle = 'white';
ctx.arc(0, 0, 5, 0, 2 * Math.PI);
ctx.fill();
}
此时的效果如下:
此时的时钟已经绘制完成,但是还不会动
1.3 让时钟跑起来
setInterval(draw, 1000);
其实只需要给时钟添加一个绘制的定时器就可以了,但是此时会存在一个问题:
需要恢复绘制的状态,所以需要在draw方法中添加ctx.restore(); 再次运行,发现还存在问题:
这是因为没有清楚之前绘制的状态,改写draw方法
const draw = () => {
ctx.clearRect(0, 0, width, height);//
drawBackGround();
const date = new Date();
const h = date.getHours();
const m = date.getMinutes();
const s = date.getSeconds();
drawHour(h, m);
drawMinutes(m);
drawSeconds(s);
drawArc();
ctx.restore();
}
draw();
再次运行,大功告成
2.实现简单得写字板功能
之前得业务场景,老师需要在线批阅学生的作业,并进行批注,所以使用canvans实现了此功能,简单得demo代码如下:
2.1 先实现一个简单得界面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, height=device-height,initial-scale=1.0,
maximum-scale=1.0,minimum-scale=1.0, user-scalable=0">
<script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
<style type="text/css">
.operate {
display: flex;
}
#canvas {
display: block;
border: 1px #ccc solid;
margin: 20px;
}
input,
#select,
#button,
#line {
width: 10%;
height: 40px;
margin-left: 16px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<div class="operate">
<input type="color" name="color" id="color">
<input type="button" name="button" id="button" value="清空">
<select id='select'>
<option>1</option>
<option>3</option>
<option>5</option>
<option>7</option>
<option>9</option>
<option>11</option>
<option>13</option>
</select>
<select id='line'>
<option value="solid">实线</option>
<option value="dashed">虚线</option>
</select>
</div>
<script>
</body>
</html>
如下:
2.2 实现 写字板功能
通过记录鼠标再画板区域移动得位置,时时执行位置moveTo到lineTo得过程即可
const x = 375;
const y = 500;
let isMove = false;
let oldPosition = { x: 0, y: 0 };
const canvasDom = document.getElementById('canvas');
const ctx = canvasDom.getContext('2d');
ctx.canvas.width = x;
ctx.canvas.height = y;
// 初始化基本操作
const initOperate = () => {
ctx.lineCap = 'round';
// 1.清空功能
$('#button').click(() => {
ctx.clearRect(0, 0, x, y);
});
$('#select').change(
() => {
ctx.lineWidth = $('#select').val();
});
$('#color').change(
() => {
ctx.lineWidth = $('s#elect').val();
const color = $('#color').val();
ctx.strokeStyle = color;
}
);
canvasDom.onmousedown = (e) => {
e.preventDefault();
isMove = true;
oldPosition = getPosition(e.clientX, e.clientY);
};
canvasDom.onmousemove = (e) => {
e.preventDefault();
const optionValue = $('#line').val()
drawLine(e,optionValue);
}
canvasDom.onmouseup = function (e) {
e.preventDefault();
isMove = false;
};
canvasDom.onmouseout = function (e) {
e.preventDefault();
isMove = false;
};
}
// 获取最新得位置
const getPosition = (x, y) => {
const json = canvasDom.getBoundingClientRect()
return { x: x - json.left, y: y - json.top }
}
// 绘画功能
const drawLine=(e,type)=>{
let newPosition = {}
if (isMove) {
newPosition = getPosition(e.clientX, e.clientY);
type==='dashed'&&ctx.setLineDash([3, 10]);//虚线绘制
ctx.beginPath();
ctx.moveTo(oldPosition.x, oldPosition.y);
ctx.lineTo(newPosition.x, newPosition.y);
ctx.stroke();
oldPosition = newPosition;
}
}
initOperate()
即可实现绘图的基本功能: