1. 效果图
2. 背景色修改
代码语言:javascript复制background: rgb(0, 0, 0);
3. 粒子相关配置
代码语言:javascript复制var max_radius = 3; //粒子的最大半径
var min_radius = 1; //粒子的最小半径
var drag = 20; //切换文字,粒子组成速度 值越小越快
var colors = ["rgba(255, 255, 255, 0.7)","rgba(255, 255, 255, 0.1)"];//粒子颜色
var bool = true; //默认粒子文字是直接显示,反之则是粒子打乱后显示
var textFont = 80; //粒子文字大小
4. 方法调用
代码语言:javascript复制changeText("❤");
5. 坐标位置
获得在画布上绘制的像素坐标位置
代码语言:javascript复制let temp;
for (let i = 0; i < data.length; i = 4) {
temp = {
x: (i / 4) % w,
y: ~~((i / 4) / w)
};
if (data[i] !== 0 && ~~(Math.random() * 5) == 1) {
ctx.beginPath();
ctx.fillStyle = 'red';
ctx.arc(temp.x, temp.y, 2, 0, Math.PI * 2);
ctx.fill();
}
}
若你不在画布上做任何操作,那么当你输出 data 时为。所以加上 data[i] !== 0 (rgb(0, 0, 0) 为黑色,只要你绘制的东西不是这个色值,肯定会出现不等于0的像素模块,因此就能确定出你作画的位置坐标)
6. 代码
代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
width: 100%;
height: 100vh;
overflow: hidden;
/* background-color: black; */
margin: 0;
}
canvas {
width: 100%;
height: 100%;
background: rgb(0, 0, 0);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
#input {
font-size: 30px;
color: white;
outline: none;
display: block;
width: 300px;
position: absolute;
left: calc(50% - 150px);
top: 30%;
border: none;
background: transparent;
border-bottom: 2px solid rgba(255, 255, 255, 0.822);
text-align: center;
}
</style>
</head>
<body>
<input id="input" type="text" maxlength="8" oninput="changeText(input.value)">
</body>
<script>
/*jshint esversion:6*/
var canvas = document.createElement("canvas");
var w = window.innerWidth;
var h = window.innerHeight;
document.body.appendChild(canvas);
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');
//储存粒子数组
var particles = [];
var max_radius = 3; //粒子的最大半径
var min_radius = 1; //粒子的最小半径
var drag = 20; //切换文字,粒子组成速度 值越小越快
var colors = ["rgba(255, 255, 255, 0.7)", "rgba(255, 255, 255, 0.1)"];//粒子颜色
var bool = true; //默认粒子文字是直接显示,反之则是粒子打乱后显示
var textFont = 80; //粒子文字大小
//初始鼠标坐标
var mouse = {
x: -1000,
y: -1000
};
//pc 鼠标移动
canvas.onmousemove = function (e) {
mouse.x = e.clientX;
mouse.y = e.clientY;
};
//mobile 手势移动
canvas.ontouchmove = function (e) {
mouse.x = e.touches[0].clientX;
mouse.y = e.touches[0].clientY;
};
// 返回一个数的平方根
function distance(x, y, x1, y1) {
// sqrt() 方法可返回一个数的平方根。
// a² b² = c²
return Math.sqrt((x1 - x) * (x1 - x) (y1 - y) * (y1 - y));
}
//创建粒子文字或改变粒子文字
//根据文字生成相对应的 粒子 ,并储存到 particles 数组内
function changeText(text) {
ctx.clearRect(0, 0, w, h);
var current = 0; //初始化 粒子个数 默认为0个
var temp; //初始化 粒子坐标
var radius; //初始化 粒子半径
var color; //初始化 粒子颜色
//文字部分
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.font = textFont "px -apple-system";
//https://www.w3school.com.cn/tags/canvas_filltext.asp
//https://www.w3school.com.cn/tags/canvas_measuretext.asp
//measureText 在画布上输出文本之前,检查字体的宽度
// 实现文字居中
ctx.fillText(text, w / 2 - ctx.measureText(text).width / 2, h / 2 textFont / 2);
//https://www.w3school.com.cn/tags/canvas_getimagedata.asp
//通过 getImageData() 复制画布上指定矩形的像素数据
var data = ctx.getImageData(0, 0, w, h).data;
// console.log(data)
// =4 对于 ImageData 对象中的每个像素,都存在着4方面的信息,即 RGBA 值
//此处 =8 是为了避免资源浪费,减少粒子个数
// = 越多,避免筛选的粒子越少,符合条件的,显示的粒子越少
//data 会有特别 特别多
for (let i = 0; i < data.length; i = 4) {
temp = {
x: (i / 4) % w,
y: ~~((i / 4) / w)
};
//比较结果上的区别
// != 返回同类型值比较结果。
// !== 不同类型不比较,且无结果,同类型才比较。
//比较过程上的区别
//!= 比较时,若类型不同,会偿试转换类型。
//!== 只有相同类型才会比较。
//此处使用 !==,只能说是为了节约资源
//http://www.fly63.com/article/detial/2802
//~~ 同Math.floor() 向下取整
//判断不为0 && 随机一个数值取值为 1 时,才生成一个粒子
if (data[i] !== 0 && ~~(Math.random() * 5) == 1) {
//因为之前定义文字颜色为 rgb(255, 255, 255)w
//所以此处判断当遇到像素色值为 255 时,不显示粒子(若加上此处判断,则会实现描边效果,反之为填充效果)
// if (data[i 4] !== 255 || data[i - 4] !== 255 || data[i w * 4] !== 255 || data[i - w * 4] !== 255) {
//判断当再次调用 changeText 改变文字时,重新打乱并组成新的粒子文字
if (current < particles.length) {
particles[current].target = temp
// console.log(current)
} else {
//随机生成一个 min max 之间的粒子半径
radius = max_radius - Math.random() * min_radius;
//粒子从随机位置生成
if (!bool) {
temp = {
x: Math.random() * w,
y: Math.random() * h
};
}
//取粒子随机色
color = colors[~~(Math.random() * colors.length)];
//创建一个粒子,并添加到粒子数组中
var p = new Particle(
temp,
{ x: (i / 4) % w, y: ~~((i / 4) / w) },
{ x: 0, y: 0 },
color,
radius);
particles.push(p);
}
// current先自己加1,再做别的事情
//存储符合条件的粒子数量
current;
}
// }
}
bool = false;
//当粒子文字变化时,删除多余的粒子
particles.splice(current, particles.length - current);
}
//创建一个粒子的类,制定相关属性
class Particle {
constructor(pos, target, vel, color, radius) {
this.pos = pos; //粒子坐标(随机位置或粒子坐标,方便实现文字切换时粒子打乱效果)
this.target = target; //粒子坐标
this.vel = vel; //记录鼠标移动打散的粒子坐标
this.color = color; //粒子颜色
this.radius = radius; //粒子半径
var arr = [-1, 1];
//粒子大小的变化速率
this.direction = arr[~~(Math.random() * 2)] * Math.random() / 10;
}
//实时更新数据
update() {
//粒子半径的变化
this.radius = this.direction;
this.vel.x = (this.pos.x - this.target.x) / drag;
this.vel.y = (this.pos.y - this.target.y) / drag;
//鼠标移动到文字上时的效果
//根据勾股定理 a² b² = c²,鼠标坐标位置 - 粒子坐标位置 得到 a b 的长度,以此求得c的长度(及打散半径)
//50 鼠标打散范围,当 c 的长度小于 50 时执行
if (distance(this.pos.x, this.pos.y, mouse.x, mouse.y) < 50) {
this.vel.x = this.vel.x - (this.pos.x - mouse.x) / 15;
this.vel.y = this.vel.y - (this.pos.y - mouse.y) / 15;
}
//实现粒子变大缩小一直循环的效果
if (this.radius >= max_radius) {
this.direction *= -1;
}
if (this.radius <= 1) {
this.direction *= -1;
}
this.pos.x -= this.vel.x;
this.pos.y -= this.vel.y;
this.draw()
}
//绘制粒子
draw() {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2);
ctx.fill();
}
}
//绘制粒子文字
function draw() {
ctx.clearRect(0, 0, w, h);
for (let n of particles) {
n.update()
}
}
//每帧执行的粒子变化动画
function animation() {
// 相当于每一帧都会执行一次方法
window.requestAnimationFrame(animation)
draw()
}
animation()
//默认显示粒子文字为 input
changeText("❤");
</script>
</html>