【Canvas与艺术】移动光源照亮墙上的字

【关键点】

使字变亮是将rgba中的alpha值提升的结果,反之即将文字变暗;

光源照亮文字是靠判断文字与光源间的距离决定的,离光源越近,其alpha值就越大。

【效果】

【代码】

<!DOCTYPE html>
<html lang="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<head>
     <title>移动光源照亮墙上的字</title>
     </head>

     <body onload="draw()">
        <canvas id="myCanvas" width="100px" height="100px" style="border:0px dashed black;">
            出现文字表示您的浏览器不支持HTML5
        </canvas>
     </body>
</html>
<script type="text/javascript">
<!--
/*****************************************************************
* 将全体代码(从<!DOCTYPE到script>)拷贝下来,粘贴到文本编辑器中,
* 另存为.html文件,再用chrome浏览器打开,就能看到实现效果。
******************************************************************/

// 常量画布宽
const Width=1200;

// 常量画布高
const Height=512;

// 文本字体大小
const FontSize=18;

// 绘图上下文
var context;

// 舞台对象
var stage;

// 肇始函数
function draw(){
    // 初始化canvas
    var canvas=document.getElementById('myCanvas');    
    canvas.width=Width;
    canvas.height=Height;    
    context=canvas.getContext('2d');    
    
    // 准备舞台
    stage=new Stage();    
    stage.init();

    // 开幕
    animate();
};

// 循环播放动画
function animate(){    
    stage.update();
    stage.paintBg(context);
    stage.paintFg(context);     

    if(true){
        // 延时执行以免下坠太快
        const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
 
        delay(90).then(() => {
            window.requestAnimationFrame(animate);
        });        
    }
}

// 舞台类
function Stage(){
    // 此数组存储字位置,内容,透明度
    this.letters=new Array();

    // 光源
    this.light={};
    
    // 初始化粒子
    this.init=function(){
        var colCnt = Math.floor(Width / FontSize) ;// 列数
        var rowCnt = Math.floor(Height/FontSize);  // 行数

        for(let i = 0;i<colCnt ; i++){
            for(let j = 0;j<rowCnt ; j++){
            
                var item={};

                // x就是所在列
                item.x=i*FontSize;
                // y取随机数
                item.y=j*FontSize;    
                // 取随机文字
                item.text=getText();
                // 透明度取最大
                item.alpha=1.0;
                
                this.letters.push(item);
            }
        }

        // 光源初始位置
        this.light.x=0;
        this.light.y=Height/2+100;
    }

    // 更新
    this.update=function(){
        this.letters.forEach(function (item, i) {
            // 按字到光源的距离设定不同的透明度
            let distence=Math.sqrt((item.x-stage.light.x)*(item.x-stage.light.x)+(item.y-stage.light.y)*(item.y-stage.light.y));

            if(distence<25){
                item.alpha=1;
            }else if(distence<50){
                item.alpha=0.5;
            }else if(distence<100){
                item.alpha=0.25;
            }else{
                item.alpha=0;
            }

            // 让字的透明度自然滑落,以达到字渐渐隐落的效果
            if(item.alpha<=0){
                item.alpha=0;
            }else{
                item.alpha-=0.05;
            }
        });
        
        // 用正弦曲线去改变光源位置
        this.light.x+=5;
        this.light.y+=Math.sin(this.light.x*10)*30;

        if(this.light.x>Width){
            this.light.x=0;
            this.light.y=Height/2+100;
        }
    };

    //  画背景
    this.paintBg=function(ctx){
        // ctx.fillStyle="black"; // 如此只是单字被照亮
        ctx.fillStyle = "rgba(0, 0, 0, 0.07)";// 加上半透明蒙层,字和光源便出现了残影效果
        
        ctx.fillRect(0,0,Width,Height);

        // 作者标识
        ctx.fillStyle="white";
        ctx.font="12px Arial";
        ctx.fillText("光源照字特效 by:逆火",Width-160,Height-15);
    };

    // 画前景
    this.paintFg=function(ctx){
        // 画被照亮的字
        this.letters.forEach(function (item, i) {
            ctx.fillStyle = "rgba(0, 219, 0, "+item.alpha+")";
            ctx.font = FontSize + "px consolas";

            ctx.fillText(item.text,item.x, item.y);
        })    

        // 画圆表示光源
        ctx.fillStyle="#ff0";
        ctx.beginPath();
        ctx.arc(this.light.x,this.light.y,20,0,Math.PI*2,true);
        ctx.closePath();
        ctx.fill();
    };
}

// 取显示的文字
function getText(){
    var sentence="甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥乾☷坤☵坎☲离☳震☶艮☴巽☱兑";
    var n=sentence.length;
    var i=Math.random()*n;
    return sentence.charAt(i);
}

/*----------------------------------
这世上有的地方,
因为不断鞭挞黑暗而获得光明;
也有的地方,
因为总是粉饰黑暗,
而堕入了更深的深渊。
----------------------------------*/
//-->
</script>

END

posted @ 2017-09-25 11:44  逆火狂飙  阅读(784)  评论(1编辑  收藏  举报
生当作人杰 死亦为鬼雄 至今思项羽 不肯过江东