简体   繁体   English

绘制时的JavaScript画布消失

[英]JavaScript Canvas on draw vanishes

I have a canvas function which draws a square if I click on the canvas field and move the mouse, that works so far. 我有一个canvas函数,如果我单击canvas字段并移动鼠标,它会绘制一个正方形,目前为止可以使用。

My Problem is that if I release the mouse and click at the canvas again the old drawn rectangle vanishes. 我的问题是,如果我松开鼠标并再次单击画布,则旧绘制的矩形将消失。

How do I make it possible that the old drawn does not get vanished. 我如何使旧画不消失。

My function: 我的功能:

function foo() {

    var tool = this;
    this.started = false;

    var canvasx = canvas.offsetLeft;
    var canvasy = canvas.offsetTop;
    var last_mousex = 0;
    var last_mousey = 0;
    var mousex = 0;
    var mousey = 0;

    this.mousedown = function (ev) {
        if(checkboxSquare.checked) {
            last_mousex = parseInt(ev.clientX-canvasx);
            last_mousey = parseInt(ev.clientY-canvasy);
            context.strokeStyle = $('#selectColor').val();
            context.lineWidth = $('#selectWidth').val();
            tool.started = true;
        }
    };

    this.mousemove = function (ev) {
        if (tool.started && checkboxSquare.checked) {
            mousex = parseInt(ev.clientX-canvasx);
            mousey = parseInt(ev.clientY-canvasy);
            context.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
            context.beginPath();
            var width = mousex-last_mousex;
            var height = mousey-last_mousey;
            context.rect(last_mousex,last_mousey,width,height);
            context.stroke();
        }
    };

    this.mouseup = function (ev) {
        if (tool.started && checkboxSquare.checked) {
            tool.mousemove(ev);
            tool.started = false;
        }
    };
}

It Looks something like this: http://jsfiddle.net/AbdiasSoftware/kqW4X/ 看起来像这样: http : //jsfiddle.net/AbdiasSoftware/kqW4X/

The old drawn rectangle vanishes on click because, you are clearing the entire canvas each time before drawing a rectangle. 旧的绘制矩形在单击时消失,因为每次绘制矩形之前您都要清除整个画布。

The easiest workaround would be to save the entire canvas as an image on mouseup and draw that image before drawing each rectangle. 最简单的解决方法是将整个画布另存为mouseup上的图像,并在绘制每个矩形之前绘制该图像。

 var canvas; var _foo = new foo(); canvas.onmousedown = _foo.mousedown; canvas.onmousemove= _foo.mousemove; canvas.onmouseup = _foo.mouseup; function foo() { canvas = $('#canvas')[0]; var context = canvas.getContext('2d'); var checkboxSquare = $('#checkboxSquare')[0]; var img = new Image(); var tool = this; this.started = false; var last_mousex = 0; var last_mousey = 0; var mousex = 0; var mousey = 0; this.mousedown = function (ev) { if(checkboxSquare.checked) { last_mousex = ev.offsetX; last_mousey = ev.offsetY; context.strokeStyle = $('#selectColor').val(); context.lineWidth = $('#selectWidth').val(); tool.started = true; } }; this.mousemove = function (ev) { if (tool.started && checkboxSquare.checked) { mousex = ev.offsetX; mousey = ev.offsetY; context.clearRect(0, 0, canvas.width, canvas.height); // clear canvas context.drawImage(img, 0, 0); // draw saved canvas (image) context.beginPath(); var width = mousex-last_mousex; var height = mousey-last_mousey; context.rect(last_mousex,last_mousey,width,height); context.stroke(); } }; this.mouseup = function (ev) { if (tool.started && checkboxSquare.checked) { tool.mousemove(ev); img.src = canvas.toDataURL(); // save canvas as image tool.started = false; } }; } 
 canvas { border: 1px solid black; cursor: default; margin-top: 5px } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <input type="checkbox" id="checkboxSquare">Square | Color <select id="selectColor"> <option value="red">red</option> <option value="green">green</option> <option value="blue">blue</option> </select> | Width <select id="selectWidth"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <canvas id="canvas" width="400" height="400"></canvas> 

Just create a background canvas same as the main canvas. 只需创建与主画布相同的背景画布即可。 When you drag out a new box, first draw the background canvas (with all the past boxes) on the main canvas then the current box being drawn. 拖出新框时,首先在主画布上绘制背景画布(包括所有过去的框),然后绘制当前框。 When you finish dragging the box, just daw it to the background canvas. 完成拖动框后,只需将其拖到背景画布上即可。

 const canvas = document.createElement("canvas"); const background = document.createElement("canvas"); canvas.style.border="2px solid black"; canvas.style.cursor = "crosshair"; background.width = canvas.width = innerWidth - 24; background.height = canvas.height = innerHeight - 24; const ctx = canvas.getContext("2d"); background.ctx = background.getContext("2d"); document.body.appendChild(canvas); const bounds = canvas.getBoundingClientRect(); var currentBox; const boxStyle = { fillStyle : "#4aF", strokeStyle : "black", lineWidth : 3, lineJoin : "round", } const mouse = { x : 0, y : 0,button : false, changed : false }; ["mousemove","mousedown","mouseup"].forEach(en => document.addEventListener(en, mouseEvent)); function createBox(x,y,w,h,style){ return {x,y,w,h,style,draw : drawBox} } function drawBox(ctx){ setStyle(ctx, this.style); ctx.beginPath(); ctx.rect(this.x,this.y,this.w,this.h); ctx.fill(); ctx.stroke(); } function setStyle(ctx, style){ Object.keys(style).forEach(key => ctx[key] = style[key]) } function mouseEvent(event) { mouse.x = event.pageX - bounds.left - scrollX; mouse.y = event.pageY - bounds.top - scrollY; if(event.type === "mousedown"){ mouse.button = true } else if(event.type === "mouseup"){ mouse.button = false } mouse.changed = true; } function mainLoop(){ var b = currentBox; // alias for readability if(mouse.changed){ if(mouse.button){ if(!b){ b = currentBox = createBox(mouse.x,mouse.y,0,0,boxStyle); }else{ bw = mouse.x - bx; bh = mouse.y - by; } }else if(b){ b.draw(background.ctx); b = currentBox = undefined; } if(b){ ctx.clearRect(0,0,canvas.width,canvas.height); ctx.drawImage(background,0,0); b.draw(ctx); canvas.style.cursor = "none"; }else{ canvas.style.cursor = "crosshair"; } mouse.changed = false; } requestAnimationFrame(mainLoop); } requestAnimationFrame(mainLoop); 

Extra Note. 额外说明。 Capture the mouse using the Document 使用文档捕获鼠标

When you create canvas drawing apps you should listen to the document mouse events rather than the canvas. 创建画布绘图应用程序时,您应该侦听文档鼠标事件而不是画布。 When the mouse button is down the mouse is captured and will continue to send mouse events while the mouse is down, even if you have moved off the canvas, document, or event outside the browser window. 按下鼠标按钮时,即使您已将画布,文档或事件移出浏览器窗口,也将捕获鼠标并在按下鼠标时继续发送鼠标事件。

This means you can drag content of the canvas and not worry about losing the mouseup event. 这意味着您可以拖动画布的内容,而不必担心丢失mouseup事件。

Burn some time. 燃烧一些时间。

I have some time to burn so will extend the demo above to include selecting and moving existing boxes. 我有一些时间要花费,所以将扩展上面的演示以包括选择和移动现有框。 Draw boxes as normal. 像往常一样画盒子。 Mouse over boxes will highlight them, click to select them. 将鼠标悬停在框上会突出显示它们,单击以选中它们。 When selected can be dragged. 选中后可以拖动。 Uses the same method background image to hold old boxes. 使用相同的方法背景图片保存旧盒子。 But have added a box list to hold old boxes A more extensive example 但是增加了一个盒子列表来容纳旧盒子一个更广泛的例子

  const canvas = document.createElement("canvas"); const background = document.createElement("canvas"); canvas.style.border="2px solid black"; canvas.style.cursor = "crosshair"; background.width = canvas.width = innerWidth - 24; background.height = canvas.height = innerHeight - 24; const ctx = canvas.getContext("2d"); background.ctx = background.getContext("2d"); document.body.appendChild(canvas); const bounds = canvas.getBoundingClientRect(); var currentBox; var selectedBox; var mouseOverBox; const styles = { box : { fillStyle : "#4aF", strokeStyle : "black", lineWidth : 3, lineJoin : "round", }, highlight : { strokeStyle : "white", lineWidth : 1, lineJoin : "round", setLineDash : [[10,10]], }, selected : { strokeStyle : "red", lineWidth : 2, lineJoin : "round", setLineDash : [[5,5]], }, } const boxes = { items : [], add(box){ // add a box and fix width and height to positive if(box.w < 0){ box.x += box.w; box.w = -box.w; } if(box.h < 0){ box.y += box.h; box.h = -box.h; } boxes.items.push(box) }, apply(name, ...args){ for(var i = 0; i < boxes.items.length; i ++ ){ boxes.items[i][name](...args); } }, }; const mouse = { x : 0, y : 0,button : false, changed : false }; ["mousemove","mousedown","mouseup"].forEach(en => document.addEventListener(en, mouseEvent)); const boxBehaviours = { draw(ctx, style = this.style){ if(!this.hide){ setStyle(ctx, style); ctx.beginPath(); ctx.rect(this.x,this.y,this.w,this.h); if(style.fillStyle) { ctx.fill() } if(style.strokeStyle) {ctx.stroke() } } }, isPointOver(x,y){ var b = this; if(x >= bx && x < bx + bw && y >= by && y < by + bh){ b.mouseOver = true; boxBehaviours.topMouseBox = b; }else { b.mouseOver =false; } }, } function createBox(x,y,w,h,style){ return {x,y,w,h,style, ...boxBehaviours}; } function setStyle(ctx, style){ Object.keys(style).forEach(key => { if(typeof ctx[key] === "function"){ ctx[key](...style[key]); }else{ ctx[key] = style[key]; } }) } function mouseEvent(event) { mouse.x = event.pageX - bounds.left - scrollX; mouse.y = event.pageY - bounds.top - scrollY; if(event.type === "mousedown"){ mouse.button = true } else if(event.type === "mouseup"){ mouse.button = false } } function redrawBackground(){ background.ctx.clearRect(0,0,canvas.width,canvas.height) boxes.apply("draw",background.ctx); } function mainLoop(time){ var b = currentBox; // alias for readability var mob = mouseOverBox; // alias for readability var sb = selectedBox; // alias for readability // first check mouse button. If button down could be // dragging a selected box or creating a new box if(mouse.button){ if(sb){ // is selected box if(!mouse.drag){ // start the drag mouse.drag = {x : mouse.x - sb.x, y : mouse.y - sb.y} }else{ // move the box sb.x = mouse.x- mouse.drag.x; sb.y = mouse.y- mouse.drag.y; } }else{ // else muse be create (or select click) if(!b){ b = currentBox = createBox(mouse.x,mouse.y,0,0,styles.box); }else{ bw = mouse.x - bx; bh = mouse.y - by; } } }else if(b || sb){ // mouse up and there is a box if(sb){ // if selected box if(mouse.drag){ // is dragging then drop it mouse.drag = undefined; sb.hide = false; redrawBackground(); sb = selectedBox = undefined; } // is the mouse is down and has not moved over 2 pixels // and there is a mob (mouseOverBox) under it // then dump the new box and select the mob box }else if(Math.abs(bw) < 2 && Math.abs(bh) < 2 && mob){ sb = selectedBox = mob; mob = mouseOverBox = undefined; b = currentBox = undefined; sb.hide = true; redrawBackground(); }else{ // just a normal box add it to box array // draw it and remove it from currentBox boxes.add(b); b.draw(background.ctx); b = currentBox = undefined; } } // clear andf draw background ctx.clearRect(0,0,canvas.width,canvas.height); ctx.drawImage(background,0,0); if(b){ // is there a current box then draw that b.draw(ctx); canvas.style.cursor = "none"; } else { // no current box so // find any boxes under the mouse boxBehaviours.topMouseBox = null; boxes.apply("isPointOver",mouse.x, mouse.y); // is there a selected box (sb) if(sb){ // yes selected box then draw it ctx.save(); styles.selected.lineDashOffset = time / 25; sb.hide = false; sb.draw(ctx,styles.selected); sb.hide = true; ctx.restore(); canvas.style.cursor = "move"; // no selected box sp then just high light the box under the // mouse and assign it to mouseOverBox (mob); }else if(boxBehaviours.topMouseBox){ mob = mouseOverBox = boxBehaviours.topMouseBox; ctx.save(); styles.highlight.lineDashOffset = time / 20; mob.draw(ctx, styles.highlight); ctx.restore(); canvas.style.cursor = "pointer"; }else{ canvas.style.cursor = "crosshair"; } } requestAnimationFrame(mainLoop); } requestAnimationFrame(mainLoop); 

I'm assuming that the foo() function is being called for every frame, either through setInterval or requestAnimationFrame . 我假设通过setIntervalrequestAnimationFrame为每个帧调用foo()函数。 If my assumption is right, the reason why your previously drawn square disappears is because you are only storing the x and y coordinates of one rectangle, and every time you click on the canvas again, it gets overwritten by the new values for the new rectangle. 如果我的假设是正确的,那么先前绘制的正方形消失的原因是因为您仅存储一个矩形的x和y坐标,并且每次再次单击画布时,它都会被新矩形的新值覆盖。

To solve your problem, you should store the x and y coordinates as well as the dimensions of the square on mouseup. 要解决您的问题,应在mouseup上存储x和y坐标以及正方形的尺寸。 These coordinates can be stored in an array. 这些坐标可以存储在数组中。

var squares = [];

this.mouseup = function (ev) {
    // other code
    var square = { 
        x: last_mousex,
        y: last_mousey,
        width: mousex - last_mousex,
        height: mousey - last_mousey
    };
    squares.push(square);
};

Now every time you draw the square, draw the squares stored in the squares array first. 现在,每次绘制正方形时,请先绘制squares数组中存储的squares

this.mousemove = function (ev) {
    if (tool.started && checkboxSquare.checked) {
        // other code
        context.clearRect(0, 0, canvas.width, canvas.height); // clear canvas

        // draw each square in the squares array after clearning the canvas
        squares.forEach(function(square) {
            context.beginPath();
            context.rect(square.x, square.y, square.width, square.height);
        });

        context.beginPath();
        var width = mousex - last_mousex;
        var height = mousey - last_mousey;
        context.rect(last_mousex, last_mousey, width, height);
        context.stroke();
    }
};

You'll see some code repetitions in drawing the squares, it's a good opportunity to abstract it into a separate function. 您将在绘制正方形时看到一些代码重复,这是将其抽象为单独函数的好机会。

 var point = []; var clicks = 0; var sketch = document.querySelector('#sketch'); var sketch_style = getComputedStyle(sketch); // Creating a tmp canvas var tmp_canvas = document.createElement('canvas'); var tmp_ctx = tmp_canvas.getContext('2d'); tmp_canvas.id = 'tmp_canvas'; tmp_canvas.width = parseInt(sketch_style.getPropertyValue('width')); tmp_canvas.height = parseInt(sketch_style.getPropertyValue('height')); sketch.appendChild(tmp_canvas); var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); canvas.id = 'paint'; canvas.width = parseInt(sketch_style.getPropertyValue('width')); canvas.height = parseInt(sketch_style.getPropertyValue('height')); sketch.appendChild(canvas); tmp_canvas.addEventListener('mousedown', mousedown, false); tmp_canvas.addEventListener('mousemove', mousemove, false); tmp_canvas.addEventListener('mouseup', mouseup, false); function mousemove(e) { if (clicks == 1) { x = e.layerX - this.offsetLeft; y = e.layerY - this.offsetTop; showRect(x, y); } } function showRect(x, y) { tmp_ctx.clearRect(0, 0, canvas.width, canvas.height); // clear canvas tmp_ctx.beginPath(); var width = x - point[0].x; var height = y - point[0].y; tmp_ctx.rect(point[0].x, point[0].y, width, height); tmp_ctx.stroke(); } function mousedown(e) { x = e.layerX - this.offsetLeft; y = e.layerY - this.offsetTop; point.push({ x, y }); clicks++; }; function mouseup() { context.drawImage(tmp_canvas, 0, 0); clicks = 0; point.length = 0; } 
 html, body { width: 100% ; height: 100% ; } #sketch { border: 10px solid gray; height: 100% ; position: relative; } #tmp_canvas { position: absolute; left: 0px; right: 0; bottom: 0; top: 0; cursor: crosshair; } 
 <html> <head> <meta charset="utf-8"> </head> <body> <div id="sketch"> </div> </body> </html> 

Try to do in temporary canvas and redraw all in main. 尝试在临时画布中进行绘制,然后在主画布中全部重绘。

jsfiddle:- https://jsfiddle.net/durga598/v0m06faz/ 的jsfiddle: - https://jsfiddle.net/durga598/v0m06faz/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM