手写 Canvas 画板
大约 5 分钟
本文简单手写一个 Canvas 画板,效果如下:
功能点:
- 更改画笔颜色;
- 更改画笔大小;
- 清空画布。
理清思路
- 获取画布元素;
- 获取画笔颜色、画笔大小;
- 获取画布上下文;
- 监听鼠标按下事件;
- 监听鼠标移动事件;
- 监听鼠标抬起事件;
- 鼠标按下时,记录鼠标按下时的坐标;
- 鼠标移动时,根据鼠标按下时的坐标和移动时的坐标,绘制出一条线段;
- 鼠标抬起时,结束绘制。
代码实现
绘制 UI
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Canvas 画板</title>
<style>
body {
margin: 0;
}
.container {
width: 1024px;
height: 600px;
margin: 50px auto 0;
box-shadow: 0 0 10px #000;
border-radius: 10px;
overflow: hidden;
cursor: crosshair;
}
/* 工具栏 */
.tool-bar {
width: 1024px;
height: 80px;
margin: 18px auto;
border-radius: 5px;
display: flex;
align-items: center;
/* background-color: #8b8888; */
}
.tool-bar div {
width: 78px;
height: 78px;
border: 1px solid #b3b2b2;
margin: 0 2px;
border-radius: 50%;
box-sizing: border-box;
cursor: pointer;
}
.tool-bar .black {
background-color: #000;
}
.tool-bar .red {
background-color: #f00;
}
.tool-bar .green {
background-color: #0f0;
}
.tool-bar .blue {
background-color: #00f;
}
.tool-bar .line {
display: flex;
justify-content: center;
align-items: center;
}
.tool-bar .line span {
display: block;
border-radius: 50%;
background-color: #000000;
}
.tool-bar .line-4 span {
width: 4px;
height: 4px;
}
.tool-bar .line-8 span {
width: 8px;
height: 8px;
}
.tool-bar .line-12 span {
width: 12px;
height: 12px;
}
.tool-bar .remove {
text-align: center;
line-height: 78px;
font-size: 40;
}
</style>
</head>
<body>
<!-- 画板主体 -->
<div class="container">
<canvas id="myCanvas" class="canvas" width="1024" height="600" />
</div>
<!-- 工具栏 -->
<div class="tool-bar">
<div class="color black" data-color="black"></div>
<div class="color red" data-color="red"></div>
<div class="color green" data-color="green"></div>
<div class="color blue" data-color="blue"></div>
<div class="line line-4" data-line="4">
<span></span>
</div>
<div class="line line-8" data-line="8">
<span></span>
</div>
<div class="line line-12" data-line="12">
<span></span>
</div>
<div class="remove">擦除</div>
</div>
</body>
</html>
编写插件事件
在写插件时,分为俩个步骤:
- 初始化插件;
- 绑定监听事件;
初始化插件
初始化插件,包含初始化数据和定义所需要的监听事件。
监听事件包含,点击事件、移动事件,抬起事件。而事件监听都放在 bindEvent 函数中。
;(function () {
// 获取元素
var can = document.getElementById('myCanvas')
var tools = document.getElementsByClassName('tool-bar')[0]
// 初始化数据
var ctx = can.getContext('2d')
var cWidth = can.width
var cHeight = can.height
var color = 'block'
var lineWidth = 4
var x = 0
var y = 0
/** 定义初始化函数 */
var init = function () {
bindEvent()
}
/** 绑定事件 */
function bindEvent() {}
/** 鼠标点击 */
function mouseDown(e) {}
/** 移动鼠标 */
function mouseMove(e) {}
/** 鼠标抬起 */
function mouseUp(e) {}
/** 点击工具栏 */
function clickTool(e) {}
init()
})()
完善监听事件
思路为:
- 获取 Canvas 上下文;
- 调用 beginPath 开始新的绘制;
- 调用 moveTo 将画笔移动到鼠标位置;
- 持续监听鼠标移动位置, 并调用 lineTo 绘制线条;
- 调用 stroke 完成绘制,并在最后鼠标抬起时,清空监听事件。
;(function () {
var can = document.getElementById('myCanvas')
var tools = document.getElementsByClassName('tool-bar')[0]
var ctx = can.getContext('2d')
var cWidth = can.width
var cHeight = can.height
var color = 'block'
var lineWidth = 4
var x = 0
var y = 0
var init = function () {
bindEvent()
}
/** 绑定事件 */
function bindEvent() {
// 画布事件
can.addEventListener(
'mousedown',
function (e) {
mouseDown(e)
document.addEventListener('mousemove', mouseMove, false)
document.addEventListener('mouseup', mouseUp, false)
},
false
)
// 工具栏事件
tools.addEventListener('click', clickTool, false)
}
/** 鼠标点击 */
function mouseDown(e) {
setCanXY(e)
ctx.beginPath() // 开始新的绘制,避免干扰之前已有的绘图
ctx.strokeStyle = color
ctx.lineWidth = lineWidth
ctx.lineCap = 'round'
ctx.lineJoin = 'round'
ctx.moveTo(x, y)
}
/** 移动鼠标 */
function mouseMove(e) {
setCanXY(e)
ctx.lineTo(x, y)
ctx.stroke()
}
/** 鼠标抬起 */
function mouseUp(e) {
document.removeEventListener('mousemove', mouseMove, false)
document.removeEventListener('mouseup', mouseUp, false)
}
/** 设置落笔位置 */
function setCanXY(e) {
var e = e || window.event
var xPos = e.clientX - can.offsetLeft
var yPos = e.clientY - can.offsetTop
// 边界限制
x = xPos < 0 ? 0 : xPos > cWidth ? cWidth : xPos
y = yPos < 0 ? 0 : yPos > cHeight ? cHeight : yPos
}
/** 点击工具栏 */
function clickTool(e) {
var e = e || window.event
var tar = e.target || e.srcElement
// 检查是否点击了颜色工具
if (tar.classList.contains('color')) {
color = tar.getAttribute('data-color')
}
// 检查是否点击了线宽工具
if (tar.classList.contains('line')) {
lineWidth = tar.getAttribute('data-line')
}
// 检查是否点击了擦除工具
if (tar.classList.contains('remove')) {
ctx.clearRect(0, 0, cWidth, cHeight)
}
}
init()
})()
完整插件代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Canvas 画板</title>
<style>
body {
margin: 0;
}
.container {
width: 1024px;
height: 600px;
margin: 50px auto 0;
box-shadow: 0 0 10px #000;
border-radius: 10px;
overflow: hidden;
cursor: crosshair;
}
/* 工具栏 */
.tool-bar {
width: 1024px;
height: 80px;
margin: 18px auto;
border-radius: 5px;
display: flex;
align-items: center;
/* background-color: #8b8888; */
}
.tool-bar div {
width: 78px;
height: 78px;
border: 1px solid #b3b2b2;
margin: 0 2px;
border-radius: 50%;
box-sizing: border-box;
cursor: pointer;
}
.tool-bar .black {
background-color: #000;
}
.tool-bar .red {
background-color: #f00;
}
.tool-bar .green {
background-color: #0f0;
}
.tool-bar .blue {
background-color: #00f;
}
.tool-bar .line {
display: flex;
justify-content: center;
align-items: center;
}
.tool-bar .line span {
display: block;
border-radius: 50%;
background-color: #000000;
}
.tool-bar .line-4 span {
width: 4px;
height: 4px;
}
.tool-bar .line-8 span {
width: 8px;
height: 8px;
}
.tool-bar .line-12 span {
width: 12px;
height: 12px;
}
.tool-bar .remove {
text-align: center;
line-height: 78px;
font-size: 40;
}
</style>
</head>
<body>
<!-- 画板主体 -->
<div class="container">
<canvas id="myCanvas" class="canvas" width="1024" height="600" />
</div>
<!-- 工具栏 -->
<div class="tool-bar">
<div class="color black" data-color="black"></div>
<div class="color red" data-color="red"></div>
<div class="color green" data-color="green"></div>
<div class="color blue" data-color="blue"></div>
<div class="line line-4" data-line="4">
<span></span>
</div>
<div class="line line-8" data-line="8">
<span></span>
</div>
<div class="line line-12" data-line="12">
<span></span>
</div>
<div class="remove">擦除</div>
</div>
<script>
;(function () {
var can = document.getElementById('myCanvas')
var tools = document.getElementsByClassName('tool-bar')[0]
var ctx = can.getContext('2d')
var cWidth = can.width
var cHeight = can.height
var color = 'block'
var lineWidth = 4
var x = 0
var y = 0
var init = function () {
bindEvent()
}
/** 绑定事件 */
function bindEvent() {
// 画布事件
can.addEventListener(
'mousedown',
function (e) {
mouseDown(e)
document.addEventListener('mousemove', mouseMove, false)
document.addEventListener('mouseup', mouseUp, false)
},
false
)
// 工具栏事件
tools.addEventListener('click', clickTool, false)
}
/** 鼠标点击 */
function mouseDown(e) {
setCanXY(e)
ctx.beginPath() // 开始新的绘制,避免干扰之前已有的绘图
ctx.strokeStyle = color
ctx.lineWidth = lineWidth
ctx.lineCap = 'round'
ctx.lineJoin = 'round'
ctx.moveTo(x, y)
}
/** 移动鼠标 */
function mouseMove(e) {
setCanXY(e)
ctx.lineTo(x, y)
ctx.stroke()
}
/** 鼠标抬起 */
function mouseUp(e) {
document.removeEventListener('mousemove', mouseMove, false)
document.removeEventListener('mouseup', mouseUp, false)
}
/** 设置落笔位置 */
function setCanXY(e) {
var e = e || window.event
var xPos = e.clientX - can.offsetLeft
var yPos = e.clientY - can.offsetTop
// 边界限制
x = xPos < 0 ? 0 : xPos > cWidth ? cWidth : xPos
y = yPos < 0 ? 0 : yPos > cHeight ? cHeight : yPos
}
/** 点击工具栏 */
function clickTool(e) {
var e = e || window.event
var tar = e.target || e.srcElement
// 检查是否点击了颜色工具
if (tar.classList.contains('color')) {
color = tar.getAttribute('data-color')
}
// 检查是否点击了线宽工具
if (tar.classList.contains('line')) {
lineWidth = tar.getAttribute('data-line')
}
// 检查是否点击了擦除工具
if (tar.classList.contains('remove')) {
ctx.clearRect(0, 0, cWidth, cHeight)
}
}
init()
})()
</script>
</body>
</html>
Loading...