0%

JS实现几何星空

ACM忙完啦,最近想给自己的xienaoban.github.io做个博客网页,刚好没学过html之类的东西,就从零开始一边学一边做。之前一直对html5做的各种特效很感兴趣,就从网页背景开始写起吧。

曾经看到过别人的博客的背景有种特效,就是背景随机散布若干点不停移动,当两个点足够近的时候就会产生一条线连接两点,就像星座的连线一样;当点离鼠标很近的时候还会被吸引。于是自己也来实现个。

感觉js真的是个很神奇的语言,没有类,就把函数“假装”为类,并作为类似构造函数而存在,然后用prototype添加成员函数。看上去还真像那么回事,哈哈。

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
//==========================================
// File: background.js
// Title: 几何星空背景
// Auther: XieNaoban
// Version: v0.9
//==========================================

//创建背景画布
var cvs = document.createElement("canvas");
cvs.width = window.innerWidth;
cvs.height = window.innerHeight;
cvs.style.cssText="\
position:fixed;\
top:0px;\
left:0px;\
z-index:-1;\
opacity:1.0;\
";
document.body.appendChild(cvs);

var ctx = cvs.getContext("2d");

var startTime = new Date().getTime();

//随机数函数
function randomInt(min,max) {
return Math.floor((max-min+1)*Math.random()+min);
}
function randomFloat(min,max) {
return (max-min)*Math.random()+min;
}

//构造点类
function Point() {
this.x = randomFloat(0,cvs.width);
this.y = randomFloat(0,cvs.height);

var maxD = 0.08, minD = 0.01;
this.speed = randomFloat(minD,maxD);
this.angle = randomFloat(0,2*Math.PI);

this.r = 1;

var grey = (this.r*30+200);
this.color = "rgba("+grey+","+grey+","+grey+",1)";
}

Point.prototype.move = function(dif) {
var dx = Math.sin(this.angle)*this.speed;
var dy = Math.cos(this.angle)*this.speed;
this.x += dx*dif;
if (this.x<0) {
this.x=0;
this.angle=2*Math.PI-this.angle;
}
else if (this.x>cvs.width){
this.x=cvs.width;
this.angle=2*Math.PI-this.angle;
}
this.y += dy*dif;
if (this.y<0) {
this.y=0;
this.angle=Math.PI-this.angle;
}
else if (this.y>cvs.height){
this.y=cvs.height;
this.angle=Math.PI-this.angle;
}
}

Point.prototype.draw = function() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x,this.y,this.r,0,Math.PI*2);
ctx.closePath();
ctx.fill();
}

//绘制每一帧
var points = [];
function initPoints(num) {
for (var i = 0; i<num; ++i) {
points.push(new Point());
}
}

var p0 = new Point();
var degree=5.0;
document.onmousemove = function(ev) {
p0.x = ev.clientX;
p0.y = ev.clientY;
}
document.onmousedown = function(ev) {
degree = 10.0;
p0.x = ev.clientX;
p0.y = ev.clientY;
}
document.onmouseup = function(ev) {
degree = 5.0;
p0.x = ev.clientX;
p0.y = ev.clientY;
}

function drawLine(p1,p2,d) {
var dis = (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y);
var t = 100/dis;
t = Math.min(t,0.4);
if(t<0.01) return false;
t = Math.min(1,t*d);
ctx.strokeStyle = "rgba(255,250,250,"+ t +")";
ctx.beginPath();
ctx.lineWidth=1;
ctx.moveTo(p1.x,p1.y);
ctx.lineTo(p2.x,p2.y);
ctx.closePath();
ctx.stroke();
return true;
}

function drawFrame() {
cvs.width = window.innerWidth;
cvs.height = window.innerHeight;
ctx.fillStyle = "rgba(0,43,54,1)";
ctx.fillRect(0,0,cvs.width,cvs.height);
var endTime = new Date().getTime();
var dif = endTime-startTime;
startTime = endTime;
for(var i = 0; i<points.length; ++i) {
for(var j = i+1; j<points.length; ++j) {
var p1 = points[i], p2 = points[j];
drawLine(p1,p2,3.0);
}
}
for(var i = 0; i<points.length; ++i) {
var p = points[i];
drawLine(p0,p,degree);
p.draw();
p.move(dif);
}
window.requestAnimationFrame(drawFrame);
}

initPoints(70);
drawFrame();

没有去实现那个鼠标跟随,因为感觉我以后应该会自己想一个原创的更好玩的特效,这个就练练手好了。不过实现效果还是蛮炫酷的很满意啊,就是当最大化窗口就会显得稀疏,窗口小了看着太密。

还有个问题就是cpu占用太高了。。。每一帧要渲染70个点、判断\(70*70=490\)条线(以及70个sin、cos可以优化为dx、dy)。。。果然以后必须得换个背景。。。(或者减少点并放大整个图形倒也不错)。

还有个八(te)哥(xing),就是切换到别的页面后不是本页面会停止渲染嘛,过半分钟再切回来会发现所有点挤在四个角落。原因是我是按照时间差来定下一帧每个点的位置的,当下一帧与当前帧的时间差太大的时候,每个点的移动距离都跑到屏幕外面了,于是被强制拉回屏幕的边缘。(感觉我这个实现方法会导致动画在低配电脑上看着很卡)。