HTML Canvas:圆形(行星)的内阴影

时间:2017-03-07 19:19:19

标签: javascript canvas shadow globalcompositeoperation

我的目的是在圆形内部绘制半圆形内部阴影,这些形状代表围绕恒星运动的行星(这是我正在研究的教育计划的一部分)。

经过多次尝试后,这对我来说几乎起作用了:

  1. 绘制一个圆形(行星)并在其上方,划出一个包含实际阴影的较大圆圈。
  2. Each planet as a circle that will paint the shadow over it 2.使用合成选项“ctx.globalCompositeOperation ='source-atop';”要绘制更大的圆圈,它只会绘制与现有内容重叠的部分:

    Only the area that overlaps content will be painted

    但问题是任何行星会重叠任何阴影圈,因此,正如您所看到的,当行星与较大的阴影重叠时,它会变得完全黑暗。 / p>

    有没有办法让它绘制特定内容(形状)的重叠区域?

    或者,你知道更好的方法吗?请记住,我必须在从行星到光源的特定角度绘制阴影。

    提前致谢!

3 个答案:

答案 0 :(得分:2)

尝试在绘制" shadow"之前调用x = "GTCCTGGTTGTAGCTGAAGCTCTTCCC[A]CTCCTCCCGATCACTGGGACGTCCTATGT" #COUNT 'A' sapply(unlist(strsplit(x,"\\[[A-z]\\]")), function(a) length(unlist(gregexpr("A", a)))) # GTCCTGGTTGTAGCTGAAGCTCTTCCC CTCCTCCCGATCACTGGGACGTCCTATGT # 3 4 #COUNT 'G' sapply(unlist(strsplit(x,"\\[[A-z]\\]")), function(a) length(unlist(gregexpr("G", a)))) # GTCCTGGTTGTAGCTGAAGCTCTTCCC CTCCTCCCGATCACTGGGACGTCCTATGT # 7 6 方法(和相关代码)像这样的星球。



clip

const ctx = canvas.getContext("2d");

//draw planet
ctx.beginPath();
ctx.arc(100, 100, 80, 0, Math.PI*2);
ctx.fillStyle = "aqua";
ctx.fill();
//save non-clipped state.
ctx.save();
//clip range by planet area.
ctx.clip();
//draw shadow
ctx.beginPath();
ctx.arc(200, 200, 200, 0, Math.PI*2);
ctx.lineWidth = 100;
ctx.stroke();
//dispose clip range.
ctx.restore();




答案 1 :(得分:1)

预渲染阴影。

为您提供太阳能系统阴影的酷解决方案。

有些设备不喜欢渲染阴影,渲染过程中的所有遮罩操作都会消除您可能添加的任何其他FX。

做阴影的一种方法是在开始时为每个行星渲染阴影。掩盖它,使其完美地适合行星。在动画期间,只需绘制行星,然后旋转阴影图像以面向太阳并调用drawImage以获得与您相同的效果,并且快速多次。

实施例

函数createShadow为行星创建自定义阴影图像,并将其作为planet.shadow添加到行星对象。函数drawPlanet首先绘制行星,然后使用正常source-over合成在其上绘制阴影。



    var canvas = document.createElement("canvas");
    canvas.width = canvas.height = 1024;
    var ctx = canvas.getContext("2d");
    document.body.appendChild(canvas);
    
    const shadowImageSafeEdge = 2; // pixel safe border around shadow image
    const shadowBlur = 0.8; // fraction of planet radius
    var sun = {
        x : canvas.width /2,
        y : canvas.height / 2,
        radius : 80,
        color : "yellow",
    }
    var sunGrad = ctx.createRadialGradient(0, 0, sun.radius/4, 0, 0, sun.radius);
    sunGrad.addColorStop(0,"#FF7");
    sunGrad.addColorStop(0.6,"#FF4");
    sunGrad.addColorStop(0.8,"#FF0");
    sunGrad.addColorStop(1,"#DC0");
    sun.color = sunGrad;

    function rInt(min,max){
        return Math.floor((max-min) * Math.random() + min);
    }
    function randCol(hue){
        var col = "hsl(";
        col += Math.floor(hue + rInt(-30,30) + 360) % 360;
        col += ",";
        col += Math.floor(80 + rInt(-20,20) + 100) % 100;
        col += "%,";
        col += Math.floor(50 + rInt(-10,10) + 100) % 100;
        col += "%)";
        return col;
    }
    // creates a planet at orbit distance from sun    
    function createPlanet(orbit){
        var planet = {
            radius : Math.random() * 20 + 5,
            orbitDist : orbit,  // dist from sun
            orbitPos : Math.random() * Math.PI * 2,
            shadow : null,
        }
        planet.color = randCol(rInt(280, 360));
        planet.shadow = createShadow(planet);
        return planet;        
    }
    // creates a shadow image that fits the planet
    function createShadow(planet){    
        var r = planet.radius;
        var s = shadowImageSafeEdge;
        var planetShadow = document.createElement("canvas");
        planetShadow.width = planetShadow.height = r * s + s * 2; // a little room to stop hard edge if zooming
        var ctx = planetShadow.ctx = planetShadow.getContext("2d");
        ctx.shadowBlur = r * shadowBlur  ;
        ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
        ctx.lineWidth = r * 2 - r * (1 - shadowBlur / 2);
        ctx.strokeStyle = ctx.shadowColor = "rgba(0,0,0,1)";
        ctx.beginPath();
        ctx.arc(-planet.orbitDist - r,r + s, planet.orbitDist + r * 2 + r * (shadowBlur /0.85) + s, 0, Math.PI * 2);
        ctx.stroke();
        ctx.stroke();
        ctx.stroke();
        ctx.shadowColor = "rgba(0,0,0,0)";
        ctx.globalCompositeOperation = "destination-in";
        ctx.beginPath();
        ctx.arc(r + s, r + s, r, 0, Math.PI * 2);  // sun will be along x axis
        ctx.fill();
        ctx.globalCompositeOperation = "source-over";
        return planetShadow;
    }

    // draws the planet and the shadow
    function drawPlanet(planet){
         var xdx = Math.cos(planet.orbitPos); 
         var xdy = Math.sin(planet.orbitPos);
         var x = xdx * planet.orbitDist + sun.x;
         var y = xdy * planet.orbitDist + sun.y;
         ctx.setTransform(1,0,0,1,x,y);
         ctx.fillStyle = planet.color;
         ctx.beginPath();
         ctx.arc(0,0,planet.radius,0,Math.PI * 2);
         ctx.fill();

         // set transform so that shadow faces away from the sun
         ctx.globalAlpha = 0.8;
         ctx.setTransform(xdx,xdy,-xdy,xdx,x,y);
         ctx.drawImage(planet.shadow,-planet.radius - 2,-planet.radius - 2);
         ctx.globalAlpha =1;
     }
     // let you guess what this function does
     function drawSun(){
         ctx.fillStyle = sun.color;
         ctx.setTransform(1,0,0,1,sun.x,sun.y);
         ctx.beginPath();
         ctx.arc(0,0,sun.radius,0,Math.PI * 2);
         ctx.fill();
     }
     // array of planets and create them
     var planets = [];
     (function(){
        var i = 10;
        while(i-- >1){
            planets.push(
                createPlanet(
                   rInt( 60 + i * 40,i * 40 + 100)
                )
            );
        }  
     }());
     // gradient for background
     var backGrad = ctx.createRadialGradient(512, 512, sun.radius, 512, 512, Math.sqrt(512 * 512 * 2));
     backGrad.addColorStop(0,"#B9E");
     backGrad.addColorStop(0.025,"#96A");
     backGrad.addColorStop(1,"#624");

     // main render loop
     function render(time){
         ctx.setTransform(1,0,0,1,0,0); // reset transform
         ctx.fillStyle = backGrad;   
         ctx.fillRect(0,0,1024,1024);  // clear
         drawSun();  
         for(var i = 0; i < planets.length; i++){ // draw all planets
             planets[i].orbitPos += Math.sqrt(10 / Math.pow(planets[i].orbitDist, 2));
             drawPlanet(planets[i]);
         }
         requestAnimationFrame(render);
     }
     requestAnimationFrame(render);
&#13;
&#13;
&#13;

答案 2 :(得分:0)

我会用几个画布。我会保持一个主要的&#34;画布并将单个元素绘制到其他画布,然后将它们混合到主画布中。

此问题包含有关将一个画布与另一个画布混合的信息:Combining two or more Canvas elements with some sort of blending