canvas跟随鼠标画有透明度的矩形边框

提示: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;
        }
        .img_tool_active{
            color: #409EFF;
            border: 1px solid #409EFF;
        }
    </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('eraser',this)">橡皮擦</div>
    </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 imgHistory = [];
        //icon历史
        // let partHistory = [];
        //操作类型
        let drawType = 'curve';
        //画线宽度
        const lineWidth = 10;
        //鼠标按下
        const mousedown = (e)=>{
            isDown = true;
            let x = (e||window.event).offsetX;
            let y = (e||window.event).offsetY;
            startPoint = {x,y};
            drawPoints.push([{x,y}]);
            topCtx.strokeStyle = 'rgba(255,0,0,0.2)';
            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;
                        
                }
                
            }
        }
        //鼠标抬起
        const mouseup = (e)=>{
            isCanUp = true;
            if(isDown){
                // topCan内容画到botCan上
                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 = botCan.toDataURL('image/png');
                historyImg.onload = ()=>{
                    //添加到历史记录
                    imgHistory.push(historyImg);
                }
                //清除topCtx画布
                topCtx.clearRect(0,0,canvasWidth,canvasHeight);
                //botCan画完之后,重置canvas的mouseup isCanUp
                if(isCanUp)isCanUp=false;
            }
            drawPoints = [];
            isDown = false;
            isMove = false;
        }
        //画矩形
        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;
        }

        //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;
            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>

总结

踩坑路漫漫长@~@

相关推荐
踩着两条虫1 分钟前
AI驱动的Vue3应用开发平台深入探究(二十五):API与参考之Renderer API 参考
前端·javascript·vue.js·人工智能·低代码·前端框架·ai编程
信创DevOps先锋9 分钟前
本土化突围:Gitee如何重新定义企业级项目管理工具价值
前端·gitee·jquery
圣光SG21 分钟前
Java类与对象及面向对象基础核心详细笔记
java·前端·数据库
Jinuss30 分钟前
源码分析之React中的useImperativeHandle
开发语言·前端·javascript
ZC跨境爬虫42 分钟前
CSS核心知识点与定位实战全解析(结合Playwright爬虫案例)
前端·css·爬虫
Jinuss44 分钟前
源码分析之React中的forwardRef解读
前端·javascript·react.js
mengsi551 小时前
Antigravity IDE 在浏览器上 verify 成功但本地 IDE 没反应 “开启Tun依然无济于事” —— 解决方案
前端·ide·chrome·antigravity
南风知我意9571 小时前
JavaScript 惰性函数深度解析:从原理到实践的极致性能优化
开发语言·javascript·性能优化
Можно1 小时前
pages.json 和 manifest.json 有什么作用?uni-app 核心配置文件详解
前端·小程序·uni-app
hzhsec1 小时前
钓鱼邮件分析与排查
服务器·前端·安全·web安全·钓鱼邮件