簡體   English   中英

繪制時的JavaScript畫布消失

[英]JavaScript Canvas on draw vanishes

我有一個canvas函數,如果我單擊canvas字段並移動鼠標,它會繪制一個正方形,目前為止可以使用。

我的問題是,如果我松開鼠標並再次單擊畫布,則舊繪制的矩形將消失。

我如何使舊畫不消失。

我的功能:

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

看起來像這樣: http : //jsfiddle.net/AbdiasSoftware/kqW4X/

舊的繪制矩形在單擊時消失,因為每次繪制矩形之前您都要清除整個畫布。

最簡單的解決方法是將整個畫布另存為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> 

只需創建與主畫布相同的背景畫布即可。 拖出新框時,首先在主畫布上繪制背景畫布(包括所有過去的框),然后繪制當前框。 完成拖動框后,只需將其拖到背景畫布上即可。

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

額外說明。 使用文檔捕獲鼠標

創建畫布繪圖應用程序時,您應該偵聽文檔鼠標事件而不是畫布。 按下鼠標按鈕時,即使您已將畫布,文檔或事件移出瀏覽器窗口,也將捕獲鼠標並在按下鼠標時繼續發送鼠標事件。

這意味着您可以拖動畫布的內容,而不必擔心丟失mouseup事件。

燃燒一些時間。

我有一些時間要花費,所以將擴展上面的演示以包括選擇和移動現有框。 像往常一樣畫盒子。 將鼠標懸停在框上會突出顯示它們,單擊以選中它們。 選中后可以拖動。 使用相同的方法背景圖片保存舊盒子。 但是增加了一個盒子列表來容納舊盒子一個更廣泛的例子

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

我假設通過setIntervalrequestAnimationFrame為每個幀調用foo()函數。 如果我的假設是正確的,那么先前繪制的正方形消失的原因是因為您僅存儲一個矩形的x和y坐標,並且每次再次單擊畫布時,它都會被新矩形的新值覆蓋。

要解決您的問題,應在mouseup上存儲x和y坐標以及正方形的尺寸。 這些坐標可以存儲在數組中。

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);
};

現在,每次繪制正方形時,請先繪制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();
    }
};

您將在繪制正方形時看到一些代碼重復,這是將其抽象為單獨函數的好機會。

 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> 

嘗試在臨時畫布中進行繪制,然后在主畫布中全部重繪。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM