1
酷炫一款动态背景 + 鼠标点击效果(HTML +js canvas)
作者:Five
- 2022 年 8 月 05 日
本文字数:8802 字
阅读完需:约 29 分钟
前言
之前用于装饰个人的 Hexo 博客背景和点击事件,于是动手弄弄顺便学习学习,现在分享出来给有需要的人。
废话不多说 ,分享一款酷炫的页面动态背景 效果见(https://fivecc.cn)
动态背景
1. 效果图:
背景效果
实例效果:
博客实例效果
2. 源码:
<!--背景css-->
<style>
* {
margin: 0;
padding: 0;
}
#canvasBg {
position: fixed;
background: #ccc;
overflow: auto;
z-index: -1;
}
</style>
<!--背景html-->
<canvas id="canvasBg"></canvas>
<!--背景js-->
<script>
window.requestAnimationFrame = (function () {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 2);
}
);
})();
var myCanvas = document.getElementById("canvasBg");
var ctx = myCanvas.getContext("2d"); //getContext 设置画笔
var num;
var w, h;
var duixiang = [];
var move = {};
function widthheight() {
w = myCanvas.width = window.innerWidth;
h = myCanvas.height = window.innerHeight;
num = Math.floor(w * h * 0.00028); //点的数量。根据屏幕大小确定
for (var i = 0; i < num; i++) {
duixiang[i] = {
x: Math.random() * w,
y: Math.random() * h,
cX: Math.random() * 0.6 - 0.3,
cY: Math.random() * 0.6 - 0.3,
R: Math.floor(Math.random() * 5) + 2,
//CC:Math.floor(Math.random()*3)+2,
r: Math.floor(Math.random() * 254),
g: Math.floor(Math.random() * 254),
b: Math.floor(Math.random() * 254),
};
// console.log(duixiang[i])
Cricle(
duixiang[i].x,
duixiang[i].y,
duixiang[i].R,
duixiang[i].r,
duixiang[i].g,
duixiang[i].b
);
//Cricle(duixiang[i].x,duixiang[i].y,duixiang[i].R,duixiang[i].CC);
}
}
widthheight(); //获取浏览器的等宽度等高
function Cricle(x, y, R, r, g, b) {
ctx.save(); //保存路径
if (Math.random() > 0.991) {
ctx.globalAlpha = 0.9;
} //ctx.fillStyle = "#CCC";}//填充的背景颜色
else {
ctx.globalAlpha = 0.47;
}
ctx.fillStyle = "rgb(" + r + "," + g + "," + b + ")";
ctx.beginPath(); //开始绘画
ctx.arc(x, y, R, Math.PI * 2, 0); //绘画圆 x y 半径(大小) 角度 一个PI 是180 * 2 = 360 真假 0/1 true/false
ctx.closePath(); //结束绘画
ctx.fill(); //填充背景颜色
ctx.restore(); //回复路径
}
Cricle();
!(function draw() {
ctx.clearRect(0, 0, w, h); //先清除画布上的点
for (var i = 0; i < num; i++) {
duixiang[i].x += duixiang[i].cX;
duixiang[i].y += duixiang[i].cY;
if (duixiang[i].x > w || duixiang[i].x < 0) {
duixiang[i].cX = -duixiang[i].cX;
}
if (duixiang[i].y > h || duixiang[i].y < 0) {
duixiang[i].cY = -duixiang[i].cY;
}
Cricle(
duixiang[i].x,
duixiang[i].y,
duixiang[i].R,
duixiang[i].r,
duixiang[i].g,
duixiang[i].b
);
//勾股定理判断两点是否连线
for (var j = i + 1; j < num; j++) {
if (
(duixiang[i].x - duixiang[j].x) * (duixiang[i].x - duixiang[j].x) +
(duixiang[i].y - duixiang[j].y) * (duixiang[i].y - duixiang[j].y) <=
55 * 55
) {
line(
duixiang[i].x,
duixiang[i].y,
duixiang[j].x,
duixiang[j].y,
0,
i,
j
);
}
if (move.x) {
if (
(duixiang[i].x - move.x) * (duixiang[i].x - move.x) +
(duixiang[i].y - move.y) * (duixiang[i].y - move.y) <=
100 * 100
) {
line(duixiang[i].x, duixiang[i].y, move.x, move.y, 1, i, 1);
}
}
}
}
window.requestAnimationFrame(draw);
})();
//绘制线条
function line(x1, y1, x2, y2, flag, i, j) {
if (flag) {
var color = ctx.createLinearGradient(x1, y1, x2, y2);
ctx.globalAlpha = 0.5;
color.addColorStop(
0,
"rgb(" + duixiang[i].r + "," + duixiang[i].g + "," + duixiang[i].b + ")"
);
color.addColorStop(0.8, "#019ee5");
} else {
var color = ctx.createLinearGradient(x1, y1, x2, y2);
ctx.globalAlpha = 0.9;
color.addColorStop(
0,
"rgb(" + duixiang[i].r + "," + duixiang[i].g + "," + duixiang[i].b + ")"
);
color.addColorStop(
1,
"rgb(" + duixiang[j].r + "," + duixiang[j].g + "," + duixiang[j].b + ")"
);
}
ctx.save();
ctx.strokeStyle = color;
ctx.lineWidth = 0.5;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
//ctx.restore();
}
//document.onmousemove = function(e){
// move.x = e.clientX;
// move.y = e.clientY;
//}
//console.log(move)//去掉注释 ,可以与背景互动
window.onresize = function () {
location.reload();
};
</script>
复制代码
鼠标点击特效
1.鼠标点击效果
鼠标点击效果
2.鼠标点击效果
<!--鼠标点击css-->
<style>
#canvasEvent {
position: fixed;
pointer-events: none;
width: 100%;
height: 100%;
overflow: auto;
z-index: 999;
}
</style>
<!--鼠标点击html-->
<canvas id="canvasEvent" style="whdth: 100%;height: 100%"></canvas>
<!--鼠标点击html-->
<script>
var _createClass = (function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
})();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var getRandom = function getRandom(min, max) {
return Math.random() * (max - min) + min;
};
var getRandomInt = function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
};
var getRandomColor = function getRandomColor() {
var colors = [
"rgba(231, 76, 60, 1)", // 红
"rgba(241, 196, 15, 1)", // 黄
"rgba(46, 204, 113, 1)", // 绿
"rgba(52, 152, 219, 1)", // 蓝
"rgba(155, 89, 182, 1)", // 紫色
];
return colors[getRandomInt(0, colors.length)];
};
// Particle//粒子模块
var Particle = (function () {
function Particle(system, x, y) {
_classCallCheck(this, Particle);
this.system = system;
this.universe = this.system.world.universe;
this.x = x;
this.y = y;
this.color = getRandomColor();
this.life = 1;
this.aging = getRandom(0.99, 0.999); // 0.99, 0.999 || 0.999, 0.9999
this.r = getRandomInt(12, 16); //初始粒子半径范围
this.speed = getRandom(18, 18.5); //粒子爆炸速度范围
this.velocity = [
getRandom(-this.speed, this.speed),
getRandom(-this.speed, this.speed),
];
}
_createClass(Particle, [
{
key: "update",
value: function update(dt) {
this.life *= this.aging;
if (
this.r < 0.1 ||
this.life === 0 ||
this.x + this.r < 0 ||
this.x - this.r > this.universe.width ||
this.y + this.r < 0 ||
this.y - this.r > this.universe.height
) {
this.system.removeObject(this);
}
this.r *= this.life;
this.x += this.velocity[0];
this.y += this.velocity[1];
},
},
{
key: "render",
value: function render(ctx) {
// Main circle //亮圈模块
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r * 1.2, 0, 2 * Math.PI, false);
ctx.fill();
ctx.closePath();
var r = this.color.match(/([0-9]+)/g)[0];
var g = this.color.match(/([0-9]+)/g)[1];
var b = this.color.match(/([0-9]+)/g)[2];
// Gradient//梯度变化曲线
var spread = 1.5;
var gradient = ctx.createRadialGradient(
this.x,
this.y,
this.r,
this.x,
this.y,
this.r * spread
);
gradient.addColorStop(0, "rgba(" + r + ", " + g + ", " + b + ", 0.5)");
gradient.addColorStop(1, "rgba(" + r + ", " + g + ", " + b + ", 0)");
ctx.globalCompositeOperation = "lighter";
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r * spread, 0, 2 * Math.PI, false);
ctx.fill();
ctx.closePath();
ctx.globalCompositeOperation = "source-over";
// Aberration//偏差
var offset = this.r * 0.5;
var color = "rgba(" + g + ", " + b + ", " + r + ", 0.5)";
ctx.globalCompositeOperation = "lighter";
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(
this.x + offset,
this.y + offset,
this.r,
0,
2 * Math.PI,
false
);
ctx.fill();
ctx.closePath();
ctx.globalCompositeOperation = "source-over";
},
},
]);
return Particle;
})();
// Crown //水波纹圈模块
var Crown = (function () {
function Crown(system, x, y) {
_classCallCheck(this, Crown);
this.system = system;
this.x = x;
this.y = y;
this.r = getRandomInt(5, 15); // 5, 20 水波纹圈半径范围
this.mod = 1.1;
this.life = 0.5; //水波纹线
this.aging = getRandom(0.83, 0.899);
this.speed = getRandom(8, 9);
this.color = {
r: getRandomInt(236, 242),
g: getRandomInt(70, 80),
b: getRandomInt(50, 70),
};
this.angle1 = Math.PI * getRandom(0, 2);
this.angle2 = this.angle1 + Math.PI * getRandom(0.3, 0.4); //水波纹圈完整度
}
_createClass(Crown, [
{
key: "update",
value: function update(dt) {
this.life *= this.aging;
if (this.life <= 0.0001) this.system.removeObject(this);
this.r += Math.abs(1 - this.life) * this.speed;
this.x1 = this.x + this.r * Math.cos(this.angle1);
this.y1 = this.y + this.r * Math.sin(this.angle1);
this.angle3 = this.angle1 + (this.angle2 - this.angle1) / 2;
this.x2 = this.x + this.r * this.mod * Math.cos(this.angle3);
this.y2 = this.y + this.r * this.mod * Math.sin(this.angle3);
},
},
{
key: "render",
value: function render(ctx) {
var gradient = ctx.createRadialGradient(
this.x,
this.y,
this.r * 0.9,
this.x,
this.y,
this.r
);
gradient.addColorStop(
0,
"rgba(" +
this.color.r +
", " +
this.color.g +
", " +
this.color.b +
", " +
this.life +
")"
);
gradient.addColorStop(
1,
"rgba(" +
this.color.r +
", " +
this.color.g +
", " +
this.color.b +
", " +
this.life * 0.5 +
")"
);
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, this.angle1, this.angle2, false);
ctx.quadraticCurveTo(this.x2, this.y2, this.x1, this.y1);
ctx.fill();
ctx.closePath();
},
},
]);
return Crown;
})();
// Explosion //爆炸模块
var Explosion = (function () {
function Explosion(world, x, y) {
_classCallCheck(this, Explosion);
this.world = world;
this.x = x;
this.y = y;
this.objects = [];
var particles = getRandomInt(10, 30); // 10, 30 //爆炸 粒子数量
var crowns = particles * getRandom(0.4, 0.5);
while (crowns-- > 0) {
this.addCrown();
}
while (particles-- > 0) {
this.addParticle();
}
}
_createClass(Explosion, [
{
key: "update",
value: function update(dt) {
this.objects.forEach(function (obj) {
if (obj) obj.update(dt);
});
if (this.objects.length <= 0) {
this.world.clearExplosion(this);
}
},
},
{
key: "render",
value: function render(ctx) {
this.objects.forEach(function (obj) {
if (obj) obj.render(ctx);
});
},
},
{
key: "addCrown",
value: function addCrown() {
this.objects.push(new Crown(this, this.x, this.y));
},
},
{
key: "addParticle",
value: function addParticle() {
this.objects.push(new Particle(this, this.x, this.y));
},
},
{
key: "removeObject",
value: function removeObject(obj) {
var index = this.objects.indexOf(obj);
if (index !== -1) {
this.objects.splice(index, 1);
}
},
},
]);
return Explosion;
})();
// World
var ConfettiWorld = (function () {
function ConfettiWorld() {
_classCallCheck(this, ConfettiWorld);
}
_createClass(ConfettiWorld, [
{
key: "init",
value: function init() {
this.objects = [];
window.addEventListener("click", this.explode.bind(this));
// Initial explosion //初始爆炸
var counter = 0;
while (counter-- > 0) {
this.explode({
clientX: window.event.clientX, //getRandomInt(10, this.universe.width) , //this.universe.width / 2,
clientY: window.event.clientY, //getRandomInt(10, 50) //this.universe.height / 2
});
}
},
},
{
key: "update",
value: function update(dt) {
this.objects.forEach(function (obj) {
if (obj) obj.update(dt);
});
var amount = this.objects.reduce(function (sum, explosion) {
return (sum += explosion.objects.length);
}, 0);
},
},
{
key: "render",
value: function render(ctx) {
this.objects.forEach(function (obj) {
if (obj) obj.render(ctx);
});
},
},
{
key: "explode",
value: function explode(event) {
var x = event.clientX;
var y = event.clientY;
this.objects.push(new Explosion(this, x, y));
},
},
{
key: "clearExplosion",
value: function clearExplosion(explosion) {
var index = this.objects.indexOf(explosion);
if (index !== -1) {
this.objects.splice(index, 1);
}
},
},
]);
return ConfettiWorld;
})();
// Time
var Time = (function () {
function Time() {
_classCallCheck(this, Time);
this.now = 0; // 当前粒子时间
this.prev = 0; // 上一粒子时间
this.elapsed = 0; //最后粒子时间
this.delta = 0; // 从上次更新开始的时间
this.fps = 60; // 期望fps
this.step = 1 / 60; // 步长
}
_createClass(Time, [
{
key: "update",
value: function update(time) {
this.now = time;
this.elapsed = (this.now - this.prev) / 1000;
this.prev = this.now;
this.delta += this.elapsed;
},
},
{
key: "raf",
value: function raf(func) {
window.requestAnimationFrame(func);
},
},
{
key: "hasFrames",
value: function hasFrames() {
return this.delta >= this.step;
},
},
{
key: "processFrame",
value: function processFrame() {
this.delta -= this.step;
},
},
]);
return Time;
})();
// Canvas
var Universe = (function () {
function Universe(element) {
_classCallCheck(this, Universe);
this.el = element;
this.ctx = this.el.getContext("2d");
this.pixelRatio = window.devicePixelRatio;
this.time = new Time();
this.worlds = {};
this.world = null; // current state
this.updateSize();
window.addEventListener("resize", this.updateSize.bind(this));
this.addWorld("confetti", ConfettiWorld);
this.setWorld("confetti");
this.start();
}
_createClass(Universe, [
{
key: "start",
value: function start() {
this.time.raf(this.tick.bind(this));
},
},
{
key: "tick",
value: function tick(time) {
this.time.update(time);
if (this.time.hasFrames()) {
this.update();
this.time.processFrame();
}
this.render();
this.time.raf(this.tick.bind(this));
},
},
{
key: "update",
value: function update() {
this.world.update(this.time.step);
},
},
{
key: "render",
value: function render() {
var gradient = this.ctx.createLinearGradient(
0,
0,
this.width,
this.height
);
this.ctx.clearRect(0, 0, this.width, this.height);
this.world.render(this.ctx);
},
// Helpers 库
},
{
key: "updateSize",
value: function updateSize() {
this.width = window.innerWidth;
this.height = window.innerHeight;
this.el.width = this.width * this.pixelRatio;
this.el.height = this.height * this.pixelRatio;
this.el.style.width = window.innerWidth + "px";
this.el.style.height = window.innerHeight + "px";
this.ctx.scale(this.pixelRatio, this.pixelRatio);
},
},
{
key: "addWorld",
value: function addWorld(worldName, World) {
this.worlds[worldName] = new World();
this.worlds[worldName].universe = this;
this.worlds[worldName].init();
},
},
{
key: "setWorld",
value: function setWorld(worldName) {
this.world = this.worlds[worldName];
},
},
]);
return Universe;
})();
// Main
console.clear();
var element = document.querySelector("#canvasEvent");
window.Canvas = new Universe(element);
</script>
复制代码
喜欢就给我点一个大大的赞是👍 外加一个关注
划线
评论
复制
发布于: 2022 年 08 月 05 日阅读数: 49
版权声明: 本文为 InfoQ 作者【Five】的原创文章。
原文链接:【http://xie.infoq.cn/article/07859db3323105087491674c6】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
Five
关注
有事多研究,没事瞎琢磨 2022.08.02 加入
CSDN 前端领域优质创作者 , 博客专家认证。 退役ACMer, IT技术狂热爱好者 擅长领域,web前端,算法, 业务架构,可视化,富文本编辑器等。 github: https://github.com/Five-great
评论