提示:canvas画图写文字
文章目录
- 前言
- 一、写文字
- 总结
前言
一、写文字
test.html
<!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 img_tool_active fl" onclick="changeType('curve',this)">涂鸦</div> -->
<!-- <div class="img_tool fl" onclick="changeType('line',this)">直线</div> -->
<div class="img_tool fl" 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);
console.log(activePath,'?????????????')
if(activePath){
isDrag = true;
switch (drawType){
case 'rect':
makeRectActive();
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';
// topCtx.beginPath();
// topCtx.moveTo(x,y);
}
//鼠标移动
const mousemove = (e)=>{
let x = (e||window.event).offsetX;
let y = (e||window.event).offsetY;
if(isDown){
isMove = true;
if(isDrag){
isDragMove = true;
switch(drawType){
case 'curve':
// drawCurve(x,y);
break;
case 'line':
// drawLine(x,y);
break;
case 'eraser':
// drawEraser(x,y);
break;
case 'rect':
// xy 为当前point的坐标
// startPoint为点击的矩形上点 查看当前point.x点距离startPoint.x移动了多少 point.y点距离startPoint.y移动了多少
console.log(activePath.x + (x - startPoint.x),activePath.y + (y - startPoint.y),activePath.width,activePath.height)
drawRect(activePath.x + (x - startPoint.x),activePath.y + (y - startPoint.y),activePath.width,activePath.height);
break;
case 'ellipse':
// drawEllipse(x,y);
break;
}
return;
}
switch(drawType){
case 'curve':
drawCurve(x,y);
break;
case 'line':
drawLine(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,y);
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.type](pathObj.path);
pathObj = null;
}
//把topCan画布生成图片
// let img = new Image();
// img.src = topCan.toDataURL('image/png');
// img.onload = ()=>{
// // partHistory.push(img);
// //添加到botCtx画布
// botCtx.drawImage(img,0,0);
// let historyImg = new Image();
// historyImg.src = botCan.toDataURL('image/png');
// historyImg.onload = ()=>{
// //添加到历史记录
// historyList.push(historyImg);
// historyIndex = historyList.length - 1;
// let ele = document.getElementById('showHistory');
// let html='';
// for(let i=0;i<historyList.length;i++){
// if(historyList[i].src)html += `<img src="${historyList[i].src}" alt="">`
// }
// ele.innerHTML = html;
// }
// //清除topCtx画布
// topCtx.clearRect(0,0,canvasWidth,canvasHeight);
// //botCan画完之后,重置canvas的mouseup isCanUp
// if(isCanUp)isCanUp=false;
// }
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.isPointInPath(path.path,x,y)){
if(botCtx.isPointInStroke(path.path,x,y)){
PointInPath = path;
break;
}
}
return PointInPath;
}
//激活rect图形轮廓
const makeRectActive = ()=>{
botCtx.clearRect(0,0,canvasWidth,canvasHeight);
let arr = [];
for(let i=0;i<pathList.length;i++){
let path = pathList[i]
if(activePath.id != path.id){
botCtx[path.type](path.path);
arr.push(path);
}else{
topCtx[path.type](path.path);
//生成点,可拖拽
// let point =
}
}
arr.push(activePath);
pathList = arr;
}
//画椭圆形
const drawEllipse = (x,y)=>{
//清除topCtx画布
topCtx.clearRect(0,0,canvasWidth,canvasHeight);
topCtx.beginPath();
// 椭圆
topCtx.ellipse((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);
topCtx.stroke();
}
//画矩形
const drawRect = (x,y,width,height)=>{
//清除topCtx画布
topCtx.clearRect(0,0,canvasWidth,canvasHeight);
topCtx.beginPath();
let path = new Path2D();
// 矩形
path.rect(x,y,width,height);
topCtx.stroke(path);
pathObj = {
shape:'rect',
type:'stroke',
path,
x,
y,
width,
height
};
}
//橡皮擦
const drawEraser = (x,y)=>{
//橡皮擦圆形半径
const radius = lineWidth/2;
botCtx.beginPath();
for(let i=0;i<radius*2;i++){
//勾股定理高h
let h = Math.abs( radius - i); //i>radius h = i-radius; i<radius h = radius - i
//勾股定理l
let l = Math.sqrt(radius*radius -h*h);
//矩形高度
let rectHeight = 1;
//矩形宽度
let rectWidth = 2*l;
//矩形X
let rectX = x-l;
//矩形Y
let rectY = y-radius + i;
botCtx.clearRect(rectX, rectY, rectWidth, rectHeight);
}
}
//画透明度直线
const drawLine = (x,y)=>{
if(!isDown)return;
//清空当前画布内容
topCtx.clearRect(0,0,canvasWidth,canvasHeight);
//必须每次都beginPath 不然会卡
topCtx.beginPath();
topCtx.moveTo(startPoint.x,startPoint.y);
topCtx.lineTo(x,y);
topCtx.stroke();
}
//画带透明度涂鸦
const drawCurve = (x,y)=>{
drawPoints.push({x,y});
//清空当前画布内容
topCtx.clearRect(0,0,canvasWidth,canvasHeight);
//必须每次都beginPath 不然会卡
topCtx.beginPath();
topCtx.moveTo(drawPoints[0].x,drawPoints[0].y);
for(let i=1;i<drawPoints.length;i++){
topCtx.lineTo(drawPoints[i].x,drawPoints[i].y);
}
topCtx.stroke();
}
//切换操作
const changeType = (type,that)=>{
// if(drawType == type) return;
let tools = document.getElementsByClassName('img_tool');
for(let i=0;i<tools.length;i++){
let ele = tools[i];
if(ele.classList.contains('img_tool_active'))ele.classList.remove('img_tool_active'); //ele.removeClassName('img_tool_active');
}
that.classList.add('img_tool_active');
drawType = type;
//撤销
if(drawType == 'revoke'){
if(historyIndex>0){
historyIndex--;
drawImage(historyList[historyIndex]);
}
//恢复
}else if(drawType == 'restore'){
if(historyIndex<historyList.length - 1){
historyIndex++;
drawImage(historyList[historyIndex]);
}
}
}
const drawImage = (img)=>{
botCtx.clearRect(0,0,canvasWidth,canvasHeight);
botCtx.drawImage(img,0,0);
}
//canvas添加鼠标事件
topCan.addEventListener('mousedown',mousedown);
topCan.addEventListener('mousemove',mousemove);
topCan.addEventListener('mouseup',mouseup);
//全局添加鼠标抬起事件
document.addEventListener('mouseup',(e)=>{
let x = (e||window.event).offsetX;
let y = (e||window.event).offsetY;
let classList = (e.target || {}).classList || [];
if(classList.contains('img_tool'))return;
if(!isCanUp){
if(drawType == 'line'){
let clientX = topCan.getBoundingClientRect().x;
let clientY = topCan.getBoundingClientRect().y;
drawLine(x-clientX,y-clientY);
}
// topCan内容画到botCan上
topToBot();
}
});
//全局添加鼠标移动事件
document.addEventListener('mousemove',(e)=>{
if(isMove)return isMove = false;
let x = (e||window.event).offsetX;
let y = (e||window.event).offsetY;
if(drawType == 'line'){
let clientX = topCan.getBoundingClientRect().x;
let clientY = topCan.getBoundingClientRect().y;
drawLine(x-clientX,y-clientY);
}
});
</script>
</body>
</html>
总结
踩坑路漫漫长@~@