制作炫酷的canvas动画作为banner背景

在标题处,制作Canvas动画作为背景。

1. 标题处图片背景

Next是响应式主题,变换网站宽度,可以看到,标题处背景最宽是991px,宽度始终是124px(我的按127px处理的)。制作背景图片的宽高比要计算好,比如背景图片的宽度是3964px,那么宽度是508px。背景图样式:

body {
    background-color: #EADEC7;
    background:url(/uploads/body-bg/sunset-glow.jpg);
    background-repeat: no-repeat;
    background-attachment:fixed;
    background-position:50% 50%;
    /* 保持图像的纵横比并将图像缩放成将完全覆盖背景定位区域的最小大小 */
    background-size: cover; 
}

这样就成功制作好了一个符合响应式的banner背景。

2. 标题处Canvas背景

Canvas不是背景图像,没有控制Canvas当背景图像的css语法,可以使用z-index控制Canvas元素成为背景图像。

2.1 使用Canvas技术绘制星空和飞碟

和动画片原理一样的,Canvas技术绘制一帧帧动画,然后不断渲染,造成动画效果。

function animate() {
    //绘制一帧图片
    context.clearRect(0,0,WINDOW_WIDTH,WINDOW_HEIGHT);
    for (var i in stars) {
        stars[i].draw();
    }
    ufo.draw();
    //循环绘制
    requestAnimationFrame(animate)
}

下面是全部代码。

var canvasImgLoaded = false;
var canvasImg = new Image;
canvasImg.onload = function() {
    canvasImgLoaded = true;
};
canvasImg.src = '/uploads/header-bg-bk/ufo.png';
    
//插入自定义canvas,兼容浏览器不支持canvas的情况
try {
    var context = document.createElement('canvas').getContext('2d');
    var canvasElement = $('<canvas id="site-meta-canvas"></canvas>');
    $(".site-meta").append(canvasElement);
    //$(".site-meta").css("background","none");移到了下面,画完canvas,再使得背景图消失
} catch(ex) {
    $(".site-meta").css("background","url(https://cdn.jsdelivr.net/gh/maplesugarr/[email protected]/imgs/header-bg.jpg)");
    $(".site-meta").css("background-position","50% 50%");
    $(".site-meta").css("background-size","auto 125px!important");
}
var WINDOW_WIDTH = 991;
var WINDOW_HEIGHT = 127;
var canvas = document.getElementById('site-meta-canvas');
canvas.width = WINDOW_WIDTH;
canvas.height = WINDOW_HEIGHT;
var context = canvas.getContext('2d');
var stars = [];
var starsNum = 100;
var ufo;
var ufoCanvasWidth = 991;
var firstLoad = true;
var firstLoadDelay = true;
setTimeout(function(){ firstLoadDelay = false; }, 3000);

function star() {
    this.index = stars.length;
    this.x = Math.round(Math.random()*WINDOW_WIDTH);
    this.y = Math.round(Math.random()*WINDOW_HEIGHT);
    this.r = Math.random()*3;
    this.valpha = Math.random()*0.05;
    this.alpha = Math.random();
    this.vx = Math.random()*0.1-0.05;
    this.vy = Math.random()*0.1-0.05;    
}

star.prototype.renew = function() {
    this.x = Math.round(Math.random()*WINDOW_WIDTH);
    this.y = 0;//Math.round(Math.random()*WINDOW_HEIGHT);
    this.alpha = Math.random();
}

star.prototype.draw = function () {
    //绘制流星
    if(this.index%10==1){
        if(this.x<0 || this.y>=WINDOW_HEIGHT){
            this.renew();
        }
        this.vx = -0.8;
        this.vy = 0.8;
        this.r = 3;
        
        var grd=context.createLinearGradient(this.x, this.y,this.x-20,this.y+20);
        grd.addColorStop(0,'rgba(255,255,255,0)');
        //grd.addColorStop(0.6,'rgba(255,255,255,'+this.alpha*0.5+')');
        grd.addColorStop(1,'rgba(255,255,255,'+this.alpha+')');
        context.strokeStyle = grd;//'rgba(255,255,255,'+this.alpha+')';
        
        context.beginPath();
        context.lineWidth = this.r;
        context.moveTo(this.x,this.y);
        context.lineTo(this.x-20,this.y+20);
        context.closePath();
        context.stroke();
        
        context.fillStyle = "rgba(255,255,255," + this.alpha + ")";
        //context.shadowColor = "white";
        //context.shadowBlur = this.r * 0.1;
        context.beginPath();
        context.arc(this.x-20,this.y+20, this.r*0.5, 0, 2 * Math.PI, false);
        context.closePath();
        context.fill();
        
        this.x += this.vx;
        this.y += this.vy;
        return;
    }
    //绘制普通星星
    //星星从亮变暗,再从暗变亮
    this.alpha += this.valpha;
    if(this.alpha<=0){
        this.alpha = 0;
        this.valpha = -this.valpha;
    }else if(this.alpha>1){
        this.alpha = 1;
        this.valpha = -this.valpha
    }
    //星星来回移动
    this.x += this.vx;
    if(this.x>=WINDOW_WIDTH){
        this.x = 0;
    }else if(this.x<0){
        this.x = WINDOW_WIDTH;
    }
    this.y += this.vy;
    if(this.y>=WINDOW_HEIGHT){
        this.y = 0;
    }else if(this.y<0){
        this.y = WINDOW_HEIGHT;
    }
    
    context.fillStyle = "rgba(255,255,255," + this.alpha + ")";
    context.shadowColor = "white";
    context.shadowBlur = this.r * 2;
    context.beginPath();
    context.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
    context.closePath();
    context.fill();
    /*
    //这种RadialGradient太耗性能
    var bg = context.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.r);
    bg.addColorStop(0,'rgba(255,255,255,'+this.alpha+')')
    bg.addColorStop(1,'rgba(255,255,255,0)')
    context.fillStyle  = bg;
    context.beginPath();
    console.log(this.x);
    context.arc(this.x,this.y, this.r, 0, Math.PI*2, false);
    context.closePath();
    context.fill();
    */
};

function ufo() {
    this.x = Math.round(Math.random()*WINDOW_WIDTH);
    this.y = Math.round(Math.random()*WINDOW_HEIGHT);
    this.vx = Math.random()*1-0.5;
    this.vy = Math.random()*1-0.5;
}

ufo.prototype.renew = function() {
    //ufo从画布外面飞来,简化成两种情况,ufo只从上下左右飞来
    if(Math.random()>0.15){
        this.x = Math.round(Math.random()*ufoCanvasWidth);
        if(Math.random()>0.5) {
            this.y = WINDOW_HEIGHT+15;
        }else {
            this.y = -15;
        }
    }else {
        this.y = Math.round(Math.random()*WINDOW_HEIGHT);
        if(Math.random()>0.5) {
            this.x = ufoCanvasWidth+15;
        }else {
            this.x = -15;
        }
    }
    this.vx = Math.random()*1-0.5;
    this.vy = Math.random()*1-0.5;
    if(this.y>0&&this.vy>0)this.vy=-this.vy;
    if(this.y<0&&this.vy<0)this.vy=-this.vy;
    if(this.x>0&&this.vx>0)this.vx=-this.vx;
    if(this.x<0&&this.vx<0)this.vx=-this.vx;
    //console.log("renew "+this.x + ' '+ this.y+' '+ ufoCanvasWidth);
}

ufo.prototype.draw = function() {
    if(!canvasImgLoaded || firstLoadDelay) return;
    //ufo飞出画布一段距离,才去更新
    if(this.x>=(ufoCanvasWidth+30) || this.x<-30){
        this.renew();
    }
    if(this.y>=(WINDOW_HEIGHT+30) || this.y<-30){
        this.renew();
    }
    if(firstLoad){
        firstLoad = false;
        this.x = -30;
        this.y = 30;
        this.vx = 0.3;
        this.vy = 0.1;
        $(".site-meta").css("background","none");
    }
    //第一次加载ufo时候,x超过120后加速,形成一个“v”字形动画
    if(this.vx == 0.3&&this.vy == 0.1&&this.x>100) this.vy=-0.2;
    context.save();
    context.shadowBlur=20;
    context.shadowColor="white";
    context.drawImage(canvasImg, this.x, this.y, 50, 15); 
    this.x += this.vx;
    this.y += this.vy;
    context.restore();
}
function animate() {
    context.clearRect(0,0,WINDOW_WIDTH,WINDOW_HEIGHT);
    for (var i in stars) {
        stars[i].draw();
    }
    ufo.draw();
    requestAnimationFrame(animate)
}
function initStarrySky() {
    for (var i = 0; i < starsNum; i++) {
        stars[i] = new star();
        stars[i].draw();
    }
    ufo = new ufo();
    animate();
}
initStarrySky();

上面的代码创建了一个id为“site-meta-canvas”的Canvas元素,在上面绘制除了星空和飞碟,然后插入到“.site-meta”中。并且如果浏览器不支持canvas,将继续显示背景图片。

流星的实现是绘制一个LinearGradient的长条棒棒,并不是利用视觉暂留实现的。

2.2 使用z-index控制Canvas元素成为背景图像

标题处的html:

<div class="site-meta" style="background: none;">
  <div>
    <a href="/" class="brand" rel="start" data-pjax-state="load">
      <span class="site-title">枫糖</span>
    </a>
  </div>
  <p class="site-subtitle">From rookie to master</p>
  <canvas id="site-meta-canvas" width="991" height="127"></canvas>
</div>

控制Canvas元素成为背景图像的css:

/* 侧边栏上面的标题的背景 */
.site-meta {
    background: url(https://cdn.jsdelivr.net/gh/maplesugarr/[email protected]/imgs/header-bg.jpg);
    background-position: 50% 50%;
    background-size: auto 125px!important;
    position: relative;
}
#site-meta-canvas {
    background: linear-gradient(45deg, #063159, #038468, #063159, #038468, #063159, #038468);
    position: absolute;
    left: 0%;
    top: 0%;
    z-index: 1;
}
.site-nav-toggle,.site-subtitle,.brand{
    z-index: 2;
}
.site-subtitle {
    position:relative;
}

.site-meta是父级元素,为了使得子元素(#site-meta-canvas)能以它为参考点绝对定位(absolute),所以它的定位是relative。.site-subtitle定位为relative,是为了使它脱离标准文档流,从而使z-index生效。

Canvas画布(#site-meta-canvas)的background设置为linear-gradient(45deg, #063159, #038468, #063159, #038468, #063159, #038468)是为了使得星空有极光的效果,并且旋转45度,产生能小宽度也可见明暗相间的条纹,这样就能产生华丽但沉稳的效果。

打赏作者

Copyright © 2021,枫糖, 版权所有,禁止转载、演绎、商用。

添加评论

code