谁是炮仗王🧨?春哥手把手带你炸掘金!

4,595 阅读4分钟

PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛

炸掘金

在我小的时候,逢年过节,我就是村里名副其实的“炮王”,炸"玻璃瓶"、炸"鱼"、炸"石头"、炸"粪坑"……

不知不觉,我已经到了30好几,到了年节我再也不是“炮王”。

但今年,我准备干一票大的:把 “掘金” 给炸了!

说到做到!我,代码暴徒,肆无忌惮!

炸pc

光炸网页端还不够!把它们 APP 也给端了! 炸APP

手把手带你写

1. 需要了解的前置知识

  • 德劳内三角化:又被称为 Delaunay 三角网,是一种将平面或曲面分割成 N 个不交叉三角形的图形学处理技术。
    (我们用它把掘金打成碎片!)
  • animejs:一款被广泛使用的前端动画库。
    (我们用它把掘金扔向深渊!)

2. 如何点“炮仗”?

我不生产爆炸物,我是炮仗的搬运工。

点炮仗

“炮仗”部分的实现来自掘友 @creen青春 的一篇文章:用svg实现鞭炮计时器

简单来说,分三步:

  1. svg 技术画一个 🧨。
  2. animejs 绘制引线变短动画。
  3. animejs 绘制火星移动动画。

详细玩法,还请大家移步原帖:用svg实现鞭炮计时器

3. 如何产生爆炸效果?

哪有什么爆炸?只有发光变大的 div 而已。

boom!

效果唬人的爆炸光波,闪光弹效果,实现起来可太容易了!

我们只需要让一个宽高为 0pxdiv 膨胀到整个屏幕大小,然后再缓缓缩小它即可。 how to boom 01 为了增加爆炸发生时的“炫目感”,我们可以让背景在这个过程中也快速变黑,然后缓缓退散。

代码如下:

  // 生成时间轴
  const timeline = animejs.timeline()
  // 爆炸光波div放大并变亮(其实是变白)
  timeline.add({
    targets: sparkRef.current,
    backgroundColor: {
      value: '#fff',
      duration: 300
    },
    width: 1 * Math.max(bodyClientHeight, bodyClientWidth),
    height: 1 * Math.max(bodyClientHeight, bodyClientWidth),
    duration: 1000
  })
  // 背景变暗
  timeline.add({
    targets: maskRef.current,
    backgroundColor: `rgba(0,0,0,0.8)`,
    duration: 800,
    easing: 'easeInOutCirc',
  }, '-=1000')
  // 爆炸光波div缩小,并逐渐变透明
  timeline.add({
    targets: sparkRef.current,
    width: 0,
    height: 0,
    opacity: 0,
    duration: 3000,
    easing: 'easeInOutCirc',
  })
  // 背景变得透明
  timeline.add({
    targets: maskRef.current,
    backgroundColor: `rgba(0,0,0,0)`,
    duration: 3000,
    opacity: 0,
    easing: 'easeInOutCirc',
  }, '-=3000')

4. 如何让掘金“破碎”?

先了解一下“德劳内三角化”到底有什么功效。

德劳内三角化

如上图所示,“德劳内三角化”可以让一堆散落的点,连成若干个互相不交叉的三角形。因此,我们的思路如下:

通过这种算法实现,我们可以通过在一个矩形上附加若干个点,然后实现它被“打碎”的效果。

破碎思路

在这个基础上,为了让它“破碎”得更有真实感,我们可以增加一些层次感。

打点思路

我们围绕中心点画若干个同心圆,然后在这个几个同心圆的圆弧周围画若干个点,这样就能营造出一种玻璃碎裂的感觉。如下:

碎裂效果

最后效果就能打成中间密集,四周稀疏的效果。

5. 如何让碎片跌落深远?

在做这一步之前,我们得把 第4步 产生的点进行进行剪切,我的做法是“把它们剪切成若干个 canvas 画布”。

我们所要做的,有以下几个步骤:

    1. 计算出每个三角形最小 canvas 矩形的大小和位置。
    1. 通过canvas.drawImage()方法渲染出它们应该渲染的部分。
    1. 通过canvas.2DContext.clip()方法剪切出三角形形状。

canvas构建思路

我将这样的碎片定义为 Fragment

export interface Box {
  x: number,
  y: number,
  w: number,
  h: number
}

export default class Fragment {
  public pointA: Array<number>; // 三角形顶点A
  public pointB: Array<number>; // 三角形顶点B
  public pointC: Array<number>; // 三角形顶点C
  public box: Box; // 盒子
  public centroid: Array<number>; // 中心
  public canvas: HTMLCanvasElement; // canvas 元素
  public ctx: CanvasRenderingContext2D | null; // canvas 2d ctx
  // ...代码过多,有兴趣的可以看源码
}

那么要怎么让给这些 canvas 坠落向深远呢?

也是需要三步:

  1. 翻转动画
  2. 不断变小
  3. 向中心点靠拢

代码如下:

  timeline.add({
    targets: fragment.canvas,
    scale: {
      value: 0,
      duration: 1000, // 变小
    },
    opacity: { // 变透明
      value: 0,
      duration: 400,
      delay: delay + 600
    },
    left: canvasWidth/2 - fragment.box.w/2, // 移动向中心
    top: canvasHeight/2 - fragment.box.h/2, // 移动向中心
    rotateX: { // X轴翻转
      value: rx,
      duration: 400,
      delay: delay + 600
    },
    rotateY: { // Y轴翻转
      value: ry,
      duration: 400,
      delay: delay + 600
    },
    easing:'cubicBezier(0.420, 0.000, 1.000, 1.000)', // 贝塞尔曲线,缓=>快=>缓
    duration: 1000,
  }, delay);

获取源码

源码地址: github.com/zhangshichu…