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

news2024/7/4 6:02:47

提示: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>

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

总结

踩坑路漫漫长@~@

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024最新在线工具箱/ 站长IT工具箱/网站系统源码下载

2024最新在线工具箱/ 站长IT工具箱/网站系统源码下载- 更多详情及下载地址请访问https://a5.org.cn/a5_ziyuan/39525.html 转载请注明出处!

Android Studio gradle-8.4 配置 GreenDao

1.配置项目下的build buildscript {repositories {mavenCentral()}dependencies {classpath ("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0")classpath ("org.greenrobot:greendao-gradle-plugin:3.3.1") // 使用最新版本} } 2.配置app下的build i…

泰坦尼克号幸存者数据分析

泰坦尼克号幸存者数据分析 1、泰坦尼克号数据集2、数据集加载与概览3、泰坦尼克号幸存者数据分析4、哪些人可能成为幸存者&#xff1f; 1、泰坦尼克号数据集 泰坦尼克号的沉没是世界上最严重的海难事故之一&#xff0c;造成了大量的人员伤亡。这是一艘号称当时世界上最大的邮轮…

深入浅出 -- 系统架构之分布式CAP理论和BASE理论

科技进步离不开理论支撑&#xff0c;而当下大行其道的分布式架构&#xff0c;透过繁荣昌盛表象&#xff0c;底层同样离不开诸多分布式理论撑持。当然&#xff0c;相信诸位在学习分布式相关技术时&#xff0c;必然学到过两个分布式领域中的基础理论&#xff0c;即&#xff1a;CA…

Java零基础入门-递归

一、概述 上一期&#xff0c;我们是具体学习了File类的一些概念基础知识点&#xff0c;以及对于该类的常用方法进行了一个全量举例演示&#xff0c;这也是考虑到有的小伙伴在阅读的同时&#xff0c;没有时间去实际测试&#xff0c;所以我也就顺带的给大家去做了实例演示&#x…

自动驾驶涉及相关的技术

当科幻走进现实&#xff0c;当影视照进生活&#xff0c;无数次憧憬的自动驾驶&#xff0c;正在慢慢的梦想成真。小时候天马星空的想象&#xff0c;现在正悄无声息的改变着我们的生活。随着汽车电动化进程的加快&#xff0c;自动驾驶技术映入眼帘&#xff0c;很多人可能感觉遥不…

软考 系统架构设计师系列知识点之数据库基本概念(1)

所属章节&#xff1a; 第6章. 数据库设计基础知识 第1节 数据库基本概念 数据&#xff08;Data&#xff09;是描述事务的符号记录&#xff0c;它具有多种表现形式&#xff0c;可以是文字、图形、图像、声音和语言等。信息&#xff08;Information&#xff09;是现实世界事物的…

使用SVD将图像压缩四分之一(MATLAB)

SVD压缩前后数据量减少的原因在于&#xff0c;通过奇异值分解&#xff08;SVD&#xff09;&#xff0c;我们将原始数据&#xff08;如图像&#xff09;转换成了一种更加紧凑的表示形式。这种转换依赖于数据内部的结构和相关性&#xff0c;以及数据中信息的不均匀分布。 让我们…

10-用PySpark建立第一个Spark RDD

目录 RDD概念RDD特点建立RDD的方式不同工具建立RDD的方式使用PySpark Shell(交互环境)建立RDD使用VSCode编程建立RDD使用Jupyter Notebook建立RDD 总结 PySpark实战笔记系列第一篇 RDD概念 Apache Spark的核心组件的基础是RDD。所谓的RDD&#xff0c;即弹性分布式数据集&#…

FL Studio 21.2.2官方中文破解版2024年最新图文安装激活教程 FL Studio 21免费激活

FL Studio 21.2.2官方中文破解版&#xff0c;中文别名水果编曲软件&#xff0c;是一款全能的音乐制作软件&#xff0c;包括编曲、录音、剪辑和混音等诸多功能&#xff0c;让你的电脑编程一个全能的录音室&#xff0c;它为您提供了一个集成的开发环境&#xff0c;使用起来非常简…

【stm32】I2C通信外设

【stm32】I2C通信外设 概念部分 如果简单应用&#xff0c;选择软件I2C。如果对性能指标要求比较高 选择硬件I2C 有硬件电路自动反转引脚电平&#xff0c;软件只需要写入控制寄存器CR和数据寄存器DR 为了实时监控时序的状态&#xff0c;还要读取状态寄存器SR 写入控制寄存器CR…

06-kafka及异步通知文章上下架

kafka及异步通知文章上下架 1)自媒体文章上下架 需求分析 2)kafka概述 消息中间件对比 特性ActiveMQRabbitMQRocketMQKafka开发语言javaerlangjavascala单机吞吐量万级万级10万级100万级时效性msusmsms级以内可用性高&#xff08;主从&#xff09;高&#xff08;主从&#…

【数据库】MySQL InnoDB存储引擎详解 - 读书笔记

MySQL InnoDB存储引擎详解 - 读书笔记 InnoDB 存储引擎概述InnoDB 存储引擎的版本InnoDB 体系架构内存缓冲池LRU List、Free List 和 Flush List重做日志缓冲&#xff08;redo log buffer&#xff09;额外的内存池 存储结构表空间系统表空间独立表空间通用表空间undo表空间临时…

力扣---反转链表 II ***

给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], left 2, right 4 输出&#xff1a;[1,4,3,…

【QT入门】 Qt代码创建布局综合运用:仿写腾讯会议登陆界面

往期回顾&#xff1a; 【QT入门】 Qt代码创建布局之水平布局、竖直布局详解-CSDN博客 【QT入门】 Qt代码创建布局之栅格布局详解-CSDN博客 【QT入门】 Qt代码创建布局之分裂器布局详解-CSDN博客 【QT入门】 Qt代码创建布局综合运用&#xff1a;仿写腾讯会议登陆界面 一、界面分…

rust 面向对象编程特性、模式与模式匹配、高级特征

面向对象编程OOP 学习了结构体、枚举&#xff0c;它们可以包含自定义数据字段&#xff0c;也可以定义内部方法&#xff0c;它们提供了与对象相同的功能。 面向对象的四大特征&#xff1a;封装、继承、多态 通过pub标记为公有的结构体&#xff0c;在其他模块中可以访问使用这…

redis主从复制与哨兵模式

redis主从复制 Redis主从复制&#xff08;Redis replication&#xff09;是Redis提供的一种数据备份和故障转移机制。通过主从复制&#xff0c;可以将一个Redis服务器&#xff08;主节点&#xff09;的数据复制到一个或多个Redis服务器&#xff08;从节点&#xff09;。这样做…

算法设计与分析实验报告java实现(排序算法、三壶谜题、交替放置的碟子、带锁的门)

一、 实验目的 1&#xff0e;加深学生对算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 1、排序算法…

ctf_show笔记篇(web入门---jwt)

目录 jwt简介 web345&#xff1a; web346&#xff1a; web347&#xff1a; web348: web349&#xff1a; web350&#xff1a; jwt简介 JSON Web Token&#xff08;JWT&#xff09;通常由三部分组成 Header&#xff08;头部&#xff09;&#xff1a;包含了两部分信息&…

使用 Clickhouse 集成的表引擎同步数据方式详解

Clickhouse作为一个列式存储分析型数据库&#xff0c;提供了很多集成其他组件的表引擎数据同步方案。 官网介绍 一 Kafka 表引擎 使用Clickhouse集成的Kafka表引擎消费Kafka写入Clickhouse表中。 1.1 流程图 1.2 建表 根据上面的流程图需要建立三张表&#xff0c;分别Click…