CSS样式
body {
background-color: rgba(255, 137, 164, 0.2);
background-size: cover;
background-repeat: no-repeat;
}
.liker {
position: absolute;
top: 0%;
left: 50%;
transform: translate(-50%, 0%);
}
.liker span {
position: absolute;
font-family: monospace;
color: #F92A82;
bottom: -70px;
left: 50%;
transform: translateX(-50%);
}
.btn {
width: 50px;
display: flex;
justify-content: center;
align-items: center;
height: 50px;
border-radius: 50%;
border: none;
position: absolute;
cursor: pointer;
left: 50%;
transform: translate(-50%, -10%);
background-color: #FF4D80;
color: #fff;
outline: none;
transition: 0.3s ease;
box-shadow: 0 3px 10px #F92A82;
}
.btn svg {
fill: #fff;
}
.btn:hover {
background-color: #F92A82;
}
js代码
class HeartsFlow {
constructor(data) {
this.el = document.querySelector(data.canvasEl);
this.w = 200;
this.h = 400;
this.ctx = this.el.getContext('2d');
this.colors = [
'255, 137, 164', //'#FF89A4',
'239, 121, 138', //'#EF798A',
'255, 77, 128', //'#FF4D80',
'249, 42, 130' //'#F92A82'
];
this.heartsAmount = data.amount;
this.heartsList = [];
this.isAnimate = false;
this.raf = null;
this.animate = this.animate.bind(this);
this.paintHeart = this.paintHeart.bind(this);
this.stopAnimation = this.stopAnimation.bind(this);
this.init();
}
getRandomColor() {
return this.colors[Math.floor(Math.random() * this.colors.length)];
}
getRandom(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
setHeartsList() {
let arr = [];
for (let i = 0; i < this.heartsAmount; i++) {
let currentSize = this.getRandom(10, 15);
let dt = {
x: this.w / 2,
y: this.h,
bx: this.w / 2,
by: this.h,
pos: this.h,
_osp: this.getRandom(200, 400) / 100,
osp: this.getRandom(11, 12) / 10,
vsp: this.getRandom(currentSize, currentSize + i * 2) / 1000,
size: currentSize,
color: this.getRandomColor(),
alfa: 1 };
arr.push(dt);
}
this.heartsList = [...this.heartsList, ...arr];
}
getCoordinates({ x, y, size, color, bx, by, _osp, osp, vsp, pos, alfa }) {
return {
xst: x,
yst: y + size / 2,
x0: x - size / 1.4,
y0: y + size / 4,
x1: x - size / 1.3,
y1: y - size / 1.3,
_x0: x + size / 1.4,
_y0: y + size / 4,
_x1: x + size / 1.3,
_y1: y - size / 1.3,
xfn: x,
yfn: y - size / 3,
bx: bx,
by: by,
_osp: _osp,
osp: osp,
vsp: vsp,
pos: pos,
alfa: alfa,
size: size,
color: color };
}
paintHeart({ xst, yst, x0, y0, _x0, _y0, x1, y1, _x1, _y1, xfn, yfn, color, alfa }) {
this.ctx.globalCompositeOperation = "lighter";
this.ctx.beginPath();
this.ctx.moveTo(xst, yst);
this.ctx.bezierCurveTo(x0, y0, x1, y1, xfn, yfn);
this.ctx.moveTo(xst, yst);
this.ctx.bezierCurveTo(_x0, _y0, _x1, _y1, xfn, yfn);
this.ctx.fillStyle = `rgba(${color}, ${alfa})`;
this.ctx.strokeStyle = `rgba(${color}, ${alfa})`;
this.ctx.fill();
this.ctx.stroke();
this.ctx.closePath();
}
mutateData() {
this.heartsList = this.heartsList.map(item => {
let pos = item.pos - 0.05;
let x = item.x + Math.sin(pos * item._osp) * ((pos - item.by) / item.osp);
let y = pos + (pos - item.by) / item.vsp * 1.6;
let alfa = this.normalize0between1(0, this.h, y).toFixed(1);
return {
...item, x: x, y: y, pos: pos, alfa: alfa };
});
this.heartsList = this.heartsList.filter(item => item.y > 0);
}
normalize0between1(min, max, value) {
return (value - min) / (max - min);
}
setCanvas() {
this.el.width = this.w;
this.el.height = this.h;
}
startAnimation() {
if (!this.isAnimate) {
this.isAnimate = true;
console.log('start animation');
this.setHeartsList();
this.animate();
} else {
this.setHeartsList();
}
}
stopAnimation() {
this.isAnimate = false;
console.log('stop animation');
cancelAnimationFrame(this.raf);
}
animate() {
this.ctx.clearRect(0, 0, this.w, this.h);
if (this.isAnimate) {
for (let i = 0, len = this.heartsList.length; i < len; i++) {
let hrt = this.getCoordinates(this.heartsList[i]);
this.paintHeart(hrt);
}
this.mutateData();
}
this.raf = requestAnimationFrame(this.animate);
if (this.heartsList.length === 0 && this.isAnimate) {
this.stopAnimation();
}
}
init() {
this.setCanvas();
this.setHeartsList();
this.animate();
}}
let ht = new HeartsFlow({
canvasEl: '.hearts-canvas',
amount: 20 });
let btn = document.querySelector('.btn');
btn.addEventListener('click', function () {
ht.startAnimation();
});
html使用模板,记得引入上面的css和js
<div class="liker">
<canvas class="hearts-canvas" width="200" height="400"></canvas>
<button class="btn">
<svg width="24" height="24" viewBox="0 0 24 24">
<path d="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"></path>
</svg>
</button>
<span>点击查看</span>
</div>