今天带大家探索一个炫酷的前端动画效果:鼠标联动粒子动画!这种效果不仅充满科技感,还能为网页增加互动性和视觉冲击力。鼠标轻轻移动,粒子会随之动态变化,线条连接点之间更是随着距离产生不同的透明度。
源代码地址:
https://codepen.io/MarcoGuglielmelli/pen/ExGYae
实现步骤
HTML
创建一个 canvas 和一个标题
html
<div id="large-header" class="large-header">
<canvas id="demo-canvas"></canvas>
<h1 class="main-title">Connect <span class="thin">Three</span></h1>
</div>
引入动画库
<script src="https://www.marcoguglie.it/Codepen/AnimatedHeaderBg/demo-1/js/TweenLite.min.js"></script>
CSS
设置页面的宽度和高度,并且给页面设置背景,将 title 水平垂直居中。
css
.large-header {
position: relative;
width: 100%;
background: #333;
overflow: hidden;
background-size: cover;
background-position: center center;
z-index: 1;
}
#large-header {
background-image: url("https://www.marcoguglie.it/Codepen/AnimatedHeaderBg/demo-1/img/demo-1-bg.jpg");
}
.main-title {
position: absolute;
margin: 0;
padding: 0;
color: #f9f1e9;
text-align: center;
top: 50%;
left: 50%;
-webkit-transform: translate3d(-50%, -50%, 0);
transform: translate3d(-50%, -50%, 0);
}
.main-title .thin {
font-weight: 200;
}
Javascript
初始化 Canvas
在 JavaScript 中,我们首先要获取 Canvas 元素,并设置它的宽度和高度为当前浏览器窗口的宽度和高度。
- 获取 Canvas 上下文:ctx = canvas.getContext('2d') 是获取 Canvas 2D 绘图上下文,我们用它来进行绘制。
- 随机生成点:我们生成了一些点,并将它们的位置保存到 points 数组中。每个点都会有一个原始位置(originX 和 originY),用于后续的动画效果。
javascript
var width,
height,
largeHeader,
canvas,
ctx,
points,
target,
animateHeader = true;
// 初始化头部和Canvas
function initHeader() {
width = window.innerWidth; // 获取浏览器的宽度
height = window.innerHeight; // 获取浏览器的高度
target = { x: width / 2, y: height / 2 }; // 设置动画的目标点为浏览器中心
// 获取Canvas元素和设置其尺寸
largeHeader = document.getElementById("large-header");
largeHeader.style.height = height + "px"; // 设置容器的高度为浏览器的高度
canvas = document.getElementById("demo-canvas");
canvas.width = width;
canvas.height = height;
ctx = canvas.getContext("2d"); // 获取2d上下文,后续绘制需要用到它
// 创建随机的点
points = [];
for (var x = 0; x < width; x += width / 20) {
for (var y = 0; y < height; y += height / 20) {
var px = x + (Math.random() * width) / 20; // x坐标带随机偏移
var py = y + (Math.random() * height) / 20; // y坐标带随机偏移
points.push({ x: px, originX: px, y: py, originY: py }); // 保存每个点的位置和原始位置
}
}
}
计算点之间的距离和连接关系
我们需要为每个点找到离它最近的 5 个点,然后通过这些点来绘制连接线。这是实现动画的关键部分。
代码实现:
javascript
// 计算每个点的5个最近邻
for (var i = 0; i < points.length; i++) {
var closest = [];
var p1 = points[i];
for (var j = 0; j < points.length; j++) {
var p2 = points[j];
if (p1 !== p2) {
// 排除自己
var placed = false;
for (var k = 0; k < 5; k++) {
if (!placed) {
if (closest[k] === undefined) {
closest[k] = p2; // 如果还没有5个邻近点,则直接添加
placed = true;
}
}
}
for (var k = 0; k < 5; k++) {
if (!placed) {
if (getDistance(p1, p2) < getDistance(p1, closest[k])) {
closest[k] = p2; // 更新最近的邻近点
placed = true;
}
}
}
}
}
p1.closest = closest; // 保存每个点的5个最近邻点
}
代码解释:
- 排除自己:if (p1 !== p2) 这段代码确保点不会与自身连接。
- 计算距离:getDistance(p1, p2) 是我们用来计算两个点之间的距离的函数。它通过勾股定理计算点间的欧几里得距离。
- 选择最近邻点:我们为每个点找到它的 5 个最近邻点,保存到 closest 数组中。
添加事件监听器
为了使得动画可以响应用户的输入,我们需要监听鼠标的移动和窗口的变化。
代码实现:
javascript
function addListeners() {
if (!("ontouchstart" in window)) {
window.addEventListener("mousemove", mouseMove); // 监听鼠标移动
}
window.addEventListener("scroll", scrollCheck); // 监听滚动事件
window.addEventListener("resize", resize); // 监听窗口大小变化
}
function mouseMove(e) {
var posx =
e.pageX ||
e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
var posy =
e.pageY ||
e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
target.x = posx; // 更新鼠标位置
target.y = posy;
}
function scrollCheck() {
if (document.body.scrollTop > height)
animateHeader = false; // 当页面滚动超过一定高度,停止动画
else animateHeader = true;
}
function resize() {
width = window.innerWidth;
height = window.innerHeight;
largeHeader.style.height = height + "px"; // 重新设置容器的高度
canvas.width = width;
canvas.height = height; // 重新设置Canvas的宽高
}
代码解释:
- mousemove:监听鼠标移动事件,将鼠标的位置作为目标点 target.x 和 target.y 更新。
- scrollCheck:根据页面的滚动位置来决定是否继续播放动画。
- resize:在窗口尺寸变化时,重新设置 Canvas 的宽高,并调整动画的显示区域。
动画实现
在这个步骤中,我们使用 requestAnimationFrame 来实现不断渲染的动画效果。我们会清除画布,重新绘制所有的点,并根据鼠标位置来调整每个点的透明度。
代码实现:
javascript
function initAnimation() {
animate(); // 启动动画
for (var i in points) {
shiftPoint(points[i]); // 给每个点添加偏移动画
}
}
function animate() {
if (animateHeader) {
ctx.clearRect(0, 0, width, height); // 清除画布
for (var i in points) {
if (Math.abs(getDistance(target, points[i])) < 4000) {
points[i].active = 0.3;
points[i].circle.active = 0.6;
} else if (Math.abs(getDistance(target, points[i])) < 20000) {
points[i].active = 0.1;
points[i].circle.active = 0.3;
} else if (Math.abs(getDistance(target, points[i])) < 40000) {
points[i].active = 0.02;
points[i].circle.active = 0.1;
} else {
points[i].active = 0;
points[i].circle.active = 0;
}
drawLines(points[i]); // 绘制连接线
points[i].circle.draw(); // 绘制圆圈
}
}
requestAnimationFrame(animate); // 请求下一帧动画
}
function shiftPoint(p) {
TweenLite.to(p, 1 + 1 * Math.random(), {
x: p.originX - 50 + Math.random() * 100,
y: p.originY - 50 + Math.random() * 100,
ease: Circ.easeInOut,
onComplete: function () {
shiftPoint(p); // 每次偏移完成后递归调用,继续动画
},
});
}
代码解释:
- clearRect:每一帧渲染之前,我们清除整个画布。
- requestAnimationFrame:浏览器提供的一个函数,用于在下一帧重新调用 animate,实现流畅的动画效果。
- shiftPoint:让每个点随机移动,产生动画效果。TweenLite 是一个动画库,它帮助我们实现平滑的动画过渡。
绘制圆圈和连接线
最终,我们需要绘制每个点的圆圈和点与点之间的连线。
代码实现:
javascript
function drawLines(p) {
if (!p.active) return; // 如果点不可见,则不绘制
for (var i in p.closest) {
ctx.beginPath();
ctx.moveTo(p.x, p.y); // 连接线起点
ctx.lineTo(p.closest[i].x, p.closest[i].y); // 连接线终点
ctx.strokeStyle = "rgba(156,217,249," + p.active + ")"; // 设置连接线颜色
ctx.stroke(); // 绘制连接线
}
}
function Circle(pos, rad, color) {
var _this = this;
// 构造函数
(function () {
_this.pos = pos || null;
_this.radius = rad || null;
_this.color = color || null;
})();
this.draw = function () {
if (!_this.active) return;
ctx.beginPath();
ctx.arc(_this.pos.x, _this.pos.y, _this.radius, 0, 2 * Math.PI, false);
ctx.fillStyle = "rgba(156,217,249," + _this.active + ")"; // 设置圆圈颜色
ctx.fill(); // 绘制圆圈
};
}
代码解释:
- drawLines:根据点与点之间的距离,绘制它们之间的连线。如果两个点距离较近,连接线的透明度较高。
- Circle:我们为每个点创建一个圆形对象,并通过 draw 方法绘制该圆。
总结
通过以上步骤,我们实现了一个基于 Canvas 的动态点线动画。每个点的位置根据鼠标位置发生变化,并且每两个点之间会根据距离绘制不同透明度的连接线。通过调整点的随机偏移,我们为每个点增加了动画效果。