2017-03-07 82 views
0

我的目的是繪製圓形內部的半圓形內部陰影,這些圓形形狀代表圍繞恆星運動的行星(這是我正在研究的一個教育計劃的一部分)。HTML畫布:圓形形狀(行星)的內部陰影

許多方法後,這是一個幾乎爲我工作:

  1. 繪製一個圓形(行星),並在其上,行程包含實際的陰影更大的圈子。

Each planet as a circle that will paint the shadow over it 2.使用該組合物的選項 「ctx.globalCompositeOperation = '源頂上';」吸取更大的圈子只會畫重疊的現有內容的部分:

Only the area that overlaps content will be painted

但問題是,任何地球將重疊任何陰影圈,所以,你可以看到,當一個行星與更大的陰影重疊時,它會變成完全黑暗。

有沒有什麼辦法讓它繪製特定內容(形狀)的重疊區域?

或者,你知道更好的方法來做到這一點嗎?請記住,我必須以從地球到光源的特定角度繪製陰影。

在此先感謝!

回答

2

試着畫的「影子」地球上像這樣之前調用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();
<canvas id="canvas" width="200" height="200"></canvas>

+0

也表現的很出色,非常感謝! –

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);

+0

這是一個很好的解決方案,我會嘗試將此方法應用於我的代碼,謝謝! –