基础的页面结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
overflow: hidden;
margin: 0;
}
h1 {
position: fixed;
top: 50%;
left: 0;
width: 100%;
text-align: center;
transform: translateY(-50%);
font-family: 'Love Ya Like A Sister', cursive;
font-size: 40px;
color: #c70012;
padding: 0 20px;
}
@media (min-width:1200px) {
h1 {
font-size: 60px;
}
h1 a {
text-decoration: none;
color: #000;
}
}
</style>
</head>
<body>
<canvas></canvas>
<h1>我会一生一世守护你</h1>
</body>
</html>
这个样子。。。。
还行吧
初始化一些变量,获取 canvas 元素,获取 ctx 上下文。为 canvas 设置画笔的颜色,填充色。hearts 数组为后面设置爱心的数组。
onResize() 函数为 canvas 设置宽高。
let canvas = document.querySelector("canvas")
let ctx = canvas.getContext("2d");
let width, height;
ctx.strokeStyle = "red";
ctx.shadowBlur = 25;
ctx.shadowColor = "hsla(0, 100%, 60%,0.5)";
let hearts = [];
function onResize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
}
onResize();
window.requestAnimationFrame
window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。如果想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用 window.requestAnimationFrame()
。
function render() {
requestAnimationFrame(render)
hearts.push(new Heart())
}
onResize();
requestAnimationFrame(render)
Heart 是一个爱心类。
// 爱心数量
let precision = 100;
class Heart {
constructor(x, y) {
this.x = x || Math.random() * width;
this.y = y || Math.random() * height;
this.size = Math.random() * 2 + 1;
this.shadowBlur = Math.random() * 10;
this.speedX = (Math.random() + 0.2 - 0.6) * 8;
this.speedY = (Math.random() + 0.2 - 0.6) * 8;
this.speedSize = Math.random() * 0.05 + 0.01;
this.opacity = 1;
this.vertices = [];
}
draw() {
// ....
}
}
绘制之前,需要把屏幕进行 clear 操作。循环 hearts 数组,调用 draw 方法进行绘制。
function render() {
requestAnimationFrame(render)
hearts.push(new Heart())
ctx.clearRect(0, 0, width, height);
for (let i = 0; i < hearts.length; i++) {
hearts[i].draw();
}
}
计算爱心的 x,y 坐标
class Heart {
constructor(x, y) {
// ....
for (let i = 0; i < precision; i++) {
let step = (i / precision - 0.5) * (Math.PI * 2);
let vector = {
x: (15 * Math.pow(Math.sin(step), 3)),
y: -(13 * Math.cos(step) - 5 * Math.cos(2 * step) - 2 * Math.cos(3 * step) - Math.cos(4 * step))
}
this.vertices.push(vector);
}
}
}
draw 函数负责绘制。由 ctx.lineTo 函数进行主要的绘制操作。传入之前计算好的 x 和 y 坐标。
在Canvas中有两个属性globalAlpha
和globalCompositeOperation
来控制图像合成操作:
globalAlpha
:设置图像的透明度。globalAlpha
属性默认值为1
,表示完全不透明,并且可以设置从0
(完全透明)到1
(完全不透明)。这个值必须设置在图形绘制之前globalCompositeOperation
:该属性的值在globalAlpha
以及所有变换都生效后控制在当前Canvas位图中绘制图形。
draw() {
this.size -= this.speedSize;
this.x += this.speedX;
this.y += this.speedY;
ctx.save();
ctx.translate(-1000, this.y);
ctx.scale(this.size, this.size);
ctx.beginPath();
for (let i = 0; i < precision; i++) {
let vector = this.vertices[i];
ctx.lineTo(vector.x, vector.y);
}
ctx.globalAlpha = this.size;
ctx.shadowBlur = Math.round((3 - this.size) * 10);
ctx.shadowColor = "hsla(0, 100%, 60%,0.5)";
ctx.shadowOffsetX = this.x + 1000;
ctx.globalCompositeOperation = "screen"
ctx.closePath();
ctx.fill()
ctx.restore();
}
这个效果怎么说呢,算是实现了吧,但问题是,这满屏的红色,属实是不太好看啊。
但 heart 的 size 小于 0 的时候,就表明需要消失在屏幕上了。
function render() {
requestAnimationFrame(render)
hearts.push(new Heart())
ctx.clearRect(0, 0, width, height);
for (let i = 0; i < hearts.length; i++) {
hearts[i].draw();
if (hearts[i].size <= 0) {
hearts.splice(i, 1);
i--;
}
}
}
当鼠标移动的时候,生成 heart 。
两次 push 是为了爱心多一点。
function onMove(e) {
hearts.push(new Heart(e.clientX, e.clientY));
hearts.push(new Heart(e.clientX, e.clientY));
}
window.addEventListener("mousemove", onMove);
兼容移动端的话
function onMove(e) {
if (e.type === "touchmove") {
hearts.push(new Heart(e.touches[0].clientX, e.touches[0].clientY));
hearts.push(new Heart(e.touches[0].clientX, e.touches[0].clientY));
} else {
hearts.push(new Heart(e.clientX, e.clientY));
hearts.push(new Heart(e.clientX, e.clientY));
}
}
window.addEventListener("touchmove", onMove);
最后来看一下效果吧。
(这玩意啊,自己写着玩玩就行,千万别给自己女朋友,或者喜欢的人。七夕还是送 SKII 比较好)
我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!