画布绘图,可拖动和移动绘制矩形、圆形、直线和曲线
最编程
2024-03-31 09:26:33
...
<!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>
div,canvas,img{
user-select: none;
}
.my_canvas,.bg_img{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.cf{
content: '';
display: block;
overflow: hidden;
clear: both;
}
.fl{
float: left;
}
.fr{
float: right;
}
.bg_img{
width: 674px;
height: 495px;
background: #ddd;
}
.img_tools{
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
border: 1px solid #eee;
border-radius: 64px;
height: 64px;
line-height: 64px;
box-sizing: border-box;
padding: 15px 20px 0;
}
.img_tool{
height: 32px;
line-height: 32px;
color: #000;
font-size: 14px;
text-align: center;
width: 80px;
border: 1px solid #ddd;
border-radius: 32px;
margin-right: 10px;
cursor: pointer;
position: relative;
}
.img_tool_active{
color: #409EFF;
border: 1px solid #409EFF;
}
.show_history{
position: absolute;
bottom:0;
left: 50%;
transform: translateX(-50%);
}
.show_history>img{
width: 120px;
margin-right: 10px;
border: 1px solid #eee;
border-radius: 4px;
}
.canvas_text{
width: 120px;
height: 32px;
line-height: 32px;
position: absolute;
top: 0;
left: 0;
border: 1px solid #c0c0c0;
border-radius: 4px;
font-size: 16px;
outline: none;
background: none;
display: none;
font-family: Arial, Helvetica, sans-serif;
padding-left: 0;
letter-spacing: 0;
}
</style>
</head>
<body>
<div class="bg_img"></div>
<canvas id="myCanvasBot" class="my_canvas" width="674" height="495"></canvas>
<canvas id="myCanvasTop" class="my_canvas" width="674" height="495"></canvas>
<div class="img_tools cf">
<div class="img_tool fl" onclick="changeType('curve',this)">涂鸦</div>
<div class="img_tool fl" onclick="changeType('line',this)">直线</div>
<div class="img_tool fl img_tool_active" onclick="changeType('rect',this)">矩形</div>
<div class="img_tool fl" onclick="changeType('ellipse',this)">圆形</div>
<!-- <div class="img_tool fl" onclick="changeType('eraser',this)">橡皮擦</div> -->
<!-- <div class="img_tool fl" onclick="changeType('text',this)">文字</div> -->
<!-- <div class="img_tool fl" onclick="changeType('revoke',this)">撤销</div> -->
<!-- <div class="img_tool fl" onclick="changeType('restore',this)">恢复</div> -->
</div>
<input id="canvasText" autofocus class="canvas_text" type="text">
<div id="showHistory" class="show_history"></div>
<script>
const canvasWidth = 674;
const canvasHeight = 495;
//底层canvas
const botCan = document.getElementById('myCanvasBot');
//顶层canvas
const topCan = document.getElementById('myCanvasTop');
//底层画布
const botCtx = botCan.getContext('2d');
//顶层画布
const topCtx = topCan.getContext('2d');
//鼠标是否按下 是否移动
let isDown = false,isMove = false;
//鼠标是否在canvas上抬起
let isCanUp = false;
//需要画图的轨迹
let drawPoints = [];
//起始点x,y
let startPoint = {
x:0,
y:0
};
//图片历史
let historyList = [];
//空历史
historyList.push(new Image())
//当前绘画历史index
let historyIndex = -1;
//icon历史
// let partHistory = [];
//操作类型
let drawType = 'rect';
//画线宽度
const lineWidth = 10;
//文字大小
const fontSize = 16;
//画线颜色
let strokeStyle = 'rgba(255,0,0,0.6)';
//path2D图形列表
let pathList = [];
//path2D单个图形
let pathObj = null;
//path2D的唯一标识
let pathId = 0;
//当前被激活的path2D
let activePath = null;
//是否为拖拽行为
let isDrag = false;
//拖拽是否移动
let isDragMove = false;
//是否为改变尺寸行为
isResize = false;
//改变尺寸点list
let pointsList = [];
//拖拽修改尺寸的点
let activePoint = null;
//文字输入框init
const canvasText = document.getElementById('canvasText');
canvasText.style.display = 'none';
canvasText.style.lineHeight = '32px';
canvasText.style.height = '32px';
canvasText.style.display = 'none';
canvasText.style.color = 'none';
canvasText.addEventListener('blur',()=>{
topCtx.font = fontSize + 'px Arial, Helvetica, sans-serif';
let h = parseFloat(canvasText.style.height);
topCtx.fillText(canvasText.value, startPoint.x+1, startPoint.y+h/2+fontSize/2-1);
canvasText.style.display = 'none';
canvasText.value = '';
topToBot();
})
//起始点x,y
let textPoint = {
x:0,
y:0
};
//鼠标按下
const mousedown = (e)=>{
isDown = true;
let x = (e||window.event).offsetX;
let y = (e||window.event).offsetY;
if(canvasText.style.display == 'none')startPoint = {x,y};
//检测是否点击到图形
activePath = isPointInPath(x,y);
if(activePath){
isDrag = true;
topCtx.strokeStyle = topCtx.fillStyle = botCtx.strokeStyle = botCtx.fillStyle = activePath.strokeStyle||strokeStyle;
topCtx.lineWidth = botCtx.lineWidth = activePath.lineWidth||lineWidth;
switch (activePath.type){
case 'rect':
makePathActive();
break;
case 'ellipse':
makePathActive();
break;
case 'line':
makePathActive();
break;
case 'curve':
makePathActive();
break;
}
return;
}
if(drawType == 'text'){
textPoint = {
x:x+topCan.offsetLeft-canvasWidth/2,
y:y+topCan.offsetTop-canvasHeight/2
};
// canvasText.style.height = 32 + 'px';
canvasText.style.top = textPoint.y+'px';
canvasText.style.left = textPoint.x+'px';
canvasText.style.display = 'block';
canvasText.style.fontSize = fontSize + 'px';
canvasText.style.color = strokeStyle;
setTimeout(()=>{
canvasText.focus();
},100)
}
if(drawType == 'curve'){
drawPoints = [];
drawPoints.push({x,y});
}
topCtx.strokeStyle = topCtx.fillStyle = botCtx.strokeStyle = botCtx.fillStyle = strokeStyle;
topCtx.lineWidth = botCtx.lineWidth = lineWidth;
topCtx.lineCap = topCtx.lineJoin = botCtx.lineCap = botCtx.lineJoin = 'round';
}
//鼠标移动
const mousemove = (e)=>{
let x = (e||window.event).offsetX;
let y = (e||window.event).offsetY;
let distanceX = 0;
let distanceY = 0;
if(isDown){
isMove = true;
if(isDrag){
isDragMove = true;
switch(activePath.type){
case 'curve':
distanceX = x - startPoint.x;
distanceY = y - startPoint.y;
let newPoints = [];
for(let i=0;i<activePath.drawPoints.length;i++){
let drawPoint = activePath.drawPoints[i];
newPoints.push({x:drawPoint.x + distanceX,y:drawPoint.y + distanceY});
}
drawCurve(newPoints);
break;
case 'line':
distanceX = x - startPoint.x;
distanceY = y - startPoint.y;
drawLine(activePath.startX + distanceX,activePath.startY + distanceY,activePath.x + distanceX,activePath.y + distanceY,);
break;
case 'eraser':
// drawEraser(x,y);
break;
case 'rect':
// xy 为当前point的坐标
// startPoint为点击的矩形上点 查看当前point.x点距离startPoint.x移动了多少 point.y点距离startPoint.y移动了多少
drawRect(activePath.x + (x - startPoint.x),activePath.y + (y - startPoint.y),activePath.width,activePath.height);
break;
case 'ellipse':
// drawEllipse(x,y);
drawEllipse(activePath.x + (x - startPoint.x),activePath.y + (y - startPoint.y),activePath.radiusX,activePath.radiusY);
break;
}
return;
}
switch(drawType){
case 'curve':
drawPoints.push({x,y});
drawCurve(drawPoints);
break;
case 'line':
drawLine(startPoint.x,startPoint.y,x,y);
break;
case 'eraser':
drawEraser(x,y);
break;
case 'rect':
// drawRect(x,y);
drawRect(startPoint.x, startPoint.y, x-startPoint.x, y - startPoint.y);
break;
case 'ellipse':
drawEllipse((x+startPoint.x)/2, (y+startPoint.y)/2, Math.abs((x-startPoint.x)/2), Math.abs((y-startPoint.y)/2),0,0, Math.PI*2,true);
break;
}
}
}
//鼠标抬起
const mouseup = (e)=>{
isCanUp = true;
if(isDown){
isDown = false
// topCan内容画到botCan上
if(isDrag){
isDrag = false;
activePath = null;
if(isDragMove){
isDragMove = false;
pathList.pop();
}else{
pathObj = pathList.pop();
}
topToBot();
return
}
if(drawType!='text')topToBot();
}
}
//topCan内容画到botCan上
const topToBot = ()=>{
if(pathObj){
pathObj.id = pathId++;
pathList.push(pathObj);
topCtx.clearRect(0,0,canvasWidth,canvasHeight);
if(isCanUp)isCanUp=false;
botCtx[pathObj.shape](pathObj.path);
pathObj = null;
}
drawPoints = [];
isDown = false;
isMove = false;
}
//判断是否点击到图形
const isPointInPath = (x,y)=>{
let PointInPath = null;
for(let i=0;i<pathList.length;i++){
let path = pathList[i];
if(botCtx.isPointInStroke(path.path,x,y)){
PointInPath = path;
break;
}
}
return PointInPath;
}
//激活rect图形轮廓
const makePathActive = ()=>{
botCtx.clearRect(0,0,canvasWidth,canvasHeight);
let arr = [];
for(let i=0;i<pathList<
上一篇: MySQL 表名 忽略案例问题记录