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

About the author

Đỗ Thu Hoài
Ko biết để gì cho hợp lý! https://www.threads.com/@dwo_th

Đăng nhận xét