canvas画图写文字,有0.5像素左右的位置偏差,无解决办法,希望有知道问题的大神告知一下

提示:canvas画图写文字

文章目录


前言

一、写文字

test.html

c 复制代码
<!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等小数均不可以,希望有知道问题的大神告知一下,谢谢

总结

踩坑路漫漫长@~@

相关推荐
cy玩具14 分钟前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
清灵xmf42 分钟前
TypeScript 类型进阶指南
javascript·typescript·泛型·t·infer
小白学大数据1 小时前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
qq_390161771 小时前
防抖函数--应用场景及示例
前端·javascript
334554321 小时前
element动态表头合并表格
开发语言·javascript·ecmascript
John.liu_Test1 小时前
js下载excel示例demo
前端·javascript·excel
Yaml42 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事2 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶2 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json
getaxiosluo2 小时前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx