<html>
<head>
<title></title>
<style>
button.active{
color: #fff;
background-color: blue;
}
#canv{
width: 800px;
height: 600px;
border: 1px solid black;
}
</style>
<script>
</script>
</head>
<body>
<div style="text-align:center;">
<canvas id="canv" width="800px" height="600px"></canvas>
</div>
<!-- 显示画布上鼠标所在坐标 -->
<div class="canvasShowPosition">
</div>
<hr>
<input type="file" id="fileInput" name="file" />
<hr>
<button id="rectBtn" class="btn rectBtn" type="button">矩形</button>
<button id="circleBtn" class="btn circleBtn" type="button">圆</button>
<button id="saveBtn" class="btn saveBtn" type="button">保存</button>
<input id="color" type="color"></input>
<button id="clearCanvasBtn" class="btn clearCanvasBtn" >清空画布</button>
<button id="AlgoProcessBtn" class="btn AlgoProcessBtn" >前处理</button>
<hr>
</body>
<script>
/*
1.图像按原图比例在画布上显示
2.获取画布对应画出的区域的坐标
3.能将坐标上的图像抠出来
ctx.getImageData(0,0,mycanvas.offsetWidth,mycanvas.offsetHeight);
4.画笔改成多边形
5.保存txt标签数据到本地
6.元素拖拽
*/
// 1.获取canvas画布和绘制对象
const mycanvas = document.querySelector('#canv')
const ctx = mycanvas.getContext('2d');
// 矩形框按钮
let rectBtn = document.querySelector('#rectBtn')
// 圆按钮
let circleBtn = document.querySelector('#circleBtn')
// 颜色
let inputColor = document.querySelector('#color')
// 保存
let saveBtn = document.querySelector('#saveBtn')
// 清空画布
let clearCanvasBtn = document.querySelector('#clearCanvasBtn')
// 上传图像
let inputElement = document.getElementById('fileInput');
// 显示图像
let img = new Image();
const canvasShowPosition = document.querySelector(".canvasShowPosition")
// 所有绘制对象列表
const allDrawedObjs = []
const statusConfig = {
IDLE:0,
DRAG_START:1,
DRAGGING:2,
}
// 所有网页上的按钮document.getElementsByTagName("input")
let btn = document.querySelectorAll('.btn');
// 画板字典变量
let draw_board = {
// 拖拽属性
status:statusConfig.IDLE,
dragTarget :null, // 拖拽对象
lastEvtPos:{x:null,y:null}, // 最后移动坐标
drawedPosData:{}, // 绘制对象坐标
// 绘制对象
type: "null", // 绘制类型,是矩形,圆,还是线
isDraw: false, // 是否绘制标志符
beginX: 0, // 鼠标在canvas画布上按下的x坐标
beginY: 0, // 鼠标在canvas画布上按下的y坐标
dataPx: 0, // 画板上一次绘画结束后的图像数据
lineWidth: 2, // 初始化线条的长度
color: null, // 默认颜色黑色
// 画矩形
rectFn: function(e) {
let x = e.pageX - mycanvas.offsetLeft;
let y = e.pageY - mycanvas.offsetTop;
ctx.beginPath();
ctx.strokeStyle = draw_board.color;
ctx.clearRect(0,0,mycanvas.offsetWidth,mycanvas.offsetHeight); //由于画笔是用一个个小圆构成的,所以每次都要清除画板数据,不然会导致线条的成型
// 绘制图像
drawImg()
if(draw_board.dataPx != 0) {
ctx.putImageData(draw_board.dataPx,0,0,0,0,mycanvas.offsetWidth,mycanvas.offsetHeight); //保留画板数据
}
ctx.lineWidth = draw_board.lineWidth;
ctx.strokeRect(draw_board.beginX,draw_board.beginY,x-draw_board.beginX,y-draw_board.beginY);
ctx.closePath();
// 获取当前绘制矩形对象坐标
draw_board.drawedPosData['centerX'] = draw_board.beginX + ( x - draw_board.beginX)/2
draw_board.drawedPosData['centerY'] = draw_board.beginY + ( y - draw_board.beginY)/2
draw_board.drawedPosData['w'] = x - draw_board.beginX
draw_board.drawedPosData['h'] = y - draw_board.beginY
},
// 画圆(鼠标移动中)
circleFn: function(e) {
// 获取当前点坐标
let x = e.pageX - mycanvas.offsetLeft;
let y = e.pageY - mycanvas.offsetTop;
// 开始绘制
ctx.beginPath();
// 线宽
ctx.lineWidth = draw_board.lineWidth;
// 绘制颜色
ctx.strokeStyle = draw_board.color;
// 清除
ctx.clearRect(0,0,mycanvas.offsetWidth,mycanvas.offsetHeight);
// 绘制图像
drawImg()
// x y r 圆(0-2pi)
ctx.arc(draw_board.beginX,draw_board.beginY,Math.abs(y-draw_board.beginY),0,2*Math.PI);
// 如果画板当前数据不为0,则放置当前画板数据(就是把上一次绘制完的画布再画一遍)
if(draw_board.dataPx != 0) {
ctx.putImageData(draw_board.dataPx,0,0,0,0,mycanvas.offsetWidth,mycanvas.offsetHeight);
}
// 绘制
ctx.stroke();
// 结束绘制
ctx.closePath();
// 获取当前绘制圆对象坐标
draw_board.drawedPosData['x'] = draw_board.beginX
draw_board.drawedPosData['y'] = draw_board.beginY
draw_board.drawedPosData['r'] = Math.abs(y-draw_board.beginY)
},
};
// 计算点与点之间的距离
const getDistance=(p1,p2) =>{
return Math.sqrt((p1.x-p2.x)**2+(p1.y-p2.y)**2)
}
// 判断当前位置是否在绘画对象内,如果在,则返回该绘制对象;否则返回 false
const ifInDrawedObj=(pos)=>{
// 如果不存在绘制对象,则直接返回false
if(allDrawedObjs.length ==0){
return false
}
// 遍历所有绘制对象
for(let i =0;i<allDrawedObjs.length;i++){
// 如果当前绘制对象是圆,进行是否在圆内判断
if(allDrawedObjs[i].type=='circle')
{
circle_r = allDrawedObjs[i].drawedPosData['r']
if(getDistance(allDrawedObjs[i].drawedPosData,pos)<circle_r)
{
console.log('在园内')
return allDrawedObjs[i]
}
}
// 如果当前绘制对象是矩形,进行是否在矩形框内进行判断
else if(allDrawedObjs[i].type=='rect')
{
let x1 = allDrawedObjs[i].drawedPosData['centerX'] -allDrawedObjs[i].drawedPosData['w']/2
let y1 = allDrawedObjs[i].drawedPosData['centerY'] -allDrawedObjs[i].drawedPosData['h']/2
let x2 = allDrawedObjs[i].drawedPosData['centerX'] +allDrawedObjs[i].drawedPosData['w']/2
let y2 = allDrawedObjs[i].drawedPosData['centerY'] +allDrawedObjs[i].drawedPosData['h']/2
if(x1<pos.x && pos.x<x2 && pos.y>y1 && pos.y<y2)
{
console.log('在矩形框内')
return allDrawedObjs[i]
}
}
}
return false
}
// 获取画布位置
const getCanvasPosition=e=>{
return {
x:e.offsetX,
y:e.offsetY,
}
}
// 监听获取上传文件路径
inputElement.addEventListener('change', (e) => {
img.src = URL.createObjectURL(e.target.files[0]);
}, false);
// 图像加载时,在画布上显示图像
img.onload = function(){
// 获取图像最长边
max_length = (img.width>img.height)?img.width:img.height
// 画布高
canvH = mycanvas.height;
// 画布宽
canvW = mycanvas.width;
// 图像高宽和画布高宽比
rh = img.height / canvH;
rw = img.width / canvW;
// 最大高宽比
max_radio = (rw>rh)?rw:rh
/*
最大比例值大于1,就同比缩小到1
最大比例值小于1,就同比放大到1
*/
if(max_radio > 1){
// console.log(img.width);
// console.log(img.height);
// console.log(img.width/max_radio);
// console.log(img.height/max_radio);
ctx.drawImage(img, 0, 0, img.width/max_radio, img.height/max_radio);
}else{
// console.log(img.width);
// console.log(img.height);
// console.log(img.width/max_radio);
// console.log(img.height/max_radio);
ctx.drawImage(img, 0, 0, img.width/max_radio, img.height/max_radio);
}
}
function drawImg(){
// 获取图像最长边
max_length = (img.width>img.height)?img.width:img.height
// 画布高
canvH = mycanvas.height;
// 画布宽
canvW = mycanvas.width;
// 图像高宽和画布高宽比
rh = img.height / canvH;
rw = img.width / canvW;
// 最大高宽比
max_radio = (rw>rh)?rw:rh
/*
最大比例值大于1,就同比缩小到1
最大比例值小于1,就同比放大到1
*/
if(max_radio > 1){
// console.log(img.width);
// console.log(img.height);
// console.log(img.width/max_radio);
// console.log(img.height/max_radio);
ctx.drawImage(img, 0, 0, img.width/max_radio, img.height/max_radio);
}else{
// console.log(img.width);
// console.log(img.height);
// console.log(img.width/max_radio);
// console.log(img.height/max_radio);
ctx.drawImage(img, 0, 0, img.width/max_radio, img.height/max_radio);
}
}
// 绘制 绘画对象
const drawDrawedObj = (drawedObj)=>{
if(drawedObj.type=='circle'){
ctx.beginPath();
ctx.arc(drawedObj.drawedPosData.x,drawedObj.drawedPosData.y,drawedObj.drawedPosData.r,0,Math.PI*2)
ctx.stroke()
ctx.closePath();
}else if(drawedObj.type == 'rect'){
ctx.beginPath();
let x = drawedObj.drawedPosData.centerX - drawedObj.drawedPosData.w/2
let y = drawedObj.drawedPosData.centerY - drawedObj.drawedPosData.h/2
let w = drawedObj.drawedPosData.w
let h = drawedObj.drawedPosData.h
ctx.strokeRect(x,y,w,h);
ctx.closePath();
}
}
// 显示按钮激活状态
function click_boxshowdow(selectedBtn) {
btn.forEach((item)=> {
// console.log(item);
item.classList.remove('active');
});
selectedBtn.classList.add('active');
// console.log(selectedBtn);
};
// 2 选中矩形
rectBtn.onclick = function() {
draw_board.type = "rect";
click_boxshowdow(this);
}
// 3 选中圆形
circleBtn.onclick = function() {
draw_board.type = "circle";
click_boxshowdow(this);
}
// 在画布中鼠标按下事件
mycanvas.onmousedown = function(e) {
// 获取当前点击下,的画布坐标
const canvasPostion = getCanvasPosition(e)
// 判断当前点击位置是否在绘画对象内
const DrawedObjRef = ifInDrawedObj(canvasPostion)
console.log('DrawedObjRef')
console.log(DrawedObjRef)
// 1.是,则返回绘画对象
if(DrawedObjRef){
// 停止绘画
draw_board.isDraw = false;
// 赋值拖拽对象
draw_board.dragTarget = DrawedObjRef
draw_board.status = statusConfig.DRAG_START
draw_board.lastEvtPos = canvasPostion
}
// 2.不是,则进行绘画
else{
// 开始绘画
draw_board.isDraw = true;
// 获取起点
draw_board.beginX = e.pageX - mycanvas.offsetLeft;
draw_board.beginY= e.pageY - mycanvas.offsetTop;
}
}
// 在画布中鼠标移动事件(核心)
mycanvas.onmousemove = function(e) {
// 确定在绘画状态
if(draw_board.isDraw) {
// 确定是哪种绘制,并进行绘制
if(draw_board.type != 'null') {
// 确定调用哪个绘画函数
let str = draw_board.type + "Fn";
// 绘画
draw_board[str](e);
}
}
// 拖拽状态
else{
// 获取当前画布位置
const canvasPostion = getCanvasPosition(e)
// 如果当前状态为开始拖拽状态并且移动距离大于5,则将拖拽状态设置为拖拽中
if(draw_board.status == statusConfig.DRAG_START && getDistance(canvasPostion,draw_board.lastEvtPos) >5 ){
console.log('try to drag')
draw_board.status = statusConfig.DRAGGING
}else if(draw_board.status === statusConfig.DRAGGING){
// 拖拽状态为拖拽中,则开始拖拽
console.log('dragging')
// 赋值拖拽对象位置,移动到当前鼠标移动位置
if(draw_board.dragTarget.type=='circle'){
draw_board.dragTarget.drawedPosData.x = canvasPostion.x
draw_board.dragTarget.drawedPosData.y = canvasPostion.y
}else if(draw_board.dragTarget.type=='rect'){
draw_board.dragTarget.drawedPosData.centerX = canvasPostion.x
draw_board.dragTarget.drawedPosData.centerY = canvasPostion.y
}
// 清空画布
ctx.clearRect(0, 0, mycanvas.width, mycanvas.height);
// 绘制图像
drawImg()
// 绘制所有 绘制对象
allDrawedObjs.forEach(drawedObj=>drawDrawedObj(drawedObj))
}
}
// 显示鼠标所在坐标
canvasShowPosition.innerHTML=`x:${e.offsetX} y:${e.offsetY} `
}
// 获取对象属性个数
const attributeCount = function(obj) {
let count = 0;
for(let i in obj) {
if(obj.hasOwnProperty(i)) { // 建议加上判断,如果没有扩展对象属性可以不加
count++;
}
}
return count;
}
// 在画布中鼠标抬起时间
mycanvas.onmouseup = function(e) {
// 停止绘画
draw_board.isDraw = false;
// 记录当前绘画对象
// 绘制完矩形后,添加到绘画对象列表中
if(draw_board.type != "null" && attributeCount(draw_board.drawedPosData)>0) {
allDrawedObjs.push({
type:draw_board.type,
drawedPosData:draw_board.drawedPosData,
})
// 获取绘制对象后,清空绘制对象坐标,为下一个绘制对象做准备
draw_board.drawedPosData = {}
}
console.log(allDrawedObjs)
// 获取当前所有画布内容
draw_board.dataPx = ctx.getImageData(0,0,mycanvas.offsetWidth,mycanvas.offsetHeight);
//如果鼠标抬起,同时当前画布的状态是拖拽中,则将拖拽中状态置为闲置状态
if(draw_board.status === statusConfig.DRAGGING){
draw_board.status = statusConfig.IDLE
}
}
//getImageData() 方法返回 ImageData 对象,该对象拷贝了画布指定矩形的像素数据。
// x 开始复制的左上角位置的 x 坐标(以像素计)。
// y 开始复制的左上角位置的 y 坐标(以像素计)。
// width 要复制的矩形区域的宽度。
// height 要复制的矩形区域的高度。
clearCanvasBtn.onclick = function(e){
ctx.clearRect(0,0,mycanvas.width,mycanvas.height)
}
</script>
</html>
(html)canvas制作画板
于 2023-07-26 10:50:40 首次发布