canvas画图,画矩形、圆形、直线可拖拽移动,可拖拽更改尺寸大小

提示: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 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;
        //resize是否移动
        let isResizeMove = false;
        //改变尺寸点
        let resizePath = null;
        //改变尺寸点List
        let resizePointList = [];
        //文字输入框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};
            //检查是否点击到resize的点
            activePoint = isResizePointInPath(x,y);
            if(activePoint){
                isResize = true;
                activePath = activePoint.activePath;//原有path,需要清除
                //可resize,清除resize点
                topCtx.clearRect(0,0,canvasWidth,canvasHeight);
                switch (activePoint.type){
                    case 'rect':
                        makePathActive();
                        break;
                    case 'ellipse':
                        makePathActive();
                        break;
                    case 'line':
                        makePathActive();
                        break;
                }
                return
            }
            //检测是否点击到图形
            activePath = isPointInPath(x,y);
            if(activePath){
                createResizePoint(activePath); //只有点击到图形的时候,才添加图形resize的点
                isDrag = true;
                topCtx.strokeStyle = topCtx.fillStyle = botCtx.strokeStyle = botCtx.fillStyle = activePath.strokeStyle||strokeStyle;
                topCtx.lineWidth = botCtx.lineWidth = activePath.lineWidth||lineWidth;
                topCtx.clearRect(0,0,canvasWidth,canvasHeight);
                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(isResize){
                    isResizeMove = true;
                    if(activePoint&&activePoint.resizeFun){
                        activePoint.resizeFun(x,y);
                    }
                    return
                }
                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(isResize){
                    isResize = false;
                    activePath = null;
                    resizePointList = [];
                    if(isResizeMove){
                        isResizeMove = false;
                        pathList.pop();
                    }else{
                        pathObj = pathList.pop();
                    }
                    topToBot();
                    return;
                }
                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);
                //如果有resizePoints
                if(pathObj.points&&pathObj.points.length>0){
                    drawResizePoint(pathObj.points);
                }
                pathObj.points = [];
                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;
        }
        //判断是否点击到图形
        const isResizePointInPath = (x,y)=>{
            let point = null;
            for(let i=0;i<resizePointList.length;i++){
                let resizePoint = resizePointList[i];
                if(botCtx.isPointInStroke(resizePoint.path,x,y)){
                    point = resizePoint;
                    break;
                }
            }
            return point;
        }
        //激活rect图形轮廓  botCtx删除activePath  topCtx添加activePath
        const makePathActive = ()=>{
            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.shape](path.path);
                    arr.push(path);
                }else{
                    topCtx.strokeStyle = path.strokeStyle;
                    topCtx[path.shape](path.path);
                    //创建可拖动的点
                }   
            }
            arr.push(activePath);
            pathList = arr;
        }
        //创建resize的点
        const createResizePoint = ()=>{
            activePath.points = [];
            let ponitFillStyle = 'rgba(0,0,0,0.8)';
            let pointWidth = 10;
            let pointHeight = 10;
            switch(activePath.type){
                case 'rect':
                    activePath.points = [
                        { 
                            x:activePath.x-pointWidth/2,                
                            y:activePath.y-pointHeight/2,                   
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:0,activePath:activePath, //用于删除原activePath
                            resizeFun:(x,y)=>{ drawRect(x,y,activePath.x + activePath.width-x,activePath.y + activePath.height-y); },
                        },
                        {
                            x:activePath.x+activePath.width/2-pointWidth/2,   
                            y:activePath.y-pointHeight/2,                         
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:1,activePath:activePath,
                            resizeFun:(x,y)=>{
                                drawRect(activePath.x,y,activePath.width,activePath.height+activePath.y-y);
                            }
                        },
                        {
                            x:activePath.x+activePath.width-pointWidth/2,     
                            y:activePath.y-pointHeight/2,                         
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:2,activePath:activePath,
                            resizeFun:(x,y)=>{drawRect(activePath.x, y ,x-activePath.x, activePath.height+ activePath.y - y);}
                        },
                        {
                            x:activePath.x+activePath.width-pointWidth/2,     
                            y:activePath.y+activePath.height/2-pointHeight/2,     
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:3,activePath:activePath,
                            resizeFun:(x,y)=>{
                               drawRect(activePath.x, activePath.y ,x-activePath.x, activePath.height);
                           }
                        },
                        {
                            x:activePath.x+activePath.width-pointWidth/2,     
                            y:activePath.y+activePath.height-pointHeight/2,       
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:4,activePath:activePath,
                            resizeFun:(x,y)=>{
                               drawRect(activePath.x, activePath.y ,x-activePath.x, y-activePath.y);
                            }
                        },
                        {
                            x:activePath.x+activePath.width/2-pointWidth/2,   
                            y:activePath.y+activePath.height-pointHeight/2,       
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:5,activePath:activePath,
                            resizeFun:(x,y)=>{
                               drawRect(activePath.x, activePath.y ,activePath.width, y-activePath.y);
                            }
                        },
                        {
                            x:activePath.x-pointWidth/2,                      
                            y:activePath.y+activePath.height-pointHeight/2,       
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:6,activePath:activePath,
                            resizeFun:(x,y)=>{
                               drawRect(x, activePath.y ,activePath.x-x+activePath.width, y-activePath.y);
                            }
                        },
                        {
                            x:activePath.x-pointWidth/2,                     
                            y:activePath.y+activePath.height/2-pointHeight/2,
                            width:10,height:10,ponitFillStyle, type:activePath.type,id:7,activePath:activePath,
                            resizeFun:(x,y)=>{
                               drawRect(x, activePath.y ,activePath.x-x+activePath.width, activePath.height);
                            }
                        },
                    ];
                    // drawResizePoint( activePath.points);
                    break;
                case 'ellipse':
                    activePath.points = [
                        {
                            x:activePath.x - pointWidth/2,
                            y:activePath.y - activePath.radiusY - pointHeight/2,                   
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:0,activePath:activePath,
                            resizeFun:(x,y)=>{drawEllipse(activePath.x,activePath.y,activePath.radiusX,Math.abs(y-activePath.y));},
                        },
                        {
                            x:activePath.x + activePath.radiusX - pointWidth/2,
                            y:activePath.y - pointHeight/2,                   
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:0,activePath:activePath,
                            resizeFun:(x,y)=>{drawEllipse(activePath.x,activePath.y,Math.abs(x - activePath.x),activePath.radiusY);},
                        },
                        {
                            x:activePath.x - pointWidth/2,
                            y:activePath.y + activePath.radiusY - pointHeight/2,                   
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:0,activePath:activePath,
                            resizeFun:(x,y)=>{drawEllipse(activePath.x,activePath.y,activePath.radiusX,Math.abs(y-activePath.y));},
                        },
                        {
                            x:activePath.x - activePath.radiusX - pointWidth/2,
                            y:activePath.y - pointHeight/2,                   
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:0,activePath:activePath,
                            resizeFun:(x,y)=>{drawEllipse(activePath.x,activePath.y,Math.abs(x - activePath.x),activePath.radiusY);},
                        },
                    ]
                    // drawResizePoint( activePath.points);
                    break;
                case 'line':
                    activePath.points = [
                        { 
                            x:activePath.startX - pointWidth/2,
                            y:activePath.startY - pointHeight/2,                   
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:0,activePath:activePath,
                            resizeFun:(x,y)=>{drawLine(x,y,activePath.x,activePath.y);},
                        },
                        { 
                            x:activePath.x - pointWidth/2,
                            y:activePath.y - pointHeight/2,                   
                            width:10,height:10,ponitFillStyle,type:activePath.type,id:0,activePath:activePath,
                            resizeFun:(x,y)=>{drawLine(activePath.startX,activePath.startY,x,y);},
                        }
                    ]
                    // drawResizePoint( activePath.points);
                    break;
            }
            drawResizePoint( activePath.points);
        }
        //画resize的点
        const drawResizePoint = (points)=>{
            resizePointList = [];
            for(let i=0;i<points.length;i++){
                let resizePoint = points[i];
                let path = new Path2D();
                topCtx.beginPath();
                topCtx.moveTo(resizePoint.x,resizePoint.y);
                path.rect(resizePoint.x,resizePoint.y,resizePoint.width,resizePoint.height);
                topCtx.strokeStyle = resizePoint.ponitFillStyle;
                topCtx.stroke(path);
                resizePoint.path = path;
                resizePointList.push(resizePoint)
            }
        }
        //画椭圆形
        const drawEllipse = (x,y,radiusX,radiusY)=>{
            pathObj = null;
            //清除topCtx画布
            topCtx.clearRect(0,0,canvasWidth,canvasHeight);
            topCtx.beginPath();
            let path = new Path2D();
            // 椭圆
            path.ellipse(x,y,radiusX,radiusY,0,0, Math.PI*2,true);
            topCtx.strokeStyle = strokeStyle;
            topCtx.stroke(path);
            pathObj = {
                type:'ellipse',
                shape:'stroke',
                path,
                x, 
                y, 
                radiusX, 
                radiusY,
                lineWidth:topCtx.lineWidth||lineWidth,
                strokeStyle:topCtx.strokeStyle||strokeStyle,
                points:[],
            };
        }
        //画矩形
        const drawRect = (x,y,width,height)=>{
            pathObj = null;
            //清除topCtx画布
            topCtx.clearRect(0,0,canvasWidth,canvasHeight);
            topCtx.beginPath();
            let path = new Path2D();
            // 矩形
            //这里需要确认起始点和结束点
            //即  不管画出的矩形是向上,向下 左上角点为起始点,做下角点为结束点
            let beginX = width<0?x+width:x;
            let beginY = height<0?y+height:y

            path.rect(beginX,beginY,Math.abs(width),Math.abs(height));
            topCtx.strokeStyle = strokeStyle;
            topCtx.stroke(path);
            pathObj = {
                type:'rect',
                shape:'stroke',
                path,
                x:beginX, 
                y:beginY, 
                width:Math.abs(width), 
                height:Math.abs(height),
                lineWidth:topCtx.lineWidth||lineWidth,
                strokeStyle:topCtx.strokeStyle||strokeStyle,
                points:[],
            };
        }
        //橡皮擦
        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 = (startX,startY,x,y)=>{
            if(!isDown)return;
            pathObj = null;
            //清空当前画布内容
            topCtx.clearRect(0,0,canvasWidth,canvasHeight);
            //必须每次都beginPath  不然会卡
            topCtx.beginPath();
            let path = new Path2D();
            path.moveTo(startX,startY);
            path.lineTo(x,y);
            topCtx.strokeStyle = strokeStyle;
            topCtx.stroke(path);
            pathObj = {
                type:'line',
                shape:'stroke',
                path,
                x, 
                y, 
                startX, 
                startY,
                lineWidth:topCtx.lineWidth||lineWidth,
                strokeStyle:topCtx.strokeStyle||strokeStyle,
                points:[],
            };
        }
        //画带透明度涂鸦
        const drawCurve = (drawPointsParams)=>{
            // drawPoints.push({x,y});
            if(!drawPointsParams||drawPointsParams.length<1)return
            pathObj = null;
            //清空当前画布内容
            topCtx.clearRect(0,0,canvasWidth,canvasHeight);
            //必须每次都beginPath  不然会卡
            topCtx.beginPath();
            let path = new Path2D();
            path.moveTo(drawPointsParams[0].x,drawPointsParams[0].y);
            for(let i=1;i<drawPointsParams.length;i++){
                path.lineTo(drawPointsParams[i].x,drawPointsParams[i].y);
            }
            topCtx.strokeStyle = strokeStyle;
            topCtx.stroke(path);
            pathObj = {
                type:'curve',
                shape:'stroke',
                path,
                drawPoints:drawPointsParams,
                lineWidth:topCtx.lineWidth||lineWidth,
                strokeStyle:topCtx.strokeStyle||strokeStyle
            };
            
        }
        //切换操作
        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');
            }
            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){
                isDown = false;
                // topCan内容画到botCan上
                if(isDrag){
                    isDrag = false;
                    activePath = null;
                    if(isDragMove){
                        isDragMove = false;
                        pathList.pop();
                    }else{
                        pathObj = pathList.pop();
                    }
                    topToBot();
                    return
                }
                if(drawType == 'line'&&!isDrag){
                    let clientX = topCan.getBoundingClientRect().x;
                    let clientY = topCan.getBoundingClientRect().y;
                    drawLine(startPoint.x,startPoint.y,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'&&!isDrag){
                let clientX = topCan.getBoundingClientRect().x;
                let clientY = topCan.getBoundingClientRect().y;
                drawLine(startPoint.x,startPoint.y,x-clientX,y-clientY);
            }
        });
    </script>
</body>
</html>

请添加图片描述
请添加图片描述

总结

踩坑路漫漫长@~@

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/519738.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LLM大语言模型(九):LangChain封装自定义的LLM

背景 想基于ChatGLM3-6B用LangChain做LLM应用&#xff0c;需要先了解下LangChain中对LLM的封装。本文以一个hello world的封装来示例。 LangChain中对LLM的封装 继承关系&#xff1a;BaseLanguageModel——》BaseLLM——》LLM LLM类 简化和LLM的交互 _call抽象方法定义 ab…

教你快速认识Java中的抽象类和接口

目录 引言 抽象类&#xff08;Abstract Class&#xff09; 抽象类的概念 抽象类的图标 抽象类的语法 抽象类的特点 接口&#xff08;Interface&#xff09; 接口的概念 接口的图标 接口的语法 接口的特点 接口的使用 接口的意义 抽象类与接口的区别 Object类 结…

idea开发 java web 高校学籍管理系统bootstrap框架web结构java编程计算机网页

一、源码特点 java 高校学籍管理系统是一套完善的完整信息系统&#xff0c;结合java web开发和bootstrap UI框架完成本系统 &#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 前段主要技术 css jq…

最优算法100例之33-数据流的中位数

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。 例如, [2,3,4] 的中位数是 3 [2,3] 的中位…

对PAC的侧信道攻击

PAC存不存在安全性问题&#xff1f;侧信道攻击&#xff1f;本博客探讨这些问题。

达梦DMHS-Manager工具安装部署

目录 1、前言 1.1、平台架构 1.2、平台原理 2、环境准备 2.1、硬件环境 2.2、软件环境 2.3、安装DMHS 2.3.1、源端DMHS前期准备 2.3.2、源端DMHS安装 2.3.3、目的端DMHS安装 3、DMHS-Manager客户端部署 3.1、启动dmhs web服务 3.2、登录web管理平台 4、添加DMHS实…

手搓Docker-Image-Creator(DIC)工具(03):实现alpine+jre的镜像

此篇博客将介绍如何使用 Docker 创建一个alpine3.10-jre1.8.0_401 的 Docker 镜像&#xff0c;并使用 Docker 运行起来。将用到 Dockerfile 的 COPY 命令、RUN 命令、ENV 命令&#xff0c;最终实现基于单一应用的 Dockerfile 构建镜像和运行。 紧急修改&#xff1a;代码我是在m…

python笔记(9)Dictionary(字典)

目录 创建字典 取值 修改字典 删除 内置函数和方法 创建字典 字典键值和value用&#xff1a;隔开&#xff0c;键值是不可变的&#xff0c;而且必须是唯一的&#xff0c;值可以变&#xff0c;可以是任意类型 dict {key1 : value1, key2 : value2 } 1&#xff09;不允许同…

【C语言】“vid”Microsoft Visual Studio安装及应用(检验内存泄露)

文章目录 前言安装包获取配置VLD完成 前言 我们在写代码时往往容易存在内存泄漏的情况&#xff0c;所以存在这样一个名为VLD的工具用来检验内存泄漏&#xff0c;现在我来教大家安装一下 安装包获取 vld下载网址&#xff1a;https://github.com/KindDragon/vld/releases/tag/…

mysql中主键索引和联合索引的原理解析

mysql中主键索引和联合索引的原理解析 一、主键索引二、什么是联合索引? 对应的B树是如何生成的?1、建立索引方式2、什么是最左前缀原则?3、回表4、为什么要遵守最左前缀原则才能利用到索引?5、什么是覆盖索引?6、索引扫描底层原理7、order by为什么会导致索引消失&#x…

vue3和vue2项目中如何根据不同的环境配置基地址?

在不同环境下取出的变量的值是不同的, 像这样的变量称为环境变量 为什么要使用环境变量呢? 开发环境生产环境下的接口地址有可能是不一样的&#xff0c;所以我们需要根据环境去配置不同的接口基地址 1、vue2环境变量配置 在根目录创建&#xff1a;.env.development和.env.p…

RTPS协议概述

一.RTPS协议概述 RTPS协议主要由四个部分组成&#xff1a; 1.发现模块&#xff08;Discovery&#xff09; ​ 发现模块是定义了RTPS的参与者&#xff08;Participant&#xff09;获取其他RTPS的参与者&#xff08;Participant&#xff09;&#xff0c;端点&#xff08;Endpo…

解决沁恒ch592单片机在tmos中使用USB总线时,接入USB Hub无法枚举频繁Reset的问题

开发产品时采用了沁恒ch592&#xff0c;做USB开发时遇到了一个奇葩的无法枚举问题。 典型症状 使用USB线直连电脑时没有问题&#xff0c;可以正常使用。 如果接入某些特定方案的USB Hub&#xff08;例如GL3510、GL3520&#xff09;&#xff0c;可能会出现以下2种情况&#xf…

C# MES通信从入门到精通(9)——c#使用visual studio2019社区版部署webservice服务

前言 在上位机软件开发领域,我们经常使用调用webservice接口来和mes系统进行交互,我们在开发Mes程序之前也可以自己先模拟发布客户的webservice接口进行调用从而加快软件开发进度,本文就是详细介绍如何自己发布webservice接口。 1、创建 参考这篇博文:webservice服务创建…

【MYSQL进阶篇】详细版MYSQL优化

Mysql优化&#xff0c;让你彻底知道优化的各种情况以及解决方案 有一系列的代码和图片让你一步一步走&#xff0c;成为索引的真正实践者 坚持看完这篇文章&#xff0c;会让你功力瞬间提升一个档次 制作不易&#xff0c;觉得不错请点赞收藏 &#xff01…

java——文件上传

一、文件上传——简介 文件上传的简介&#xff1a;文件上传是指将本地计算机中的文件传输到网络上的服务器或另一台计算机上的过程。在 Web 开发中&#xff0c;文件上传通常指的是将用户通过 Web 页面提交的文件&#xff08;如图像、文档、音频、视频等&#xff09;传输到服务器…

包子凑数【蓝桥杯】/完全背包

包子凑数 完全背包 完全背包问题和01背包的区别就是&#xff0c;完全背包问题每一个物品能取无限次。 思路&#xff1a;当n个数的最大公约数不为1&#xff0c;即不互质时&#xff0c;有无限多个凑不出来的&#xff0c;即n个数都可以表示成kn&#xff0c;k为常数且不为1。当n个…

DFS序列

什么是DFS序 DFS序是指对一棵树进行DFS时&#xff0c;每个节点被访问到的顺序。DFS序分成两个部分&#xff1a;进入该节点的顺序和退出该节点的顺序。 如何求DFS序 对于DFS中当前节点 1&#xff1a;计数 2&#xff1a;进入当前节点的顺序等于当前计数 3&#xff1a;想所有…

达梦使用disql登录数据库显示“未连接”

基础环境 操作系统&#xff1a;Red Hat Enterprise Linux Server release 7.9 (Maipo) 数据库版本&#xff1a;DM Database Server 64 V8 架构&#xff1a;单实例问题&#xff1a;达梦数据库在使用disql登录时&#xff0c;显示“未连接”。 指定了IP和端口号还是连接异常。 […

淘宝扭蛋机小程序源码搭建:打造专属电商娱乐新平台

在数字化浪潮的推动下&#xff0c;电商平台不断创新&#xff0c;以满足消费者日益多样化的需求。淘宝扭蛋机小程序作为一种创新的电商娱乐形式&#xff0c;受到了广大用户的热烈追捧。为了满足市场需求&#xff0c;许多企业和开发者开始关注淘宝扭蛋机小程序的源码搭建&#xf…