sevenvalentine-edibotox
Version:
This is a simple JavaScript code snippet to create a fullscreen Valentine's Day heart confession effect using HTML5 Canvas. The code renders a heart-shaped curve on the canvas and continuously updates it to create an animated effect.
183 lines (147 loc) • 5.39 kB
JavaScript
const btn = document.createElement('button');
btn.textContent = 'Toggle Animation';
btn.style.position = 'fixed';
btn.style.top = '20px';
btn.style.right = '20px';
document.body.appendChild(btn);
let animationRunning = true;
btn.addEventListener('click', () => {
animationRunning = !animationRunning;
if (animationRunning) {
render();
btn.textContent = 'Stop Animation';
} else {
cancelAnimationFrame(render);
btn.textContent = 'Start Animation';
}
});
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const scale = 10;
ctx.fillStyle = '#FF0000';
ctx.beginPath();
for (let angle = 0; angle <= 2 * Math.PI; angle += 0.01) {
const x = scale * (16 * Math.pow(Math.sin(angle), 3));
const y = -scale * (13 * Math.cos(angle) - 5 * Math.cos(2 * angle) - 2 * Math.cos(3 * angle) - Math.cos(4 * angle));
ctx.lineTo(x + canvas.width / 2, y + canvas.height / 2);
}
ctx.fill();
ctx.closePath();
if (animationRunning) {
requestAnimationFrame(render);
}
}
import { bind, clear } from 'size-sensor';
import { requestAnimationFrame, cancelAnimationFrame, range, canvasStyle } from './utils';
export default class CanvasNest {
static version = __VERSION__;
constructor(el, config) {
this.el = el;
this.c = {
zIndex: -1, // z-index
opacity: 0.5, // opacity
color: '0,0,0', // color of lines
pointColor: '0,0,0', // color of points
count: 99, // count
...config,
};
this.canvas = this.newCanvas();
this.context = this.canvas.getContext('2d');
this.points = this.randomPoints();
this.current = {
x: null,
y: null,
max: 20000
};
this.all = this.points.concat([this.current]);
this.bindEvent();
this.requestFrame(this.drawCanvas);
}
bindEvent() {
bind(this.el, () => {
this.canvas.width = this.el.clientWidth;
this.canvas.height = this.el.clientHeight;
});
this.onmousemove = window.onmousemove;
window.onmousemove = e => {
this.current.x = e.clientX - this.el.offsetLeft + document.scrollingElement.scrollLeft; // 当存在横向滚动条时,x坐标再往右移动滚动条拉动的距离
this.current.y = e.clientY - this.el.offsetTop + document.scrollingElement.scrollTop; // 当存在纵向滚动条时,y坐标再往下移动滚动条拉动的距离
this.onmousemove && this.onmousemove(e);
};
this.onmouseout = window.onmouseout;
window.onmouseout = () => {
this.current.x = null;
this.current.y = null;
this.onmouseout && this.onmouseout();
};
}
randomPoints = () => {
return range(this.c.count).map(() => ({
x: Math.random() * this.canvas.width,
y: Math.random() * this.canvas.height,
xa: 2 * Math.random() - 1, // 随机运动返现
ya: 2 * Math.random() - 1,
max: 6000 // 沾附距离
}));
};
newCanvas() {
if (getComputedStyle(this.el).position === 'static') {
this.el.style.position = 'relative'
}
const canvas = document.createElement('canvas'); // 画布
canvas.style.cssText = canvasStyle(this.c);
canvas.width = this.el.clientWidth;
canvas.height = this.el.clientHeight;
this.el.appendChild(canvas);
return canvas;
}
requestFrame(func) {
this.tid = requestAnimationFrame(() => func.call(this));
}
drawCanvas() {
const context = this.context;
const width = this.canvas.width;
const height = this.canvas.height;
const current = this.current;
const points = this.points;
const all = this.all;
context.clearRect(0, 0, width, height);
// 随机的线条和当前位置联合数组
let e, i, d, x_dist, y_dist, dist; // 临时节点
// 遍历处理每一个点
points.forEach((r, idx) => {
r.x += r.xa;
r.y += r.ya; // 移动
r.xa *= r.x > width || r.x < 0 ? -1 : 1;
r.ya *= r.y > height || r.y < 0 ? -1 : 1; // 碰到边界,反向反弹
context.fillStyle = `rgba(${this.c.pointColor})`;
context.fillRect(r.x - 0.5, r.y - 0.5, 1, 1); // 绘制一个宽高为1的点
// 从下一个点开始
for (i = idx + 1; i < all.length; i ++) {
e = all[i];
// 当前点存在
if (null !== e.x && null !== e.y) {
x_dist = r.x - e.x; // x轴距离 l
y_dist = r.y - e.y; // y轴距离 n
dist = x_dist * x_dist + y_dist * y_dist; // 总距离, m
dist < e.max && (e === current && dist >= e.max / 2 && (r.x -= 0.03 * x_dist, r.y -= 0.03 * y_dist), // 靠近的时候加速
d = (e.max - dist) / e.max,
context.beginPath(),
context.lineWidth = d / 2,
context.strokeStyle = `rgba(${this.c.color},${d + 0.2})`,
context.moveTo(r.x, r.y),
context.lineTo(e.x, e.y),
context.stroke());
}
}
});
this.requestFrame(this.drawCanvas);
}
destroy() {
clear(this.el);
window.onmousemove = this.onmousemove;
window.onmouseout = this.onmouseout;
cancelAnimationFrame(this.tid);
this.canvas.parentNode.removeChild(this.canvas);
}
}