canvas如何实现鼠标绘制图形以及拖拽

发布时间 2023-04-12 10:25:21作者: 10后程序员劝退师
const shapes = [];
var animate

// 画布初始化
export const canvasInit = () => {
    const cvs = document.querySelector('canvas');
    const ctx = cvs.getContext('2d');
    const w = 500, h = 300;
    cvs.width = w * devicePixelRatio; 
    cvs.height = h * devicePixelRatio; // set canvas size
    cvs.style.width = w + 'px';         // add width and height to the style attribute of the html element
    cvs.style.height = h + 'px';         // add width and height to the style attribute of the html element

    cvs.onmousedown = (e) => {
        canvasDraw(ctx)

        const rect = cvs.getBoundingClientRect(); // get the dimensions of the canvas element.
        const clickX = e.clientX - rect.left; // get the x-coordinate of the mouse-click.
        const clickY = e.clientY - rect.top; // get the y-coordinate of the mouse-click.
        const shape  = getShape(clickX, clickY)
        if(shape){
            const { startX, startY, endX, endY } = shape
            window.onmousemove = (e) => {

                const disX = e.clientX - rect.left - clickX
                const disY = e.clientY - rect.top - clickY
                
                // 禁止移动到边界
                if(e.clientX - rect.left <= 0 || e.clientX - rect.left >= w || e.clientY - rect.top <= 0 || e.clientY - rect.top >= h) {
                    window.onmousemove = null; // stop the mouse-movement from triggering a callback function.
                    cancelAnimationFrame(animate)
                }

                shape.startX = startX + disX// set the startX value.
                shape.endX = endX + disX
                shape.startY = startY + disY
                shape.endY = endY + disY

                // shape.startX = (startX + disX) <= 0 ? 0 : (startX + disX) > w ? w: startX + disX; // set the startX value.
                // shape.endX = (endX + disX) > w ? w: (endX + disX) 
                // shape.startY = (startY + disY) < 0? 0: (startY + disY) 
                // shape.endY = (endY + disY) > h? h: (endY + disY)
                


            } // on mouse move event.

        }else {
            const shape = new Rectangle(ctx, 'blue', clickX, clickY); // create a rectangle with the specified color and dimensions and draw it.
            shapes.push(shape)
            window.onmousemove = (e) => {
                shape.endX = e.clientX - rect.left; // get the x-coordinate of the mouse-click.
                shape.endY = e.clientY - rect.top; // get the y-coordinate of the mouse-click.
            }
            
        }
        
        window.onmouseup = (e) => {
            console.log(shapes)
            window.onmousemove = null; // stop the mouse-movement from triggering a callback function.
            window.onmouseup = null; // stop the mouse-click from triggering a callback function.
            cancelAnimationFrame(animate)
        } // on mouse move, update the location of the shape.
    }
}

// 绘制方法
const canvasDraw = (ctx) => {
    animate = requestAnimationFrame(() => {
        canvasDraw(ctx)
    });

    ctx.clearRect(0, 0, canvas.width, canvas.height); // clear the canvas before starting to draw the lines.
    for (const s of shapes) {
        s.draw();                // draw the lines of the shape.
    }
}

// 获取当前鼠标点击到的图形
const getShape = (x, y) => {
    for(let i = shapes.length-1; i>=0; i--){
       const s = shapes[i]
       if(s.isInside(x, y)){
        return s
       }
    }
    return null
}

// 绘制矩形实例
class Rectangle {
    constructor(ctx, color, startX, startY){
        this.ctx = ctx;
        this.color = color;
        this.startX = startX;             // start x position of the rectangle
        this.startY = startY;             // start y position of the rectangle
        this.endX = startX
        this.endY = startY;
    }

    get minX(){
        return Math.min(this.startX, this.endX)
    }
    get maxX(){                 // get the minimum y position of the rectangle
        return Math.max(this.startX, this.endX)
    }
    get minY(){
        return Math.min(this.startY, this.endY)
    }
    get maxY(){                 // get the minimum y position of the rectangle
        return Math.max(this.startY, this.endY)
    }
    // 绘制矩形
    draw(){
        this.ctx.beginPath();             // start a new path
        this.ctx.moveTo(this.minX * devicePixelRatio, this.minY * devicePixelRatio)
        this.ctx.lineTo(this.maxX * devicePixelRatio, this.minY * devicePixelRatio)    // draw a line from the max x point of the rectangle to max y point
        this.ctx.lineTo(this.maxX * devicePixelRatio, this.maxY * devicePixelRatio)    // draw a line
        this.ctx.lineTo(this.minX * devicePixelRatio, this.maxY * devicePixelRatio)    // draw a line from the min x point of the rectangle to max y point
        this.ctx.lineTo(this.minX * devicePixelRatio, this.minY * devicePixelRatio);     // close the path of the rectangle
        this.ctx.fillStyle = this.color;        // set fill style of the path to the color specified in the constructor and draw the path in the
        this.ctx.fill();                // fill the path of the rectangle with the fill style specified in the constructor and draw the path in the ctx.
        this.ctx.lineCaps = 'square';        // set line caps of the path to round and draw the path in the ctx.
        this.ctx.strokeStyle = '#fff';    // set stroke style of the path to the color specified in the constructor and draw the path in the ctx.
        this.ctx.lineWidth = 3 * devicePixelRatio
        this.ctx.stroke()            // stroke the path of the rectangle with the stroke style specified in the constructor and draw the path in the ctx.
 
    }
    
    // 判断鼠标点击区域是否在绘制图形内
    isInside(x, y){
       return x >=this.minX && x<=this.maxX && y>=this.minY && y<=this.maxY ;

    }
}



export default Rectangle