繁体   English   中英

如何绘制不透明的画布尾线

[英]How to draw canvas trailing line with opacity

我正在尝试在此画布动画中绘制具有尾随不透明度的旋转线,但它不起作用。 我已经在矩形和圆弧中看到了这种效果,但从未在直线上看到过,因此我不确定需要添加什么。

function radians(degrees) {
  return degrees * (Math.PI / 180);
}

var timer = 0;

function sonar() {
  var canvas = document.getElementById('sonar');
  if (canvas) {
    var ctx = canvas.getContext('2d');
    var cx = innerWidth / 2,
        cy = innerHeight / 2;

    canvas.width = innerWidth;
    canvas.height = innerHeight;

    //ctx.clearRect(0, 0, innerWidth, innerHeight);
    ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
    ctx.fillRect(0, 0, innerWidth, innerHeight);

    var radii = [cy, cy - 30, innerHeight / 3.33, innerHeight / 6.67];

    for (var a = 0; a < 4; a++) {
      ctx.beginPath();
      ctx.arc(cx, cy, radii[a], radians(0), radians(360), false);
      ctx.strokeStyle = 'limegreen';
      ctx.stroke();
      ctx.closePath();
    }

    // draw grid lines
    for (var i = 0; i < 12; i++) {
      var x = cx + cy * Math.cos(radians(i * 30));
      var y = cy + cy * Math.sin(radians(i * 30));
      ctx.beginPath();
      ctx.moveTo(cx, cy);
      ctx.lineTo(x, y);
      ctx.lineCap = 'round';
      ctx.strokeStyle = 'rgba(50, 205, 50, 0.45)';
      ctx.stroke();
      ctx.closePath();
    }

    if (timer <= 360) {
      timer++;
      ctx.beginPath();
      ctx.fillstyle = 'limegreen';
      ctx.moveTo(cx, cy);
      ctx.lineTo(cx + cy * Math.cos(radians(timer)), cy + cy * Math.sin(radians(timer)));
      ctx.strokeStyle = 'limegreen';
      ctx.stroke();
      ctx.closePath();
    } else {
      timer = 0;
    }
    requestAnimationFrame(sonar);
  }
}
sonar();

jsbin示例

这有两种方法可以实现:使用渐变和添加半透明线。

旁注,您应该尝试只重绘需要重绘的内容。 我分开了画布,将一个放在另一个之上,这样我们就不会一直重新绘制网格。

 function radians(degrees) { return degrees * (Math.PI / 180); } var timer = 0; function trail() { var canvas = document.getElementById('trail'); var ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, innerWidth, innerHeight); var cx = innerWidth / 2, cy = innerHeight / 2; canvas.width = innerWidth; canvas.height = innerHeight; if (timer <= 360) { timer++; ctx.beginPath(); ctx.fillstyle = 'limegreen'; ctx.moveTo(cx, cy); ctx.arc(cx,cy,cy,radians(timer-30),radians(timer)); ctx.lineTo(cx + cy * Math.cos(radians(timer)), cy + cy * Math.sin(radians(timer))); var gradient = ctx.createLinearGradient( cx+cy*Math.cos(radians(timer)), cy+cy*Math.sin(radians(timer)), cx+cy*0.9*Math.cos(radians(timer-30)), cy+cy*0.9*Math.sin(radians(timer-30))); gradient.addColorStop(0,'limegreen'); gradient.addColorStop(1,'transparent'); ctx.strokeStyle='transparent'; ctx.fillStyle = gradient; ctx.fill(); ctx.beginPath(); var fade = 10; for(var i =0;i<fade;i++) { ctx.moveTo(cx, cy); ctx.lineTo(cx+cy*Math.cos(radians(180+timer-i*1.3)),cy+cy*Math.sin(radians(180+timer-i*1.3))); ctx.strokeStyle ="rgba(50,205,50,0.1)"; ctx.lineWidth=5; ctx.closePath(); ctx.stroke(); } } else { timer = 0; } requestAnimationFrame(trail); } function sonar() { var canvas = document.getElementById('sonar'); if (canvas) { var ctx = canvas.getContext('2d'); var cx = innerWidth / 2, cy = innerHeight / 2; canvas.width = innerWidth; canvas.height = innerHeight; //ctx.clearRect(0, 0, innerWidth, innerHeight); var radii = [cy, cy - 30, innerHeight / 3.33, innerHeight / 6.67]; for (var a = 0; a < 4; a++) { ctx.beginPath(); ctx.arc(cx, cy, radii[a], radians(0), radians(360), false); ctx.strokeStyle = 'limegreen'; ctx.stroke(); ctx.closePath(); } // draw grid lines for (var i = 0; i < 12; i++) { var x = cx + cy * Math.cos(radians(i * 30)); var y = cy + cy * Math.sin(radians(i * 30)); ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(x, y); ctx.lineCap = 'round'; ctx.strokeStyle = 'rgba(50, 205, 50, 0.45)'; ctx.stroke(); ctx.closePath(); } } } sonar(); trail(); 
 canvas{ position: absolute; } 
 <canvas id=sonar></canvas> <canvas id=trail></canvas> 

问题是要获得这种效果,您需要沿着弧线绘制具有渐变的三角形,而不能在画布中做到这一点。 渐变必须是线性或径向的。

另一个选择是,每次要绘制扫除器时,都要运行一个内循环,然后从扫除器线向后移动 ,每次绘制时的不透明度都会略微降低。 但是可以说,您希望扫描范围为15度-显然,如果在d处有100%的不透明度线,而在d - 15处有5%的不透明度线,那是没有用的。 因此,开始填充更多的线条,并增加更多的线条...您将不得不绘制许多线条以使其看起来似乎充满了您的性能。

我的建议-您不必在每一帧都重新绘制它。 我只需要制作一个看起来像您想要的PNG,然后将其放置并在每个帧的中心绕其旋转即可。 然后,无需一直重绘它。 这将比画一条线快得多。

画布堆栈轨迹。

以下是如何使用一堆画布创建拖尾效果的快速演示。

您在屏幕上有一个普通画布(此FX不会起作用),然后是跟踪FX的一堆画布。 您将每一帧移到堆栈中的下一个画布,首先将其稍稍清除,然后在其上绘制要拖移的内容。 然后,渲染该画布及其上方的一个画布。

需要牢记的一点是,这些路径也可以具有很大的FX范围,例如模糊(每次渲染时只渲染每个帧堆栈本身略有偏移),放大和缩小路径。 上方或下方的小径。 您可以更改步距,等等。

这是过度杀伤,但过度杀伤很有趣。

演示上方的滑块控制路径长度。 代码也需要babel,因为我没有时间为ES5编写代码。

顶部滑块是跟踪量,其下一个是跟踪距离。 Trail dist过渡不顺利。 对于那个很抱歉。

 //============================================================================== // helper function function $(query,q1){ if(q1 !== undefined){ if(typeof query === "string"){ var e = document.createElement(query); if(typeof q1 !== "string"){ for(var i in q1){ e[i] = q1[i]; } }else{ e.id = q1; } return e; } return [...query.querySelectorAll(q1)]; } return [...document.querySelectorAll(query)]; } function $$(element,e1){ if(e1 !== undefined){ if(typeof element === "string"){ $(element)[0].appendChild(e1); return e1; } element.appendChild(e1); return e1; } document.body.appendChild(element); return element; } function $E(element,types,listener){ if(typeof types === "string"){ types = types.split(","); } element = $(element)[0]; types.forEach(t=>{ element.addEventListener(t,listener) }); return element; } function R(I){ if(I === undefined){ return Math.random(); } return Math.floor(Math.random()*I); } //============================================================================== //============================================================================== // answer code // canvas size const size = 512; const trailDist = 10; // There is this many canvases so be careful var trailDistCurrent = 10; // distance between trails var clearAll = false; // create a range slider for trail fade $$($("input",{type:"range",width : size, min:0, max:100, step:0.1, value:50, id:"trail-amount",title:"Trail amount"})); $("#trail-amount")[0].style.width = size + "px"; $E("#trail-amount","change,mousemove",function(e){fadeAmount = Math.pow(this.value / 100,2);}); // create a range slider trail distance $$($("input",{type:"range",width : size, min:2, max:trailDist , step:1, value:trailDist , id:"trail-dist",title:"Trail seperation"})); $("#trail-dist")[0].style.width = size + "px"; $E("#trail-dist","change,mousemove", function(e){ if(this.value !== trailDistCurrent){ trailDistCurrent= this.value; clearAll = true; } }); $$($("br","")) // put canvas under the slider // Main canvas var canvas; $$(canvas = $("canvas",{width:size,height:size})); // Not jquery. Just creates a canvas // and adds canvas to the document var ctx = canvas.getContext("2d"); // Trailing canvas var trailCanvases=[]; var i =0; // create trail canvas while(i++ < trailDist){trailCanvases.push($("canvas",{width:size,height:size}));} var ctxT = trailCanvases.map(c=>c.getContext("2d")); // get context var topCanvas = 0; var fadeAmount = 0.5; // Draw a shape function drawShape(ctx,shape){ ctx.lineWidth = shape.width; ctx.lineJoin = "round"; ctx.strokeStyle = shape.color; ctx.setTransform(shape.scale,0,0,shape.scale,shape.x,shape.y); ctx.rotate(shape.rot); ctx.beginPath(); var i = 0; ctx.moveTo(shape.shape[i++],shape.shape[i++]); while(i < shape.shape.length){ ctx.lineTo(shape.shape[i++],shape.shape[i++]); } ctx.stroke(); } // Create some random shapes var shapes = (function(){ function createRandomShape(){ var s = []; var len = Math.floor(Math.random()*5 +4)*2; while(len--){ s[s.length] = (R() + R()) * 20 * (R() < 0.5 ? -1 : 1); } return s; } var ss = []; var i = 10; while(i--){ ss[ss.length] = createRandomShape(); } ss[ss.length] = [0,0,300,0]; // create single line return ss; })(); // Create some random poits to move the shapes var points = (function(){ function point(){ return { color : "hsl("+R(360)+",100%,50%)", shape : shapes[R(shapes.length)], width : R(4)+1, x : R(size), y : R(size), scaleMax : R()*0.2 + 1, scale : 1, s : 0, rot : R()*Math.PI * 2, dr : R()*0.2 -0.1, dx : R()*2 - 1, dy : R()*2 - 1, ds : R() *0.02 + 0.01, } } var line = shapes.pop(); var ss = []; var i = 5; while(i--){ ss[ss.length] = point(); } var s = ss.pop(); s.color = "#0F0"; sx = sy = size /2; s.dx = s.dy = s.ds = 0; s.scaleMax = 0.5; s.dr = 0.02; s.shape = line; s.width = 6; ss.push(s); return ss; })(); var frameCount = 0; // used to do increamental fades for long trails function update(){ // to fix the trail distance problem when fade is low and distance high if(clearAll){ ctxT.forEach(c=>{ c.setTransform(1,0,0,1,0,0); c.clearRect(0,0,size,size); }); clearAll = false; } frameCount += 1; // get the next canvas that the shapes are drawn to. topCanvas += 1; topCanvas %= trailDistCurrent; var ctxTop = ctxT[topCanvas]; // clear the main canvas ctx.setTransform(1,0,0,1,0,0); // reset transforms // Fade the trail canvas ctxTop.setTransform(1,0,0,1,0,0); ctx.clearRect(0,0,size,size); // clear main canvas // slowly blendout trailing layer if(fadeAmount < 0.1){ // fading much less than this leaves perminant trails // so at low levels just reduce how often the fade is done if(((Math.floor(frameCount/trailDistCurrent)+topCanvas) % Math.ceil(1 / (fadeAmount * 10))) === 0 ){ ctxTop.globalAlpha = 0.1; ctxTop.globalCompositeOperation = "destination-out"; ctxTop.fillRect(0,0,size,size); } }else{ ctxTop.globalAlpha = fadeAmount; ctxTop.globalCompositeOperation = "destination-out"; ctxTop.fillRect(0,0,size,size); } ctxTop.globalCompositeOperation = "source-over"; ctxTop.globalAlpha = 1; // draw shapes for(var i = 0; i < points.length; i ++){ var p = points[i]; px += p.dx; // move the point py += p.dy; p.rot += p.dr; ps += p.ds; p.dr += Math.sin(ps) * 0.001; p.scale = Math.sin(ps) * p.scaleMax+1; px = ((px % size) + size) % size; py = ((py % size) + size) % size; drawShape(ctxTop,p); // draw trailing layer (middle) } // draw the trail the most distance from the current position ctx.drawImage(trailCanvases[(topCanvas + 1)%trailDistCurrent],0,0); // do it all again. requestAnimationFrame(update); } update(); 

暂无
暂无

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

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