趣味Canvas之这都是满屏的Love❤️

基础的页面结构

<!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>

图片.png

这个样子。。。。

还行吧


初始化一些变量,获取 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中有两个属性globalAlphaglobalCompositeOperation来控制图像合成操作:

  • globalAlpha:设置图像的透明度。globalAlpha属性默认值为1,表示完全不透明,并且可以设置从0(完全透明)到1(完全不透明)。这个值必须设置在图形绘制之前
  • globalCompositeOperation:该属性的值在globalAlpha以及所有变换都生效后控制在当前Canvas位图中绘制图形。

图片.png

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();
}

Jul-23-2022 08-54-00.gif

这个效果怎么说呢,算是实现了吧,但问题是,这满屏的红色,属实是不太好看啊。

但 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 比较好)

Jul-22-2022 20-57-38.gif

我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!

猜你喜欢

转载自juejin.im/post/7123372907468144677