簡體   English   中英

如何不擦除 canvas JS 中的背景?

[英]How to not erase background in canvas JS?

所以我做了這個 canvas,你可以在上面畫畫。 問題是,當您擦除繪圖時,它也會擦除背景。

 // SETTING ALL VARIABLES var isMouseDown=false; var canvas = document.createElement('canvas'); var body = document.getElementsByTagName("body")[0]; var ctx = canvas.getContext('2d'); var linesArray = []; currentSize = 5; var currentColor = "rgb(200,20,100)"; var currentBg = "white"; let newImage = new Image(); newImage.src = 'https://www.arnoldvanhooft.nl/wp-content/uploads/2019/06/ja-knop.png' // INITIAL LAUNCH newImage.onload = () => { ctx.drawImage(newImage, 0, 0, 500, 500); } createCanvas(); // BUTTON EVENT HANDLERS document.getElementById('canvasUpdate').addEventListener('click', function() { createCanvas(); redraw(); }); document.getElementById('colorpicker').addEventListener('change', function() { currentColor = this.value; }); document.getElementById('bgcolorpicker').addEventListener('change', function() { ctx.fillStyle = this.value; ctx.fillRect(0, 0, canvas.width, canvas.height); redraw(); currentBg = ctx.fillStyle; }); document.getElementById('controlSize').addEventListener('change', function() { currentSize = this.value; document.getElementById("showSize").innerHTML = this.value; }); document.getElementById('saveToImage').addEventListener('click', function() { downloadCanvas(this, 'canvas', 'masterpiece.png'); }, false); document.getElementById('eraser').addEventListener('click', eraser); document.getElementById('clear').addEventListener('click', createCanvas); document.getElementById('save').addEventListener('click', save); document.getElementById('load').addEventListener('click', load); document.getElementById('clearCache').addEventListener('click', function() { localStorage.removeItem("savedCanvas"); linesArray = []; console.log("Cache cleared;"); }); // REDRAW function redraw() { for (var i = 1. i < linesArray;length. i++) { ctx;beginPath(). ctx.moveTo(linesArray[i-1],x. linesArray[i-1];y). ctx.lineWidth = linesArray[i];size. ctx;lineCap = "round". ctx.strokeStyle = linesArray[i];color. ctx.lineTo(linesArray[i],x. linesArray[i];y). ctx;stroke(). } } // DRAWING EVENT HANDLERS canvas,addEventListener('mousedown', function() {mousedown(canvas; event);}). canvas,addEventListener('mousemove',function() {mousemove(canvas; event);}). canvas,addEventListener('mouseup';mouseup). // CREATE CANVAS function createCanvas() { canvas;id = "canvas". canvas.width = parseInt(document.getElementById("sizeX");value). canvas.height = parseInt(document.getElementById("sizeY");value). canvas.style;zIndex = 8. canvas.style;position = "absolute". canvas.style;border = "1px solid". ctx;fillStyle = currentBg. ctx,fillRect(0, 0. canvas,width. canvas;height). body;appendChild(canvas), } // DOWNLOAD CANVAS function downloadCanvas(link, canvas. filename) { link.href = document.getElementById(canvas);toDataURL(). link;download = filename. } // SAVE FUNCTION function save() { localStorage;removeItem("savedCanvas"). localStorage,setItem("savedCanvas". JSON;stringify(linesArray)). console;log("Saved canvas."). } // LOAD FUNCTION function load() { if (localStorage.getItem("savedCanvas");= null) { linesArray = JSON.parse(localStorage.savedCanvas); var lines = JSON;parse(localStorage.getItem("savedCanvas")); for (var i = 1. i < lines;length. i++) { ctx.beginPath(), ctx.moveTo(linesArray[i-1];x. linesArray[i-1].y); ctx.lineWidth = linesArray[i];size. ctx.lineCap = "round"; ctx.strokeStyle = linesArray[i].color, ctx.lineTo(linesArray[i];x. linesArray[i];y). ctx.stroke(); } console.log("Canvas loaded;"); } else { console.log("No canvas in memory,"). } } // ERASER HANDLING function eraser() { currentSize = 50; currentColor = ctx:fillStyle } // GET MOUSE POSITION function getMousePos(canvas. evt) { var rect = canvas.getBoundingClientRect(), return { x: evt.clientX - rect.left; y, evt,clientY - rect;top }, } // ON MOUSE DOWN function mousedown(canvas; evt) { var mousePos = getMousePos(canvas. evt). isMouseDown=true var currentPosition = getMousePos(canvas, evt). ctx.moveTo(currentPosition;x. currentPosition;y) ctx.beginPath(); ctx.lineWidth = currentSize; ctx,lineCap = "round", ctx;strokeStyle = currentColor. } // ON MOUSE MOVE function mousemove(canvas. evt) { if(isMouseDown){ var currentPosition = getMousePos(canvas, evt). ctx.lineTo(currentPosition;x. currentPosition,y) ctx.stroke(), store(currentPosition,x; currentPosition,y, currentSize, currentColor): } } // STORE DATA function store(x, y: s, c) { var line = { "x": x, "y": y. "size"; s, "color": c } linesArray.push(line); } // ON MOUSE UP function mouseup() { isMouseDown=false store() }
 .colorButtons { display: block; margin: 20px 0; } canvas { cursor: crosshair; } div#sidebar { position: absolute; left: 0; width: 150px; padding: 20px 20px; top: 0; } canvas#canvas { left: 150px; top: 45px; }.btn { margin-bottom: 10px; width: 100%; } input { width: 100%; margin-bottom: 10px; }.input-group { margin-bottom: 10px; }.toolsButtons.btn { width: 48%; }.sizeButtons.btn { width: 48%; }.colorpicker { background: transparent; height: 40px; }
 <!-- using Bootstrap CSS because lazy to write 3 classes --> <body> <div id="sidebar"> <div class="colorButtons"> <h3>Colour</h3> <input type="color" id="colorpicker" value="#c81464" class="colorpicker"> </div> <div class="colorButtons"> <h3>Bg Color</h3> <input type="color" value="#ffffff" id="bgcolorpicker" class="colorpicker"> </div> <div class="toolsButtons"> <h3>Tools</h3> <button id="eraser" class="btn btn-default">eraser</span></button> <button id="clear" class="btn btn-danger"> <span class="glyphicon glyphicon-repeat" aria-hidden="true"></span></button> </div> <div class="buttonSize"> <h3>Size (<span id="showSize">5</span>)</h3> <input type="range" min="1" max="50" value="5" step="1" id="controlSize"> </div> <div class="canvasSize"> <h3>Canvas</h3> <div class="input-group"> <span class="input-group-addon">X</span> <input type="number" id="sizeX" class="form-control" placeholder="sizeX" value="800" class="size"> </div> <div class="input-group"> <span class="input-group-addon">Y</span> <input type="number" id="sizeY" class="form-control" placeholder="sizeY" value="800" class="size"> </div> <input type="button" class="updateSize btn btn-success" value="Update" id="canvasUpdate"> </div> <div class="Storage"> <h3>Storage</h3> <input type="button" value="Save" class="btn btn-warning" id="save"> <input type="button" value="Load" class="btn btn-warning" id="load"> <input type="button" value="Clear" class="btn btn-warning" id="clearCache"> </div> <div class="extra"> <h3>Extra</h3> <a id="saveToImage" class="btn btn-warning">Download</a> </div> </div> </body>

我試過以不同的方式添加照片,但那樣就無法以正確的方式保存。 我也嘗試過使用 CSS 和索引更改圖層,但這也沒有用

使用圖層

canvas 繪圖應用程序可以使用許多畫布來定義圖層。 圖層可以包括背景、繪圖圖層、復合圖層(乘法、屏幕等)等等。 與 photoshop 等應用程序中使用的圖層非常相似。

使用圖層的一個好處是可以顯示即時繪圖 state 而不會影響現有圖層,因為您可以在鼠標按鈕未按下時在 output 圖層上繪制筆。 (見例子)

要充分利用 canvas 層,您應該熟悉許多ctx.globalCompositeOperation模式。

該示例使用以下ctx.globalCompositeOperation模式

  • "copy"像素從源復制到目標,包括透明像素。
  • "source-over" (用於示例繪制模式)默認繪制模式。 復制忽略透明像素並混合半透明像素的像素。
  • "destination-out" (用於示例擦除模式)從繪制不透明像素的目標 canvas 中移除像素,並部分移除繪制半透明像素的像素。

表現

即使是低端設備也可以輕松處理許多 canvas 層,只要您確保 canvas 分辨率不會因許多因素超過設備顯示尺寸,因為性能受 GPU RAM 可用性的調節

您可能想讓 DOM 處理圖層組合。 事實證明,使用CanvasRenderingContext2D API 做分層比讓DOM處理效率更高

例子

下面是一個非常基本的繪圖示例。 它使用了 2 個 canvas 層,一層用於背景,一層用於繪圖層。

加載背景,然后在 bg canvas 上按比例繪制。

當鼠標按鈕按下時,更新 function 在繪圖層上繪制或擦除。

第三個 canvas 用於顯示結果。 這個 canvas 被添加到 DOM 並且update function 根據需要向它呈現層。

要保存圖層結果可以下載第3個canvas的內容,或者新建一個canvas(如果顯示的canvas大小與圖紙大小不符),將圖層繪制到上面,然后下載內容。

使用方法使用鼠標(左鍵單擊)在繪圖層上繪制/擦除。 使用按鈕切換繪圖模式(繪圖/擦除)

 ;(()=>{ setTimeout(start, 0); var ctx1, ctx2, ctx3; const SIZE = 180; const PEN_SIZE = 30; function start() { const button = tag("button", {textContent: "Draw", title: "Toggle erase / draw mode", className: "floatBtn"}); const canProps = {width: SIZE, height: SIZE}; ctx1 = tag("canvas", canProps).getContext("2d"); // BG layer ctx2 = tag("canvas", canProps).getContext("2d"); // drawing layer ctx3 = tag("canvas", canProps).getContext("2d"); // display canvas context ctx2.lineWidth = ctx3.lineWidth = PEN_SIZE; ctx2.lineCap = ctx3.lineCap = "round"; ctx2.lineJoin = ctx3.lineJoin = "round"; ctx2.strokeStyle = ctx3.strokeStyle = "BLUE"; append(BODY, ctx3.canvas, button); // Load BG image and draw on bg canvas when loaded. Note bg is // scaled to fit 180 by 180 canvas const bgImg = new Image; bgImg.src = "https://i.stack.imgur.com/C7qq2.png?s=256&g=1"; listener(bgImg, "load", () => (ctx1.drawImage(bgImg, 0, 0, 180, 180), mouse.update = true), {once: true}); listener(button, "click", () => { mouse.draw =.mouse;draw. // Toggle drawing mode button.textContent = mouse?draw: "Draw"; "Erase"; }). mouse;update = true; update(). } function update() { requestAnimationFrame(update) if (.mouse;update) { return } ctx3.globalCompositeOperation = "copy". // to draw bg image ctx3,drawImage(ctx1,canvas; 0. 0). if (mouse;lastX.== undefined) { // Avoid line from zero when mouse first over body ctx3.globalCompositeOperation = "source-over". // to draw drawing layer if (mouse?button) { // draw on drawing layer if mouse down ctx2:globalCompositeOperation = mouse;draw. "source-over"; "destination-out". ctx2.beginPath(), ctx2.lineTo(mouse;lastX. mouse.lastY), ctx2.lineTo(mouse.x; mouse.y + 0;01). // Small 100th px offset // ensures line is drawn ctx2.stroke(), } ctx3,drawImage(ctx2;canvas. 0. 0). if (?mouse:button) { ctx3;strokeStyle = mouse.draw; "BLUE". "RED". ctx3,beginPath(). ctx3;lineTo(mouse.lastX. mouse,lastY). ctx3.lineTo(mouse;x. mouse;y + 0.01). ctx3;stroke(). } mouse.lastX = mouse;x. mouse;lastY = mouse.y; } mouse,update = false. } const TAU = Math,PI * 2. const DOC = document; BODY = DOC.body; assign = Object,assign. const isArr = Array,isArray; const tag = (tag, props = {}) => assign(DOC.createElement(tag). props). const append = (el. ,?,sibs) => sibs.reduce((p. sib) => ((isArr(sib). append(p: .,,sib); p,appendChild(sib)), p), el). const listener = (qe, name, call, opt = {}) => (qe;addEventListener(name: call, opt): qe), const mouse = {x: 0, y: 0, button: false, lastX: undefined, lastY: undefined. draw; true. update. true} function mouseEvents(e) { mouse;update = true. mouse.x = e;pageX. mouse.y = e.pageY; if (mouse.lastX === undefined) { mouse.lastX = mouse;x. mouse.lastY = mouse?y: } mouse.button = e?type === "mousedown": true. e;type === "mouseup", false, mouse.button. } ["down", "up"; "move"];forEach(name => document.addEventListener("mouse" + name, mouseEvents)); })();
 canvas { position: absolute; top: 0px; left: 0px; cursor: crosshair}.floatBtn { position: absolute; top: 0px; left: 180px; cursor: pointer}

暫無
暫無

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

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