如何使用不透明度绘制画布尾随线

时间:2016-10-26 18:40:37

标签: javascript canvas

我正试图在此画布动画中绘制旋转线,但尾部不透明但它不起作用。我已经看到了矩形和弧形的这种效果,但从来没有一条线,所以我不确定我需要添加什么。

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 example

3 个答案:

答案 0 :(得分:2)

以下两种方法:使用渐变和添加半透明线条。

Sidenote,您应该尝试重绘您需要重绘的内容。我将画布分开并将一个放在另一个上面,这样我们就不会一直重绘网格。

&#13;
&#13;
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();
&#13;
canvas{
position: absolute;
  }
&#13;
<canvas id=sonar></canvas>
<canvas id=trail></canvas>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

问题是要获得此效果,您需要绘制一个沿弧形渐变的三角形,而不能在画布中执行此操作。梯度必须是线性或径向的。

另一种选择是每次想要绘制清扫车时都运行一个内循环,然后从清扫车行向后向后,每次都会略微减少不透明度。但是让我们说你希望你的扫描覆盖15度 - 显然,如果你在d有100%的不透明度线和d - 15有5%的不透明度线,那就不行了。所以开始填充更多的线条和更多的线条......你将不得不画出这么多的线条以使它看起来充满你的表现可能会受到影响。

我的建议 - 你不应该在每一帧上重绘它。我只想制作一个看起来像你想要的PNG,然后放置它,然后围绕每个框架的中心旋转它。不需要一直重绘它。这比绘制一堆线要快得多。

答案 2 :(得分:0)

Canvas堆栈路径。

下面是如何使用一叠画布创建尾随效果的快速演示。

你有一个普通的屏幕画布(这个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";
    s.x = s.y = 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];
        p.x += p.dx;  // move the point
        p.y += p.dy;
        p.rot += p.dr;
        p.s += p.ds;
        p.dr += Math.sin(p.s) * 0.001;
        p.scale = Math.sin(p.s) * p.scaleMax+1;
        p.x = ((p.x % size) + size) % size;
        p.y = ((p.y % 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();