0%

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

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

1. 标题处图片背景

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

/source/_data/styles.styl
1
2
3
4
5
6
7
8
9
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背景

只是一个静止的图片,不够炫酷,有动画效果才好,了解到HTML5 的Canvas技术。只是Canvas不是背景图像,没有控制Canvas当背景图像的css语法,可以使用z-index控制Canvas元素成为背景图像。

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

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

1
2
3
4
5
6
7
8
9
10
function animate() {
//绘制一帧图片
context.clearRect(0,0,WINDOW_WIDTH,WINDOW_HEIGHT);
for (var i in stars) {
stars[i].draw();
}
ufo.draw();
//循环绘制
requestAnimationFrame(animate)
}

上面的代码中绘制了star,绘制了ufo。由于比较简单,它们的运动函数,没有被分离出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
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/blog-assets@master/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:

1
2
3
4
5
6
7
8
9
<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:

/source/_data/styles.styl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 侧边栏上面的标题的背景 */
.site-meta {
background: url(https://cdn.jsdelivr.net/gh/maplesugarr/blog-assets@master/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度,产生能小宽度也可见明暗相间的条纹,这样就能产生华丽但沉稳的效果。

3. 其他

为什么不直接在canvas中画出极光呢?因为很复杂。

为什么不直接在canvas中画出飞碟呢?因为很复杂。

参考:https://codepen.io/LouiseX/pen/aGmYEd

4. 判断你的浏览器是否是垃圾

代码调用的是requestAnimationFrame这个浏览器指定的Api,运算量也很小。如果你的浏览器看起来一卡卡的,那么你的浏览器就是垃圾。