櫻花的季節,教大家用canvas畫出飛舞的櫻花樹


又到了櫻花的季節,教大家使用canvas畫出飛舞的櫻花樹效果。

廢話少說,先看效果。

演示效果地址:http://suohb.com/work/tree4.htm

查看演示效果

櫻花效果

 

 

 

 

第一步,我們先畫出一棵樹的主體。

我畫樹的使用的原理是,定義一個起始點,從這個點開始,向一個角度移動一段距離。得到另一個點。

畫出一條線連接兩個點。

以新得到的點,依舊向這個角度,移動一段距離。得到第三個點,連寫第二第三個點。

以此類推。一定步長之后,就得到一條射線。

我們根據自然界中的真實樹的情況,這條線越來越細,直到最細地方結束。

<body bgcolor="#000000">
<canvas id="tree"></canvas>
<script>
var treeCanvas = document.getElementById("tree");
treeCanvas.width = window.innerWidth;
treeCanvas.height = window.innerHeight ;
var tCxt = treeCanvas.getContext("2d");
var rootTop = 450 ;//樹起始位置
var treeColor = "#FFF" ;//樹顏色

function drawTree(x,y,deg,step){
    var x1 = x + Math.cos(deg) * step ;//越細的枝干越短,所以以步長來做
    var y1 = y + Math.sin(deg) * step ;
    tCxt.beginPath();
    tCxt.lineWidth = step/3;//樹干越來越細
    tCxt.moveTo(x,y);
    tCxt.lineTo(x1,y1);
    tCxt.strokeStyle = treeColor ;
    tCxt.stroke();
    step -- ;
    if(step > 0){
        drawTree(x1,y1,deg,step);
    }
}

drawTree(treeCanvas.width/2,rootTop,-Math.PI/2,30);
</script>
</body>

 

 

 

樹干出來之后,要做分叉,每個分叉其實就是向另一個方向的樹干。

而且分叉要比主干細一些。我們在第二階段樹干位置,每三步向左右分叉。

if(step > 0){
        drawTree(x1,y1,deg,step);
        if(step%3 == 1)
            drawTree(x1,y1,deg+0.5 ,Math.round(step/1.13));//右分叉
        if(step%3 == 0)
            drawTree(x1,y1,deg-0.5, Math.round(step/1.13));//左分叉
    }

 

這樣一棵樹的主干基本上就已經完成了。

我們在樹的末端幾個節點,畫一個粉色的半透明的圓。當做櫻花。

為了保證所有櫻花不是千篇一律的一個角度,我們隨機一個起始角度。

function drawTree(x,y,deg,step){
    var x1 = x + Math.cos(deg) * step ;//越細的枝干越短,所以以步長來做
    var y1 = y + Math.sin(deg) * step ;
    tCxt.beginPath();
    tCxt.lineWidth = step/3;//樹干越來越細
    tCxt.moveTo(x,y);
    tCxt.lineTo(x1,y1);
    tCxt.strokeStyle = treeColor ;
    tCxt.stroke();
    if(step < 5 ){//在末端五個節點,畫一個半圓,作為櫻花效果
        var r = 2+Math.random()*2
        tCxt.fillStyle = flowerColor ;
        tCxt.arc(x1+Math.random()*3,y1+Math.random()*3,r,0,Math.PI) tCxt.fill(); }
    step -- ;
    if(step > 0){
        drawTree(x1,y1,deg,step);
        if(step%3 == 1)
            drawTree(x1,y1,deg+0.5 ,Math.round(step/1.13));//右分叉
        if(step%3 == 0)
            drawTree(x1,y1,deg-0.5, Math.round(step/1.13));//左分叉
    }
}

 

 

這個時候,如果沒有特別的要求的,基本算是已經完成了。

之后再要做的就是精益求精,對現在效果做出微調。

1、樹干都是直線,在計算下一個點的時候,做一些偏移。

2、櫻花樹形態比較扁平。給X軸方向上偏移稍微大點,Y軸稍微偏小一點。

3、至少做兩種花瓣顏色,一種稍微深一些,一種淺一些。

4、在新建一個canvas層,做一些飄落櫻花的效果。

5、在分叉,畫花瓣等地方多使用一些隨機數,樹形狀不能太單一

 

最終效果如下:

完整代碼如下:

<!doctype html>
<html>
<head>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no" />
<style type="text/css">
canvas{
    position: absolute;
    left: 0;
    top: 0;
}
</style>
</head>
<body bgcolor="#000000">
<canvas id="tree"></canvas>
<canvas id="flower"></canvas>
<script>
//兩個canvas
var tree = document.getElementById("tree");
tree.width = window.innerWidth;
tree.height = window.innerHeight ;
var tCxt = tree.getContext("2d");
var flower = document.getElementById("flower");
flower.width = window.innerWidth;
flower.height = window.innerHeight ;
var cxt = flower.getContext("2d");

var flowerList = [];//櫻花列表
var rootTop = 450 ;//樹起點
var flowerColor = "rgba(255,192,203,.3)" ;//花色
var flowerColorDeep = "rgba(241,158,194,.5)" ;//花色深
var treeColor2 = "rgba(255,192,203,.5)" ;//樹枝顏色
var treeColor = "#FFF" ;//樹干顏色
var fallList = [];//飄落櫻花列表
var g = 0.01 ;//重力加速度
var gWind = 0.005;//風力加速度
var limitSpeedY = 1;//速度上限
var limitSpeedX = 1 ;//速度上限

cxt.shadowColor= "#FFF" ;
cxt.shadowBlur = 10 ;

function drawTree(x,y,deg,step,type){
    var deg1 = step%2 == 0 ? 0.1 : -0.1 ;
    var x1 = x + Math.cos(deg+deg1) * (step+4) * 0.8 ;//以步長來判斷枝干長度 x軸偏移大一些
    var y1 = y + Math.sin(deg+deg1) * (step-1) * 0.8 ;//以步長來判斷枝干長度 Y軸壓縮一些
    tCxt.beginPath();
    tCxt.lineWidth = step/3;
    tCxt.moveTo(x,y);
    tCxt.lineTo(x1,y1);
    tCxt.strokeStyle = (step > 5 ) ? treeColor : treeColor2 ;//細紙條都換成花的顏色
    tCxt.stroke();
    if(step > 20){//樹干相交的位置有間隙,以一個圓填充
        tCxt.fillStyle = treeColor ;
        tCxt.arc(x,y,step/6,0,Math.PI*2);
        tCxt.fill();
    }
    if(step < 3 || (step < 23 && Math.random() > 0.1)){
        //末梢位置 畫花瓣
        var color = [flowerColorDeep,flowerColor,flowerColor][Math.round(Math.random()+0.2)] ;
        var r = 2+Math.random()*2
        tCxt.fillStyle = color ;
        tCxt.arc(x1+Math.random()*3,y1+Math.random()*3,r,0,Math.PI)
        tCxt.fill();
        flowerList.push({x:x,y:y,sx:(Math.random()-0.5),sy:0,color:color,r:r,deg:deg});//保存下畫櫻花的位置
    }
    step -- ;
    if(step > 0){
        drawTree(x1,y1,deg,step,type);
        if(step%3 == 1 && step > 0 && step < 30)
            drawTree(x1,y1,deg+0.2 + 0.3 * Math.random(),Math.round(step/1.13));//右分叉
        if(step%3 == 0 && step > 0 && step < 30)
            drawTree(x1,y1,deg-0.2 - 0.3 * Math.random(),Math.round(step/1.13));//左分叉
    }
}

drawTree(tree.width/2,rootTop,-Math.PI/2,30,1);//執行

var len = flowerList.length ;
function step(){
    if(Math.random() > 0.3)    fallList.push(flowerList[Math.floor(Math.random()*len)]);//隨機取出一個,花瓣復制到飄落花瓣的列表中

    cxt.clearRect(0,0,tree.width,tree.height);
    for(var i = 0 ;i < fallList.length ; i ++){
        if(fallList[i].sy < limitSpeedY) fallList[i].sy += g ;
        fallList[i].sx += gWind ;
        fallList[i].x += fallList[i].sx ;
        fallList[i].y += fallList[i].sy ;
        if(fallList[i].y > rootTop){//飄到樹根的花瓣移除
            fallList.splice(i,1);
            i -- ;
            continue ;
        }
        cxt.beginPath();
        cxt.fillStyle = fallList[i].color ;
        fallList[i].deg += fallList[i].sx*0.05 ;//跟速度相關的旋轉花瓣
        cxt.arc(fallList[i].x,fallList[i].y,fallList[i].r,fallList[i].deg,fallList[i].deg+Math.PI*1.3);
        cxt.fill();
    }
    requestAnimationFrame(step);
}
requestAnimationFrame(step);
</script>
</body>
</html>

查看演示效果

更多特效,請關注我的微信公眾號。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM