//html 元素
<canvas width="500" height="300" id="canvas"></canvas>
//js 初始化背景
let canvas = document.getElementById("canvas")//获取canvas对象
//创建Image对象
let img = new Image();
img.src = this.imageSrc; //imgSrc 是背景图片的地址 我用的是 base64码
img.onload = function () {
let ctx = canvas.getContext("2d"); //获取画图2d笔
ctx.drawImage(this, 0, 0, canvas.width, canvas.height) //设置背景图的长宽和 canvas的一样
}
2.1 绘制矩形
// 绘制矩形
function drawerRect(ctx, left, top, w, h) {
ctx.strokeStyle = '#889385'// 画笔颜色
ctx.lineWidth = '2' // 画笔粗细
//ctx.save()
ctx.beginPath()
ctx.rect(left, top, w, h)
ctx.stroke()
ctx.restore()
return {
data: [left, top, w, h]
}
}
2.2 绘制线段
// 绘制直线
function drawerLine(ctx, x, y, z, n) {
ctx.save()
ctx.fillStyle = '#889385'
ctx.lineWidth = '2' // 画笔粗细
ctx.strokeStyle = '#889385'// 画笔颜色
ctx.beginPath()
ctx.moveTo(x, y)
ctx.lineTo(z, n)
ctx.stroke()
ctx.restore()
return {
data: [x, y, z, n]
}
}
2.3 绘制点
function drawerPoint(ctx, x, y) {
ctx.lineWidth = '2' // 画笔粗细
let w = 20
ctx.moveTo(x - w / 2, y);
ctx.lineTo(x + w / 2, y);
ctx.strokeStyle = '#889385'
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = '2' // 画笔粗细
ctx.moveTo(x, y - w / 2);
ctx.lineTo(x, y + w / 2);
ctx.strokeStyle = '#889385'
ctx.stroke();
return {
data: [x, y]
}
}
实际是对画布进行不间断的清除和 重画,我们可以将画好的图形保存在一个数组中,在清除重画后从这个数组里面找到所有的图形 并且调用上面的方法进行重画
清除画布:
ctx.clearRect(0, 0, canvas.width, canvas.height);
重新加载背景 :
ctx.drawImage(image,0,0,canvas.width,canvas.height)
在绘画的时候,需要进行监听,方法有onmousedown onmouseup onmousemove
拖拽实际就是监听鼠标的位置,并且在画布上不断的画图,注意:需要将数组中已经存在的目标形状清除掉
该操作实际和上面类似,监听鼠标即可
在需要的页面调用:、
initCanvas(canvas,img)
绘制什么形状,调用
drawer(shape) //1矩形 3 线段 4 点
删除形状 方法:
setRemove() 在页面上点下想要删除的图形
全部删除:
setRemove() //设置系统需要进行删除 removeAllShape() //删除全部形状
let canvas = undefined
let ctx = undefined
export let shapeList = []
let backImage = new Image();
let canDraw = false //是否可以绘画
let canDrag = false //是否可以拖拽
let canResize = false //是否可以缩放
let canRemove = false //是否可以进行删除
export let currentShape = undefined
let resizePoint = {}
function Point(x, y, type) {
this.x = x
this.y = y
this.type = type // 左击 1 右击 3
}
export function windowToCanvas(e, canvas) {
// 返回元素的大小以及位置
let rect = canvas.getBoundingClientRect()
// rect 的宽度会加上 canvas 的 border 会影响精度
return new Point(e.clientX - rect.left * (canvas.width / rect.width),
e.clientY - rect.top * (canvas.height / rect.height), e.which)
}
function setTitle(x, y, context, name) {
context.font = 'bold 15px Arial';
context.textAlign = 'center';
context.textBaseline = 'bottom';
context.fillStyle = '#39e9d6';
context.fillText(name, x, y);
return true
}
// 绘制矩形
function drawerRect(ctx, left, top, w, h) {
ctx.strokeStyle = '#889385'// 画笔颜色
ctx.lineWidth = '2' // 画笔粗细
//ctx.save()
ctx.beginPath()
ctx.rect(left, top, w, h)
ctx.stroke()
ctx.restore()
return {
data: [left, top, w, h]
}
}
// 绘制圆
function drawerCircle(ctx, x, y, r) {
if (canDraw && !canDrag) {
ctx.strokeStyle = '#889385'// 画笔颜色
ctx.lineWidth = '2' // 画笔粗细
ctx.beginPath()// 开始路径
ctx.arc(x, y, r, 0, Math.PI * 2, true)// 参数依次为圆心坐标x,y,半径,开始结束角,绘制方向顺时针
ctx.stroke()
ctx.restore()
return {
data: [x, y, r]
}
}
}
// 绘制直线
function drawerLine(ctx, x, y, z, n) {
ctx.save()
ctx.fillStyle = '#889385'
ctx.lineWidth = '2' // 画笔粗细
ctx.strokeStyle = '#889385'// 画笔颜色
ctx.beginPath()
ctx.moveTo(x, y)
ctx.lineTo(z, n)
ctx.stroke()
ctx.restore()
return {
data: [x, y, z, n]
}
}
function drawerPoint(ctx, x, y) {
ctx.lineWidth = '2' // 画笔粗细
let w = 20
ctx.moveTo(x - w / 2, y);
ctx.lineTo(x + w / 2, y);
ctx.strokeStyle = '#889385'
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = '2' // 画笔粗细
ctx.moveTo(x, y - w / 2);
ctx.lineTo(x, y + w / 2);
ctx.strokeStyle = '#889385'
ctx.stroke();
return {
data: [x, y]
}
}
function inSideCircle(x1, y1, x, y) {
return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1)) <= 5;
}
//鼠标是否落在矩形内
export function isInsideRect(p0, p1, p) {
let x0 = p0.x
let y0 = p0.y
let x1 = p1.x
let y1 = p1.y
let x = p.x
let y = p.y
let distanceP1 = inSideCircle(x0, y0, x, y)
let distanceP2 = inSideCircle(x1, y1, x, y)
if (distanceP1 || distanceP2) {//缩放
if (distanceP1) {
resizePoint = {
x: x1,
y: y1
}
} else if (distanceP2) {
resizePoint = {
x: x0,
y: y0
}
}
return 2
} else if ((x0 < x && x < x1) && ((y1 < y && y < y0) || (y0 < y && y < y1))) { //移动
return 1
} else { //默认
return 0
}
}
//鼠标是否落在线段所属范围内
export function isInsideLine(p1, p2, p3) {
let distanceP1 = inSideCircle(p1.x, p1.y, p3.x, p3.y)
let distanceP2 = inSideCircle(p2.x, p2.y, p3.x, p3.y)
if (distanceP1 || distanceP2) {
if (distanceP1) {
resizePoint = {
x: p2.x,
y: p2.y
}
} else if (distanceP2) {
resizePoint = {
x: p1.x,
y: p1.y
}
}
return 2
}
let len;
//如果p1.x==p2.x 说明是条竖着的线
if (p1.x - p2.x == 0) {
len = Math.abs(p3.x - p1.x)
} else {
let A = (p1.y - p2.y) / (p1.x - p2.x)
let B = p1.y - A * p1.x
len = Math.abs((A * p3.x + B - p3.y) / Math.sqrt(A * A + 1))
}
return len <= 1 ? 1 : 0;
}
//鼠标是否落在点上
export function isInPoint(p0, p1) {
return Math.sqrt(Math.abs(p0.x - p1.x) * Math.abs(p0.x - p1.x) + Math.abs(p0.y - p1.y) * Math.abs(p0.y - p1.y)) <= 4 ? 1 : 0
}
function listenDrawRect() {
let ctx = canvas.getContext('2d')
let type = 1
canvas.onmousedown = function (e) {
e.preventDefault()
let mousedown = windowToCanvas(e, canvas)
canvas.onmousemove = function (e) {
if (canDrag) { //拖拽逻辑
let point = windowToCanvas(e, canvas) //当前点的坐标
let left = currentShape.shapeStart.x + point.x - mousedown.x;
let top = currentShape.shapeStart.y + point.y - mousedown.y;
removeCurrentShapeInShapeList()
reDrawAllShape(ctx, canvas)
drawerRect(ctx, left, top, currentShape.width, currentShape.height)
setTitle(left, top, ctx, '矩形');
} else if (canDraw) { //第一次画
reDrawAllShape(ctx, canvas)
e.preventDefault()
let point = windowToCanvas(e, canvas)
let w = Math.abs(point.x - mousedown.x)
let h = Math.abs(point.y - mousedown.y)
let left = point.x > mousedown.x ? mousedown.x : point.x
let top = point.y > mousedown.y ? mousedown.y : point.y
drawerRect(ctx, left, top, w, h)
} else if (canResize) { //对角线缩放
e.preventDefault()
let point = windowToCanvas(e, canvas)
let x = point.x
let y = point.y
removeCurrentShapeInShapeList()
reDrawAllShape(ctx, canvas)
let w = Math.abs(x - resizePoint.x)
let h = Math.abs(y - resizePoint.y)
let left = x > resizePoint.x ? resizePoint.x : x
let top = y > resizePoint.y ? resizePoint.y : y
drawerRect(ctx, left, top, w, h)
setTitle(left, top, ctx, '矩形');
}
}
canvas.onmouseup = function (e) {
if (canDraw) {
let result = setTitle(mousedown.x, mousedown.y, ctx, '矩形');
if (result) {
let point = windowToCanvas(e, canvas)
let shapeStart = mousedown
let shapeEnd = point
let width = Math.abs(point.x - mousedown.x)
let height = Math.abs(point.y - mousedown.y)
let uid = guid();
shapeList.push({shapeStart, shapeEnd, width, height, type, uid})
}
canDraw = false;
} else if (canDrag) {
let point = windowToCanvas(e, canvas)
if (!(point.x===mousedown.x && point.y===mousedown.y)){
currentShape.shapeStart.x = currentShape.shapeStart.x + point.x - mousedown.x
currentShape.shapeStart.y = currentShape.shapeStart.y + point.y - mousedown.y
currentShape.shapeEnd.x = currentShape.shapeEnd.x + point.x - mousedown.x
currentShape.shapeEnd.y = currentShape.shapeEnd.y + point.y - mousedown.y
shapeList.push(currentShape)
canDrag = false;
}
} else if (canResize) {
let point = windowToCanvas(e, canvas)
currentShape.shapeEnd.x = point.x > resizePoint.x ? point.x : resizePoint.x
currentShape.shapeEnd.y = point.y > resizePoint.y ? point.y : resizePoint.y
currentShape.shapeStart.x = point.x > resizePoint.x ? resizePoint.x : point.x
currentShape.shapeStart.y = point.y > resizePoint.y ? resizePoint.y : point.y
currentShape.width = Math.abs(point.x - resizePoint.x)
currentShape.height = Math.abs(point.y - resizePoint.y)
shapeList.push(currentShape)
canResize = false;
}else if(canRemove) {
removeCurrentShapeInShapeList()
reDrawAllShape(ctx,canvas)
canRemove = false
}
isInsideShape(canvas)
}
}
}
function listenDrawLine() {
let ctx = canvas.getContext('2d')
let type = 3
canvas.onmousedown = function (e) {
e.preventDefault()
let mousedown = windowToCanvas(e, canvas)
canvas.onmousemove = function (e) {
if (canDrag) { //拖拽逻辑
let point = windowToCanvas(e, canvas) //当前点的坐标
let p1_x = currentShape.shapeStart.x + point.x - mousedown.x;
let p1_y = currentShape.shapeStart.y + point.y - mousedown.y;
let p2_x = currentShape.shapeEnd.x + point.x - mousedown.x;
let p2_y = currentShape.shapeEnd.y + point.y - mousedown.y;
removeCurrentShapeInShapeList()
reDrawAllShape(ctx, canvas)
drawerLine(ctx, p1_x, p1_y, p2_x, p2_y)
setTitle(p1_x, p1_y, ctx, '线段');
} else if (canDraw) { //第一次画
reDrawAllShape(ctx, canvas)
e.preventDefault()
let point = windowToCanvas(e, canvas)
drawerLine(ctx, mousedown.x, mousedown.y, point.x, point.y)
} else if (canResize) {
e.preventDefault()
let point = windowToCanvas(e, canvas)
let x = point.x
let y = point.y
removeCurrentShapeInShapeList()
reDrawAllShape(ctx, canvas)
drawerLine(ctx, x, y, resizePoint.x, resizePoint.y)
setTitle(x, y, ctx, '线段');
}
}
canvas.onmouseup = function (e) {
if (canDraw) {
let result = setTitle(mousedown.x, mousedown.y, ctx, '线段');
if (result) {
let point = windowToCanvas(e, canvas)
let shapeStart = mousedown
let shapeEnd = point
let uid = guid()
shapeList.push({shapeStart, shapeEnd, type, uid})
}
canDraw = false
} else if (canDrag) {
let point = windowToCanvas(e, canvas)
let result = setTitle(currentShape.shapeStart.x + point.x - mousedown.x, currentShape.shapeStart.y + point.y - mousedown.y, ctx, '线段');
if (result) {
if (!(point.x===mousedown.x && point.y===mousedown.y)){
currentShape.shapeStart.x = currentShape.shapeStart.x + point.x - mousedown.x;
currentShape.shapeStart.y = currentShape.shapeStart.y + point.y - mousedown.y;
currentShape.shapeEnd.x = currentShape.shapeEnd.x + point.x - mousedown.x;
currentShape.shapeEnd.y = currentShape.shapeEnd.y + point.y - mousedown.y;
shapeList.push(currentShape)
canDrag = false
}
}
} else if (canResize) {
let point = windowToCanvas(e, canvas)
currentShape.shapeStart.x = point.x
currentShape.shapeStart.y = point.y
currentShape.shapeEnd.x = resizePoint.x
currentShape.shapeEnd.y = resizePoint.y
shapeList.push(currentShape)
canResize = false
}else if(canRemove) {
removeCurrentShapeInShapeList()
reDrawAllShape(ctx,canvas)
canRemove = false
}
isInsideShape(canvas)
}
}
}
function listenDrawPoint() {
let ctx = canvas.getContext('2d')
let type = 4
canvas.onmousedown = function (event) {
canvas.onmousemove = function (e) {
if (canDrag) { //拖拽逻辑
let point = windowToCanvas(e, canvas) //当前点的坐标
removeCurrentShapeInShapeList()
reDrawAllShape(ctx, canvas)
drawerPoint(ctx, point.x, point.y)
setTitle(point.x - 10, point.y - 10, ctx, '点');
} else if (canDraw) {
}
}
canvas.onmouseup = function (e) {
let point = windowToCanvas(e, canvas)
if (canDraw) {
let result = setTitle(point.x - 10, point.y - 10, ctx, '点');
if (result) {
let uid = guid()
let shapeStart = point
drawerPoint(ctx, point.x, point.y)
shapeList.push({shapeStart, type, uid})
}
canDraw = false
} else if (canDrag) {
let result = setTitle(point.x - 10, point.y - 10, ctx, '点');
if (result) {
currentShape.shapeStart.x = point.x;
currentShape.shapeStart.y = point.y;
shapeList.push(currentShape)
drawerPoint(ctx, currentShape.shapeStart.x, currentShape.shapeStart.y)
canDrag = false
}
}else if(canRemove) {
removeCurrentShapeInShapeList()
reDrawAllShape(ctx,canvas)
canRemove = false
}
isInsideShape(canvas)
}
}
}
export function removeCurrentShapeInShapeList() {
let index = shapeList.findIndex(item => {
if (item.uid === currentShape.uid) {
return true
}
});
if (index !== -1) {
shapeList.splice(index, 1)
}
}
export function setRemove(){
canRemove = true
}
export function removeAllShape(){
shapeList = []
reDrawAllShape()
}
// 绘制的方法及事件,根据当前选择的工具进行不同的方法绘制图形
export function drawer(type) {
canDraw = true
canvas.onclick = null
canvas.onmousedown = null
canvas.onmouseup = null
canvas.onmousemove = null
switch (type) {
case 1: //矩形
listenDrawRect(canvas)
break;
case 2: //圆形
/*canvas.onmousedown = function (e) {
e.preventDefault()
let mousedown = windowToCanvas(e, canvas)
canvas.onmousemove = function (e) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(backImage,0,0,canvas.width,canvas.height)
e.preventDefault()
let point = windowToCanvas(e, canvas)
let rx = (point.x - mousedown.x) / 2
let ry = (point.y - mousedown.y) / 2
let r = Math.sqrt(rx * rx + ry * ry)
drawerCircle(ctx, rx + mousedown.x, ry + mousedown.y, r)
}
canvas.onmouseup = function (e) {
let result = setTitle(mousedown.x, mousedown.y, ctx, '圆形');
if (result) {
let point = windowToCanvas(e, canvas)
shapeList.push(point)
}
e.preventDefault()
canvas.onmousemove = null
canDraw = false
for (let i = 0; i < shapeList.length; i++) {
console.log(shapeList[i])
}
}
}*/
break;
case 3: //线段
listenDrawLine(canvas)
break;
case 4: //点
listenDrawPoint(canvas)
break;
default:
canDraw = false
canResize = false
break;
}
}
export function initCanvas(ca,img){
canvas = ca
ctx = canvas.getContext("2d")
backImage = img
isInsideShape()
}
function guid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
//是否可以被拖动
function isInsideShape() {
canvas.onmousemove = function (e) {
let bool = undefined;
canDrag = false
canResize = false
canDraw = false
canvas.style.cursor = 'default'
cas:for (let i = 0; i < shapeList.length; i++) {
switch (shapeList[i].type) {
case 1:
bool = isInsideRect(shapeList[i].shapeStart, shapeList[i].shapeEnd, windowToCanvas(e, canvas));
bool === 1 ? canvas.style.cursor = 'move' : (bool === 2 ? canvas.style.cursor = 'se-resize' : 'default');
if (bool === 1 ) {
currentShape = shapeList[i]
canDrag = !canRemove
} else if (bool === 2) {
currentShape = shapeList[i]
canResize = true
}
listenDrawRect(canvas)
break;
case 3:
bool = isInsideLine(shapeList[i].shapeStart, shapeList[i].shapeEnd, windowToCanvas(e, canvas));
bool === 1 ? (canvas.style.cursor = 'move') : (bool === 2 ? canvas.style.cursor = 'se-resize' : canvas.style.cursor = 'default');
if (bool === 1) {
currentShape = shapeList[i]
canDrag = !canRemove
listenDrawLine(canvas)
} else if (bool === 2) {
currentShape = shapeList[i]
canResize = true
listenDrawLine(canvas)
}
break;
case 4:
bool = isInPoint(shapeList[i].shapeStart, windowToCanvas(e, canvas));
bool === 1 ? canvas.style.cursor = 'move' : canvas.style.cursor = 'default';
if (bool === 1 && !canRemove) {
currentShape = shapeList[i]
canDrag = true
listenDrawPoint(canvas)
}else if(canRemove) {
currentShape = shapeList[i]
listenDrawPoint(canvas)
}
break;
default:
bool = undefined
canvas.style.cursor = 'default'
}
if (bool===1 || bool===2) {
break cas;
}
}
}
}
function reDrawAllShape() {
ctx.beginPath()
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(backImage, 0, 0, canvas.width, canvas.height)
for (let i = 0; i < shapeList.length; i++) {
let left = undefined;
let top = undefined;
if (shapeList[i].type === 1) {
left = shapeList[i].shapeStart.x > shapeList[i].shapeEnd.x ? shapeList[i].shapeEnd.x : shapeList[i].shapeStart.x
top = shapeList[i].shapeStart.y > shapeList[i].shapeEnd.y ? shapeList[i].shapeEnd.y : shapeList[i].shapeStart.y
drawerRect(ctx, left, top, shapeList[i].width, shapeList[i].height)
setTitle(left, top, ctx, '矩形');
}
if (shapeList[i].type === 2) { //圆形
}
if (shapeList[i].type === 3) { //线段
drawerLine(ctx, shapeList[i].shapeStart.x, shapeList[i].shapeStart.y, shapeList[i].shapeEnd.x, shapeList[i].shapeEnd.y)
top = shapeList[i].shapeStart.y
left = shapeList[i].shapeStart.x
setTitle(left, top, ctx, '线段');
}
if (shapeList[i].type === 4) { //线段
drawerPoint(ctx, shapeList[i].shapeStart.x, shapeList[i].shapeStart.y)
top = shapeList[i].shapeStart.y
left = shapeList[i].shapeStart.x
setTitle(left - 10, top - 10, ctx, '点');
}
}
}