簡體   English   中英

畫布:了解 globalCompositeOperation 以擦除圖像疊加的一部分

[英]Canvas: Understanding globalCompositeOperation to erase part of an image overlay

我想換我周圍的頭globalCompositeOperation試圖通過這兩個例子組合屬性:的jsfiddleCodepen

前者使用destination-out ,后者使用source-over 是否可以在 Codepen 中使用火熱的光標,但也可以刪除用戶單擊的覆蓋填充部分,就像在 Fiddle 中一樣?

任何幫助將不勝感激。 如有必要,我可以結合 Codepen 上的演示來使用相同的方法。

相關小提琴代碼:

function drawDot(mouseX,mouseY){
    bridgeCanvas.beginPath();
    bridgeCanvas.arc(mouseX, mouseY, brushRadius, 0, 2*Math.PI, true);
    bridgeCanvas.fillStyle = '#000';
    bridgeCanvas.globalCompositeOperation = "destination-out";
    bridgeCanvas.fill();
}

相關 Codepen 代碼:

Fire.prototype.clearCanvas = function(){
    this.ctx.globalCompositeOperation = "source-over";
    this.ctx.fillStyle = "rgba( 15, 5, 2, 1 )";
    this.ctx.fillRect( 0, 0, window.innerWidth, window.innerHeight );

    this.ctx.globalCompositeOperation = "lighter";
    this.ctx.rect(0, 0, this.canvas.width, this.canvas.height);
    this.ctx.fillStyle = this.pattern;
    this.ctx.fill();/**/
}    

正如我在評論中所說,您必須將代碼至少分為兩部分。
cropper 函數使用"destination-out"合成操作來移除畫布上應該繪制新像素的已繪制像素。 在您的版本中,它使用背景圖像,一旦刪除了前景像素,您就可以在畫布的現在透明區域中看到此背景。

另一方面,火焰使用“更輕”、 "color-dodge""soft-light"混合操作。 這將添加已有像素和新繪制像素的顏色。
至少第一個,如果在透明區域上使用,將與默認的"source-over"復合操作相同。 因此,您需要將背景圖像繪制到畫布上才能在混合中使用它。

為此,您必須使用第二個屏幕外畫布,您將只在其中應用橡皮擦"destination-out"操作。 然后,在可見畫布上,在每個新的橡皮擦框架上,您必須在可見畫布上繪制背景圖像,然后是橡皮擦,帶有孔,最后是混合,這將混合在一起。

這是一個快速的代碼轉儲,在那里我重寫了橡皮擦,並修改了 Fire 橡皮擦,以使我們的主函數處理事件和動畫循環。

 function MainDrawing(){ this.canvas = document.getElementById('main'); this.ctx = this.canvas.getContext('2d'); this.background = new Image(); this.background.src = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/calgary-bridge-1943.jpg" this.eraser = new Eraser(this.canvas); this.fire = new Fire(this.canvas); this.attachEvents(); } MainDrawing.prototype = { anim: function(){ if(this.stopped) return; this.ctx.globalCompositeOperation = 'source-over'; this.ctx.drawImage(this.background, 0,0); this.ctx.drawImage(this.eraser.canvas, 0,0); this.fire.run(); requestAnimationFrame(this.anim.bind(this)); }, stop: function(){ this.stopped = true; }, attachEvents: function(){ var mouseDown = false; this.canvas.onmousedown = function(){ mouseDown = true; }; this.canvas.onmouseup = function(){ mouseDown = false; }; this.canvas.onmousemove = function(e){ if(mouseDown){ this.eraser.handleClick(e); } this.fire.updateMouse(e); }.bind(this); } }; function Eraser(canvas){ this.main = canvas; this.canvas = canvas.cloneNode(); var ctx = this.ctx = this.canvas.getContext('2d'); this.img = new Image(); this.img.onload = function(){ ctx.drawImage(this, 0, 0); ctx.globalCompositeOperation = 'destination-out'; }; this.img.src = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/calgary-bridge-2013.jpg"; this.getRect(); } Eraser.prototype = { getRect: function(){ this.rect = this.main.getBoundingClientRect(); }, handleClick: function(evt){ var x = evt.clientX - this.rect.left; var y = evt.clientY - this.rect.top; this.draw(x,y); }, draw: function(x, y){ this.ctx.beginPath(); this.ctx.arc(x, y, 30, 0, Math.PI*2); this.ctx.fill(); } }; var Fire = function(canvas){ this.canvas = canvas; this.ctx = this.canvas.getContext('2d'); this.aFires = []; this.aSpark = []; this.aSpark2 = []; this.mouse = { x : this.canvas.width * .5, y : this.canvas.height * .75, } } Fire.prototype.run = function(){ this.update(); this.draw(); } Fire.prototype.start = function(){ this.bRuning = true; this.run(); } Fire.prototype.stop = function(){ this.bRuning = false; } Fire.prototype.update = function(){ this.aFires.push( new Flame( this.mouse ) ); this.aSpark.push( new Spark( this.mouse ) ); this.aSpark2.push( new Spark( this.mouse ) ); for (var i = this.aFires.length - 1; i >= 0; i--) { if( this.aFires[i].alive ) this.aFires[i].update(); else this.aFires.splice( i, 1 ); } for (var i = this.aSpark.length - 1; i >= 0; i--) { if( this.aSpark[i].alive ) this.aSpark[i].update(); else this.aSpark.splice( i, 1 ); } for (var i = this.aSpark2.length - 1; i >= 0; i--) { if( this.aSpark2[i].alive ) this.aSpark2[i].update(); else this.aSpark2.splice( i, 1 ); } } Fire.prototype.draw = function(){ this.drawHalo(); this.ctx.globalCompositeOperation = "overlay";//or lighter or soft-light for (var i = this.aFires.length - 1; i >= 0; i--) { this.aFires[i].draw( this.ctx ); } this.ctx.globalCompositeOperation = "soft-light";//"soft-light";//"color-dodge"; for (var i = this.aSpark.length - 1; i >= 0; i--) { if( ( i % 2 ) === 0 ) this.aSpark[i].draw( this.ctx ); } this.ctx.globalCompositeOperation = "color-dodge";//"soft-light";//"color-dodge"; for (var i = this.aSpark2.length - 1; i >= 0; i--) { this.aSpark2[i].draw( this.ctx ); } } Fire.prototype.updateMouse = function( e ){ this.mouse.x = e.clientX; this.mouse.y = e.clientY; } Fire.prototype.drawHalo = function(){ var r = rand( 300, 350 ); this.ctx.globalCompositeOperation = "lighter"; this.grd = this.ctx.createRadialGradient( this.mouse.x, this.mouse.y,r,this.mouse.x, this.mouse.y, 0 ); this.grd.addColorStop(0,"transparent"); this.grd.addColorStop(1,"rgb( 50, 2, 0 )"); this.ctx.beginPath(); this.ctx.arc( this.mouse.x, this.mouse.y - 100, r, 0, 2*Math.PI ); this.ctx.fillStyle= this.grd; this.ctx.fill(); } var Flame = function( mouse ){ this.cx = mouse.x; this.cy = mouse.y; this.x = rand( this.cx - 25, this.cx + 25); this.y = rand( this.cy - 5, this.cy + 5); this.lx = this.x; this.ly = this.y; this.vy = rand( 1, 3 ); this.vx = rand( -1, 1 ); this.r = rand( 30, 40 ); this.life = rand( 2, 7 ); this.alive = true; this.c = { h : Math.floor( rand( 2, 40) ), s : 100, l : rand( 80, 100 ), a : 0, ta : rand( 0.8, 0.9 ) } } Flame.prototype.update = function() { this.lx = this.x; this.ly = this.y; this.y -= this.vy; this.vy += 0.08; this.x += this.vx; if( this.x < this.cx ) this.vx += 0.2; else this.vx -= 0.2; if( this.r > 0 ) this.r -= 0.3; if( this.r <= 0 ) this.r = 0; this.life -= 0.12; if( this.life <= 0 ){ this.ca -= 0.05; if( this.ca <= 0 ) this.alive = false; }else if( this.life > 0 && this.ca < this.c.ta ){ this.ca += .08; } } Flame.prototype.draw = function( ctx ){ this.grd1 = ctx.createRadialGradient( this.x, this.y, this.r*3, this.x, this.y, 0 ); this.grd1.addColorStop( 0.5, "hsla( " + this.ch + ", " + this.cs + "%, " + this.cl + "%, " + (this.ca/20) + ")" ); this.grd1.addColorStop( 0, "transparent" ); this.grd2 = ctx.createRadialGradient( this.x, this.y, this.r, this.x, this.y, 0 ); this.grd2.addColorStop( 0.5, "hsla( " + this.ch + ", " + this.cs + "%, " + this.cl + "%, " + this.ca + ")" ); this.grd2.addColorStop( 0, "transparent" ); ctx.beginPath(); ctx.arc( this.x, this.y, this.r * 3, 0, 2*Math.PI ); ctx.fillStyle = this.grd1; //ctx.fillStyle = "hsla( " + this.ch + ", " + this.cs + "%, " + this.cl + "%, " + (this.ca/20) + ")"; ctx.fill(); ctx.globalCompositeOperation = "overlay"; ctx.beginPath(); ctx.arc( this.x, this.y, this.r, 0, 2*Math.PI ); ctx.fillStyle = this.grd2; ctx.fill(); ctx.beginPath(); ctx.moveTo( this.lx , this.ly); ctx.lineTo( this.x, this.y); ctx.strokeStyle = "hsla( " + this.ch + ", " + this.cs + "%, " + this.cl + "%, 1)"; ctx.lineWidth = rand( 1, 2 ); ctx.stroke(); ctx.closePath(); } var Spark = function( mouse ){ this.cx = mouse.x; this.cy = mouse.y; this.x = rand( this.cx -40, this.cx + 40); this.y = rand( this.cy, this.cy + 5); this.lx = this.x; this.ly = this.y; this.vy = rand( 1, 3 ); this.vx = rand( -4, 4 ); this.r = rand( 0, 1 ); this.life = rand( 4, 8 ); this.alive = true; this.c = { h : Math.floor( rand( 2, 40) ), s : 100, l : rand( 40, 100 ), a : rand( 0.8, 0.9 ) } } Spark.prototype.update = function() { this.lx = this.x; this.ly = this.y; this.y -= this.vy; this.x += this.vx; if( this.x < this.cx ) this.vx += 0.2; else this.vx -= 0.2; this.vy += 0.08; this.life -= 0.1; if( this.life <= 0 ){ this.ca -= 0.05; if( this.ca <= 0 ) this.alive = false; } } Spark.prototype.draw = function( ctx ){ ctx.beginPath(); ctx.moveTo( this.lx , this.ly); ctx.lineTo( this.x, this.y); ctx.strokeStyle = "hsla( " + this.ch + ", " + this.cs + "%, " + this.cl + "%, " + (this.ca / 2) + ")"; ctx.lineWidth = this.r * 2; ctx.lineCap = 'round'; ctx.stroke(); ctx.closePath(); ctx.beginPath(); ctx.moveTo( this.lx , this.ly); ctx.lineTo( this.x, this.y); ctx.strokeStyle = "hsla( " + this.ch + ", " + this.cs + "%, " + this.cl + "%, " + this.ca + ")"; ctx.lineWidth = this.r; ctx.stroke(); ctx.closePath(); } rand = function( min, max ){ return Math.random() * ( max - min) + min; }; var app = new MainDrawing(); app.anim();
 <canvas id="main" width="750" height="465"></canvas>

暫無
暫無

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

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