Tạo hình nền bong bóng chuyển động tuyệt đẹp với Javascript
Bài viết hướng dẫn bạn sử dụng JavaScript cho phép bạn tạo các bong bóng chuyển động với màu sắc đẹp tuyệt vời, mượt mà cho nền blog/website và không cần dùng hình ảnh
File JS tạo bong bóng chuyển động
Bạn sử dụng đoạn code bên dưới<script async src="background.js"></script>Nội dung file JS
/*
* background.js - simple abstract background generator
*
* Changes:
* 21-Jan-2016
* > Cap frames to 10 on mobile to prevent scroll lag
* 20-Jan-2016
* + Added more versatile mobile options
* > Using tabs and 80 width
* > Settings object instead of lotsa variables
* > Bokeh jitter now assumes inverted min/max, added x/y options instead
* - Removed previous change of rounding values, for better looks
* - Removed gradient jitter, not useful
* 6-Jan-2016
* > Stored PI * 2 in var for better framerate on mobile
* > Drawing circles to off-screen canvas only once, reducing .arc() ops
* + Added mobile detection
* + In-depth mobile+debug options
*
* @author pbondoer - http://bondoer.fr/
* @license CC0 - https://creativecommons.org/publicdomain/zero/1.0/
*/
window.addEventListener("load", function() {
var ctx = document.getElementById('background').getContext('2d');
//gradient
var options =
{
resolution: 1,
gradient:
{
resolution: 4,
smallRadius: 0,
hue:
{
min: 0,
max: 360
},
saturation:
{
min: 40,
max: 80
},
lightness:
{
min: 25,
max: 35
}
},
bokeh:
{
count: 30,
size:
{
min: 0.1,
max: 0.3
},
alpha:
{
min: 0.05,
max: 0.4
},
jitter:
{
x: 0.3,
y: 0.3
}
},
speed:
{
min: 0.0001,
max: 0.001
},
debug:
{
strokeBokeh: false,
showFps: false
}
};
var mobile =
{
force: false,
resolution: 0.5,
bokeh:
{
count: 6
}
};
//buffers
var gradientBuffer = document.createElement('canvas').getContext('2d');
var circleBuffer = document.createElement('canvas').getContext('2d');
//render time, fps calculations, debug
var time;
var targetFps = 60; //not actual fps, but updates per second
var curFps = 0;
var cntFps = 0;
var fps = 0;
var w = 0;
var h = 0;
var scale = 0;
//constants for faster calcs
var pi2 = Math.PI * 2;
//util functions
function lerp(a, b, step) {
return step * (b - a) + a;
}
function clamp(a) {
if (a < 0) return 0;
if (a > 1) return 1;
return a;
}
function rand(obj) {
return Math.random() * (obj.max - obj.min) + obj.min;
}
function newColor() {
return new Color(
rand(options.gradient.hue),
rand(options.gradient.saturation),
rand(options.gradient.lightness)
);
}
function isMobile() {
return (
mobile.force
|| navigator.userAgent.match(/Android/i)
|| navigator.userAgent.match(/webOS/i)
|| navigator.userAgent.match(/iPhone/i)
|| navigator.userAgent.match(/iPad/i)
|| navigator.userAgent.match(/iPod/i)
|| navigator.userAgent.match(/BlackBerry/i)
|| navigator.userAgent.match(/Windows Phone/i)
);
}
window.requestAnimFrame = (function(callback) {
if (isMobile())
return function(callback) {
window.setTimeout(callback, 1000 / 10);
};
return window.requestAnimationFrame || window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame || window.oRequestAnimationFrame
|| window.msRequestAnimationFrame || function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
//classes
function Color(h, s, l) {
this.h = h;
this.s = s;
this.l = l;
this.str = function() {
return this.h + ", " + this.s + "%, " + this.l +"%";
}
}
function ColorPoint(x, y, color) {
this.x = x;
this.y = y;
this.oldColor = color;
this.newColor = color;
this.step = 0;
this.speed = 0;
this.color = function() {
return new Color(lerp(this.oldColor.h, this.newColor.h, this.step),
lerp(this.oldColor.s, this.newColor.s, this.step),
lerp(this.oldColor.l, this.newColor.l, this.step));
}
}
var colorPoints = [
new ColorPoint(0, 0, new Color(196, 59, 34)),
new ColorPoint(0, 1, new Color(269, 79, 32)),
new ColorPoint(1, 0, new Color(30, 42, 33)),
new ColorPoint(1, 1, new Color(304, 47, 27))
];
function BokehCircle(x, y, size, alpha) {
this.oldX = x;
this.oldY = y;
this.oldSize = size;
this.oldAlpha = alpha;
this.newX = 0;
this.newY = 0;
this.newAlpha = 0;
this.newSize = 0;
this.step = 0;
this.speed = 0;
this.x = function() {
return lerp(this.oldX, this.newX, this.step);
}
this.y = function() {
return lerp(this.oldY, this.newY, this.step);
}
this.alpha = function() {
return lerp(this.oldAlpha, this.newAlpha, this.step);
}
this.size = function() {
return lerp(this.oldSize, this.newSize, this.step);
}
}
var circles = [];
function setJitter(circle) {
circle.newX = clamp(circle.oldX + rand({
min: -options.bokeh.jitter.x,
max: options.bokeh.jitter.x
}));
circle.newY = clamp(circle.oldY + rand({
min: -options.bokeh.jitter.y,
max: options.bokeh.jitter.y
}));
}
function resize() {
var width = window.innerWidth;
var height = window.innerHeight;
w = width * options.resolution;
h = height * options.resolution;
scale = Math.sqrt(w * h);
//actual canvas
ctx.canvas.width = width;
ctx.canvas.height = height;
ctx.scale(1 / options.resolution, 1 / options.resolution);
//circle canvas
var circleSize = options.bokeh.size.max * scale;
circleBuffer.canvas.width = circleSize * 2 + 1;
circleBuffer.canvas.height = circleSize * 2 + 1;
circleBuffer.fillStyle = "rgb(255, 255, 255)";
circleBuffer.beginPath();
circleBuffer.arc(circleSize, circleSize, circleSize, 0, pi2);
circleBuffer.closePath();
circleBuffer.fill();
//force render on mobile
if (isMobile())
render();
}
function softCopy(src, dest)
{
var i = 0;
for (var property in src)
{
if (dest.hasOwnProperty(property))
if (softCopy(src[property], dest[property]) == 0)
dest[property] = src[property];
i++;
}
return i;
}
function init() {
gradientBuffer.canvas.height = options.gradient.resolution;
gradientBuffer.canvas.width = options.gradient.resolution;
if (isMobile())
softCopy(mobile, options);
resize();
colorPoints.forEach(function(point) {
point.oldColor = newColor();
point.newColor = newColor()
point.speed = rand(options.speed);
});
for(i = 0; i < options.bokeh.count; i++) {
circles.push(new BokehCircle(Math.random(), Math.random(),
rand(options.bokeh.size), rand(options.bokeh.alpha)));
circles[i].newAlpha = rand(options.bokeh.alpha);
circles[i].newSize = rand(options.bokeh.size);
circles[i].speed = rand(options.speed);
setJitter(circles[i]);
}
}
function iterate() {
var now = Date.now();
curFps += (now - (time || now));
cntFps++;
var delta = (now - (time || now)) / (1000 / targetFps);
time = now;
if(curFps > 1000) {
fps = 1000 / (curFps / cntFps);
curFps -= 1000;
cntFps = 0;
}
colorPoints.forEach(function(point) {
point.step += point.speed * delta;
if (point.step >= 1) {
point.step = 0;
point.oldColor = point.newColor;
point.newColor = newColor();
point.speed = rand(options.speed);
}
});
circles.forEach(function(circle) {
circle.step += circle.speed * delta;
if(circle.step >= 1) {
circle.step = 0;
circle.oldX = circle.newX;
circle.oldY = circle.newY;
circle.oldAlpha = circle.newAlpha;
circle.oldSize = circle.newSize;
setJitter(circle);
circle.newAlpha = rand(options.bokeh.alpha);
circle.newSize = rand(options.bokeh.size);
circle.speed = rand(options.speed);
}
});
}
function render() {
iterate();
//draw point gradient to buffer
colorPoints.forEach(function(point) {
var x = point.x * options.gradient.resolution;
var y = point.y * options.gradient.resolution;
var grad = gradientBuffer.createRadialGradient(x, y,
options.gradient.smallRadius, x, y,
options.gradient.resolution);
grad.addColorStop(0, 'hsla(' + point.color().str() + ', 255)');
grad.addColorStop(1, 'hsla(' + point.color().str() + ', 0)');
gradientBuffer.fillStyle = grad;
gradientBuffer.fillRect(0, 0,
options.gradient.resolution, options.gradient.resolution);
});
//draw gradient from memory
ctx.globalCompositeOperation = "source-over";
ctx.drawImage(gradientBuffer.canvas, 0, 0, w, h);
//draw bokeh
ctx.globalCompositeOperation = "overlay";
if (options.debug.strokeBokeh)
ctx.strokeStyle = "yellow";
circles.forEach(function(circle) {
var size = circle.size() * scale;
ctx.globalAlpha = circle.alpha();
ctx.drawImage(circleBuffer.canvas,
circle.x() * w - size / 2, circle.y() * h - size / 2,
size, size);
if(options.debug.strokeBokeh) {
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
ctx.strokeRect(circle.x() * w - size / 2,
circle.y() * h - size / 2, size, size);
ctx.globalCompositeOperation = "overlay";
}
});
ctx.globalAlpha = 1;
//debug info
if (options.debug.showFps) {
if(fps <= 10) ctx.fillStyle = 'red';
else ctx.fillStyle = 'yellow';
ctx.font = "20px sans-serif";
ctx.fillText(Math.round(fps) + " fps", 10, 20);
}
//done rendering, wait for frame
window.requestAnimFrame(render);
}
//does not seem to impact performance
window.addEventListener("resize", resize);
//init and render :)
init();
render();
});
Sử dụng HTML để hiển thị nền bong bóng
<canvas id="background"></canvas>
Sử dụng CSS để cố định nền
<style>
body, html {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: 0;
padding: 0;
}
#background {
position: fixed;
top: 0;
left: 0;
z-index: -100;
}
</style>
Cảm ơn đã theo dõi và chúc thành công! ./.
Nguồn: cssscript.com