特效介绍

基于html5+js+canvas的气泡光晕特效,气泡随机产生,大大小小铺满整个屏幕,并且缓缓移动,每次刷新,背景的渐变色都会变化。非常适合做网页背景来使用。由于代码比较简单,就不提供下载地址,直接按照下面的方法就可以了。
使用方法
1、如果要当做背景,请加入下面的css:
01 | body, html { |
02 | position : absolute ; |
03 | top : 0 ; |
04 | bottom : 0 ; |
05 | left : 0 ; |
06 | right : 0 ; |
07 | margin : 0 ; |
08 | padding : 0 ; |
09 | } |
10 | #bac kground { |
11 | position : fixed ; |
12 | top : 0 ; |
13 | left : 0 ; |
14 | z-index : -100 ; |
15 | } |
2、在适当位置加入canvas
1 | < canvas id = "background" ></ canvas > |
001 | window.addEventListener( "load" , function () { |
002 | var ctx = document.getElementById( 'background' ).getContext( '2d' ); |
003 | //gradient |
004 | var options = |
005 | { |
006 | resolution: 1, |
007 | gradient: |
008 | { |
009 | resolution: 4, |
010 | smallRadius: 0, |
011 | hue: |
012 | { |
013 | min: 0, |
014 | max: 360 |
015 | }, |
016 | saturation: |
017 | { |
018 | min: 40, |
019 | max: 80 |
020 | }, |
021 | lightness: |
022 | { |
023 | min: 25, |
024 | max: 35 |
025 | } |
026 | }, |
027 | bokeh: |
028 | { |
029 | count: 30, |
030 | size: |
031 | { |
032 | min: 0.1, |
033 | max: 0.3 |
034 | }, |
035 | alpha: |
036 | { |
037 | min: 0.05, |
038 | max: 0.4 |
039 | }, |
040 | jitter: |
041 | { |
042 | x: 0.3, |
043 | y: 0.3 |
044 | } |
045 | }, |
046 | speed: |
047 | { |
048 | min: 0.0001, |
049 | max: 0.001 |
050 | }, |
051 | debug: |
052 | { |
053 | strokeBokeh: false , |
054 | showFps: false |
055 | } |
056 | }; |
057 | var mobile = |
058 | { |
059 | force: false , |
060 | resolution: 0.5, |
061 | bokeh: |
062 | { |
063 | count: 6 |
064 | } |
065 | }; |
066 | //buffers |
067 | var gradientBuffer = document.createElement( 'canvas' ).getContext( '2d' ); |
068 | var circleBuffer = document.createElement( 'canvas' ).getContext( '2d' ); |
069 | //render time, fps calculations, debug |
070 | var time; |
071 | var targetFps = 60; //not actual fps, but updates per second |
072 | var curFps = 0; |
073 | var cntFps = 0; |
074 | var fps = 0; |
075 | var w = 0; |
076 | var h = 0; |
077 | var scale = 0; |
078 | //constants for faster calcs |
079 | var pi2 = Math.PI * 2; |
080 | //util functions |
081 | function lerp(a, b, step) { |
082 | return step * (b - a) + a; |
083 | } |
084 | function clamp(a) { |
085 | if (a < 0) return 0; |
086 | if (a > 1) return 1; |
087 | return a; |
088 | } |
089 | function rand(obj) { |
090 | return Math.random() * (obj.max - obj.min) + obj.min; |
091 | } |
092 | function newColor() { |
093 | return new Color( |
094 | rand(options.gradient.hue), |
095 | rand(options.gradient.saturation), |
096 | rand(options.gradient.lightness) |
097 | ); |
098 | } |
099 |
100 | function isMobile() { |
101 | return ( |
102 | mobile.force |
103 | || navigator.userAgent.match(/Android/i) |
104 | || navigator.userAgent.match(/webOS/i) |
105 | || navigator.userAgent.match(/iPhone/i) |
106 | || navigator.userAgent.match(/iPad/i) |
107 | || navigator.userAgent.match(/iPod/i) |
108 | || navigator.userAgent.match(/BlackBerry/i) |
109 | || navigator.userAgent.match(/Windows Phone/i) |
110 | ); |
111 | } |
112 |
113 | window.requestAnimFrame = ( function (callback) { |
114 | if (isMobile()) |
115 | return function (callback) { |
116 | window.setTimeout(callback, 1000 / 10); |
117 | }; |
118 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame |
119 | || window.mozRequestAnimationFrame || window.oRequestAnimationFrame |
120 | || window.msRequestAnimationFrame || function (callback) { |
121 | window.setTimeout(callback, 1000 / 60); |
122 | }; |
123 | })(); |
124 |
125 | //classes |
126 | function Color(h, s, l) { |
127 | this .h = h; |
128 | this .s = s; |
129 | this .l = l; |
130 |
131 | this .str = function () { |
132 | return this .h + ", " + this .s + "%, " + this .l + "%" ; |
133 | } |
134 | } |
135 | function ColorPoint(x, y, color) { |
136 | this .x = x; |
137 | this .y = y; |
138 | this .oldColor = color; |
139 | this .newColor = color; |
140 | this .step = 0; |
141 | this .speed = 0; |
142 |
143 | this .color = function () { |
144 | return new Color(lerp( this .oldColor.h, this .newColor.h, this .step), |
145 | lerp( this .oldColor.s, this .newColor.s, this .step), |
146 | lerp( this .oldColor.l, this .newColor.l, this .step)); |
147 | } |
148 |
149 | } |
150 | var colorPoints = [ |
151 | new ColorPoint(0, 0, new Color(196, 59, 34)), |
152 | new ColorPoint(0, 1, new Color(269, 79, 32)), |
153 | new ColorPoint(1, 0, new Color(30, 42, 33)), |
154 | new ColorPoint(1, 1, new Color(304, 47, 27)) |
155 | ]; |
156 |
157 | function BokehCircle(x, y, size, alpha) { |
158 | this .oldX = x; |
159 | this .oldY = y; |
160 | this .oldSize = size; |
161 | this .oldAlpha = alpha; |
162 | this .newX = 0; |
163 | this .newY = 0; |
164 | this .newAlpha = 0; |
165 | this .newSize = 0; |
166 | this .step = 0; |
167 | this .speed = 0; |
168 |
169 | this .x = function () { |
170 | return lerp( this .oldX, this .newX, this .step); |
171 | } |
172 | this .y = function () { |
173 | return lerp( this .oldY, this .newY, this .step); |
174 | } |
175 | this .alpha = function () { |
176 | return lerp( this .oldAlpha, this .newAlpha, this .step); |
177 | } |
178 | this .size = function () { |
179 | return lerp( this .oldSize, this .newSize, this .step); |
180 | } |
181 | } |
182 | var circles = []; |
183 |
184 | function setJitter(circle) { |
185 | circle.newX = clamp(circle.oldX + rand({ |
186 | min: -options.bokeh.jitter.x, |
187 | max: options.bokeh.jitter.x |
188 | })); |
189 | circle.newY = clamp(circle.oldY + rand({ |
190 | min: -options.bokeh.jitter.y, |
191 | max: options.bokeh.jitter.y |
192 | })); |
193 | } |
194 | function resize() { |
195 | var width = window.innerWidth; |
196 | var height = window.innerHeight; |
197 |
198 | w = width * options.resolution; |
199 | h = height * options.resolution; |
200 | scale = Math.sqrt(w * h); |
201 |
202 | //actual canvas |
203 | ctx.canvas.width = width; |
204 | ctx.canvas.height = height; |
205 | ctx.scale(1 / options.resolution, 1 / options.resolution); |
206 |
207 | //圆 |
208 | var circleSize = options.bokeh.size.max * scale; |
209 | circleBuffer.canvas.width = circleSize * 2 + 1; |
210 | circleBuffer.canvas.height = circleSize * 2 + 1; |
211 |
212 | circleBuffer.fillStyle = "rgb(255, 255, 255)" ; |
213 | circleBuffer.beginPath(); |
214 | circleBuffer.arc(circleSize, circleSize, circleSize, 0, pi2); |
215 | circleBuffer.closePath(); |
216 | circleBuffer.fill(); |
217 |
218 | //force render on mobile |
219 | if (isMobile()) |
220 | render(); |
221 | } |
222 | function softCopy(src, dest) |
223 | { |
224 | var i = 0; |
225 |
226 | for ( var property in src) |
227 | { |
228 | if (dest.hasOwnProperty(property)) |
229 | if (softCopy(src[property], dest[property]) == 0) |
230 | dest[property] = src[property]; |
231 | i++; |
232 | } |
233 | return i; |
234 | } |
235 | function init() { |
236 | gradientBuffer.canvas.height = options.gradient.resolution; |
237 | gradientBuffer.canvas.width = options.gradient.resolution; |
238 |
239 | if (isMobile()) |
240 | softCopy(mobile, options); |
241 |
242 | resize(); |
243 |
244 | colorPoints.forEach( function (point) { |
245 | point.oldColor = newColor(); |
246 | point.newColor = newColor() |
247 | point.speed = rand(options.speed); |
248 | }); |
249 |
250 | for (i = 0; i < options.bokeh.count; i++) { |
251 | circles.push( new BokehCircle(Math.random(), Math.random(), |
252 | rand(options.bokeh.size), rand(options.bokeh.alpha))); |
253 | circles[i].newAlpha = rand(options.bokeh.alpha); |
254 | circles[i].newSize = rand(options.bokeh.size); |
255 | circles[i].speed = rand(options.speed); |
256 | setJitter(circles[i]); |
257 | } |
258 | } |
259 | function iterate() { |
260 | var now = Date.now(); |
261 | curFps += (now - (time || now)); |
262 | cntFps++; |
263 | var delta = (now - (time || now)) / (1000 / targetFps); |
264 | time = now; |
265 |
266 | if (curFps > 1000) { |
267 | fps = 1000 / (curFps / cntFps); |
268 | curFps -= 1000; |
269 | cntFps = 0; |
270 | } |
271 |
272 | colorPoints.forEach( function (point) { |
273 | point.step += point.speed * delta; |
274 |
275 | if (point.step >= 1) { |
276 | point.step = 0; |
277 |
278 | point.oldColor = point.newColor; |
279 |
280 | point.newColor = newColor(); |
281 | point.speed = rand(options.speed); |
282 | } |
283 | }); |
284 |
285 | circles.forEach( function (circle) { |
286 | circle.step += circle.speed * delta; |
287 | if (circle.step >= 1) { |
288 | circle.step = 0; |
289 |
290 | circle.oldX = circle.newX; |
291 | circle.oldY = circle.newY; |
292 | circle.oldAlpha = circle.newAlpha; |
293 | circle.oldSize = circle.newSize; |
294 |
295 | setJitter(circle); |
296 | circle.newAlpha = rand(options.bokeh.alpha); |
297 | circle.newSize = rand(options.bokeh.size); |
298 | circle.speed = rand(options.speed); |
299 | } |
300 | }); |
301 | } |
302 |
303 | function render() { |
304 | iterate(); |
305 |
306 | //绘制点渐变到缓冲区 |
307 | colorPoints.forEach( function (point) { |
308 | var x = point.x * options.gradient.resolution; |
309 | var y = point.y * options.gradient.resolution; |
310 | var grad = gradientBuffer.createRadialGradient(x, y, |
311 | options.gradient.smallRadius, x, y, |
312 | options.gradient.resolution); |
313 | grad.addColorStop(0, 'hsla(' + point.color().str() + ', 255)' ); |
314 | grad.addColorStop(1, 'hsla(' + point.color().str() + ', 0)' ); |
315 |
316 | gradientBuffer.fillStyle = grad; |
317 | gradientBuffer.fillRect(0, 0, |
318 | options.gradient.resolution, options.gradient.resolution); |
319 | }); |
320 |
321 | //绘制渐变 |
322 | ctx.globalCompositeOperation = "source-over" ; |
323 | ctx.drawImage(gradientBuffer.canvas, 0, 0, w, h); |
324 |
325 | //绘制气泡 |
326 | ctx.globalCompositeOperation = "overlay" ; |
327 | if (options.debug.strokeBokeh) |
328 | ctx.strokeStyle = "yellow" ; |
329 |
330 | circles.forEach( function (circle) { |
331 | var size = circle.size() * scale; |
332 |
333 | ctx.globalAlpha = circle.alpha(); |
334 | ctx.drawImage(circleBuffer.canvas, |
335 | circle.x() * w - size / 2, circle.y() * h - size / 2, |
336 | size, size); |
337 |
338 | if (options.debug.strokeBokeh) { |
339 | ctx.globalAlpha = 1; |
340 | ctx.globalCompositeOperation = "source-over" ; |
341 | ctx.strokeRect(circle.x() * w - size / 2, |
342 | circle.y() * h - size / 2, size, size); |
343 | ctx.globalCompositeOperation = "overlay" ; |
344 | } |
345 | }); |
346 | ctx.globalAlpha = 1; |
347 |
348 | if (options.debug.showFps) { |
349 | if (fps <= 10) ctx.fillStyle = 'red' ; |
350 | else ctx.fillStyle = 'yellow' ; |
351 |
352 | ctx.font = "20px sans-serif" ; |
353 | ctx.fillText(Math.round(fps) + " fps" , 10, 20); |
354 | } |
355 |
356 | //渲染完成,等待帧 |
357 | window.requestAnimFrame(render); |
358 | } |
359 |
360 | //不影响性能 |
361 | window.addEventListener( "resize" , resize); |
362 |
363 | //初始化和渲染 |
364 | init(); |
365 | render(); |
366 | }); |
部分素材资源来源网站,本站提供免费下载,如有侵权请联系站长马上删除!