提示: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('revoke',this)">撤销</div>
<div class="img_tool fl" onclick="changeType('restore',this)">恢复</div>
<div class="img_tool fl" onclick="changeType('text',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');
topCtx.lineCap = 'round';
topCtx.lineJoin = 'round';
//鼠标是否按下 是否移动
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 = 'curve';
//画线宽度
const lineWidth = 10;
//文字大小
const fontSize = 16;
//画线颜色
let strokeStyle = 'rgba(255,0,0,0.6)';
//文字输入框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};
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 = strokeStyle;
topCtx.fillStyle = strokeStyle;
topCtx.lineWidth = lineWidth;
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;
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);
break;
case 'ellipse':
drawEllipse(x,y);
break;
}
}
}
//鼠标抬起
const mouseup = (e)=>{
isCanUp = true;
if(isDown){
// topCan内容画到botCan上
if(drawType!='text')topToBot();
}
}
//topCan内容画到botCan上
const topToBot = ()=>{
//把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 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)=>{
//清除topCtx画布
topCtx.clearRect(0,0,canvasWidth,canvasHeight);
topCtx.beginPath();
// 矩形
topCtx.rect(startPoint.x, startPoint.y, x-startPoint.x, y - startPoint.y);
topCtx.stroke();
}
//橡皮擦
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>
文字会有0.5像素左右的位置偏差,尝试用0.1-0.9等小数均不可以,希望有知道问题的大神告知一下,谢谢
总结
踩坑路漫漫长@~@