量子破碎能量球

这是个人工做室,我想要作一个量子破碎能量球,html

const breakBall = {}
复制代码

先设定好球的半径,git

const ballRadius = 60
复制代码

我须要1000块碎片,github

const chipLen = 1000
复制代码

准备个大盒子放碎片,省得不当心丢了,canvas

const chips = []
复制代码

先来设计碎片,量子破碎给个人是感受是「尖锐」的,我打算把碎片作成随机的三角形:bash

由于三点肯定一个圆,因此能够用三个角度来表示一个碎片。但为了防止随机的角度太小,加了些固定数值用来限制,dom

function rdmVertexDeg() {
  return [(0 / 3 + Math.random() / 1.5) * Math.PI,
          (2 / 3 + Math.random() / 1.5) * Math.PI,
          (4 / 3 + Math.random() / 1.5) * Math.PI]
}

复制代码

碎片大小由三点圆的半径决定,且须要在必定的范围内,函数

function rdmSize() {
  return Math.random() * 20
}

复制代码

要让这些碎片组成球状,能够用三角函数的知识,将它们排列起来,ui

function rdmPos2Ball() {
  // 用与球中心的距离、角度肯定碎片的位置及碎片朝向
  return {
    dist: Math.random() * ballRadius,
    deg: Math.random() * Math.PI * 2,
    selfDeg: Math.random() * Math.PI * 2
  } 
}
复制代码

粒子老是保持运动的,我须要让每一个碎片动起来。粒子会受到引力围绕球心运动,且自身也会作自转的运动,就像地球和太阳同样,spa

function rdmRotateDeg() {
  // 随机生成碎片公转和自转的速度,用很小的角度值表示
  return {
    perDeg: Math.PI * 2 * ( 1 - 2* Math.random()) * 0.008,
    perSelfDeg: Math.PI * 2 * ( 1 - 2* Math.random()) * 0.01
  }
}
复制代码

给碎片准备颜料。球颜色的话我打算用「灰色」为底色,「深灰色」为装饰,「淡蓝色」为主色。为了凸显主色,总体带点透明的科幻感。颜色的比值设为 15:10:5,赶忙先记下来,设计

const colorData =  
    [{ occur: 5, alpha: 0.8, style: 'hsl(178, 100%, 60%)' },
    { occur: 10, alpha: 0.8, style: '#222' },
    { occur: 15, alpha: 0.8, style: '#555' }];
复制代码

作一个随机吐色墨喷水管,

const colors = []
colorData.forEach(color => {
  for (let i = 0; i < color.occur; i++) {
    colors.push(color);
  }
});
function rdmColor() {
  return colors[colors.length * Math.random() | 0];
}
复制代码

好啦,设计好了,开始制做碎片,

for(let i = 0; i < chipLen; i++) {
  const chip = {
    color: rdmColor(),
    size: rdmSize() * (i / chipLen), // 我但愿随机碎片有从小到大的趋势
    vertexDeg: rdmVertexDeg(),
    pos2Ball: rdmPos2Ball(),
    rotate: rdmRotateDeg()
  }
  chips.push(chip);
}
复制代码

碎片作好了,我准备把它放到暗黑森林里,因此要申请使用那的入口,

// html
<canvas id="darkForest" style="backgroud:#000"></canvas>

const darkForest = document.getElementById('darkForest')
// 我须要整个森林的使用权
darkForest.width = window.innerWidth
darkForest.height = window.innerHeight
const entry = darkForest.getContext('2d')
复制代码

能量球须要能量来运行它,因此我得买个能量鼠,做为球的引擎。我选了个无限版本,它无时无刻释放出能量,

const mouseEngine = buyAMouseEngine('mousemove');
复制代码

看了下能量鼠的原理,

function buyAMouseEngine(type) {
  return {
    config: function (action) {
      window.addEventListener(type, action);
    }
  }
}
复制代码

能量鼠的位置做为球的中心点,配置一下,

const mousePos = []
mouseEngine.config((e) => {
  mousePos[0] = e.clientX;
  mousePos[1] = e.clientY;
})
// 一开始我但愿它在森林的中心
mousePos[0] = darkForest.width / 2
mousePos[1] = darkForest.height / 2
复制代码

想在黑暗森林里存在,必须提供一份运动轨迹说明,

const explain = function() {
  // 为了准确描述,先重置下申请的入口
  entry.clearRect(0, 0, darkForest.width, darkForest.height)
  
  // 每一个碎片都须要描述一遍
  chips.forEach((chip, idx) => {
    entry.beginPath();
    entry.globalAlpha = chip.color.alpha;
    entry.fillStyle = chip.color.style;
    
    // 计算碎片的坐标
    const pos = []
    chip.pos2Ball.deg += chip.rotate.perDeg;
    pos[0] = Math.cos(chip.pos2Ball.deg) * chip.pos2Ball.dist * (idx/chips.length);
    pos[1] = Math.sin(chip.pos2Ball.deg) * chip.pos2Ball.dist * (idx/chips.length);
    // 计算碎片方向
    chip.pos2Ball.selfDeg += chip.rotate.perSelfDeg;
    
    // 球的总体描述
    entry.moveTo(
    mousePos[0] + pos[0] + Math.cos(chip.vertexDeg[0] + chip.pos2Ball.selfDeg) * chip.size, 
    mousePos[1] + pos[1] + Math.sin(chip.vertexDeg[0]+ chip.pos2Ball.selfDeg) * chip.size)
    
    entry.lineTo(
    mousePos[0] + pos[0] + Math.cos(chip.vertexDeg[1]+ chip.pos2Ball.selfDeg) * chip.size, 
    mousePos[1] + pos[1] + Math.sin(chip.vertexDeg[1]+ chip.pos2Ball.selfDeg) * chip.size)
    
    entry.lineTo(
    mousePos[0] + pos[0] + Math.cos(chip.vertexDeg[2]+ chip.pos2Ball.selfDeg) * chip.size, 
    mousePos[1] + pos[1] + Math.sin(chip.vertexDeg[2]+ chip.pos2Ball.selfDeg) * chip.size)
    
    entry.closePath();
    entry.fill();
  })
  
  requestAnimationFrame(explain);
}
复制代码

提交说明,

explain()
复制代码

啊哈哈,申请成功了,和想象中的同样。
不过移动能量球的时候,缺乏一种动态的魔力感,我得调整下设计。

先加个能量鼠移动时的运动轨迹存储器,

const mouseStack = {}
// 轨迹数据
mouseStack.data = new Array(100).fill(null).map(() =>  [...mousePos]);
// 随机因子
mouseStack.factor = 0
复制代码

随机获轨迹存储器坐标,

function getMouseStack(pos) {
  const stackLen = mouseStack.data.length
  return mouseStack.data[(stackLen + 
  (mouseStack.factor - pos * stackLen) | 0) % stackLen]
}
复制代码

更新下说明explain,从新提交给黑暗森林,

// 在森林里,须要点燃能量鼠存储器的随机因子
mouseStack.factor++;
// 把此刻能量鼠的位置写入存储器
const stack = getMouseStack(0);
stack[0] = mousePos[0];
stack[1] = mousePos[1];
复制代码

将碎片随机排列到运动轨迹上,

const rdmStack = getMouseStack(1 - idx / chips.length);
mousePos[0] = rdmStack[0];
mousePos[1] = rdmStack[1];
复制代码

从新提交说明,

explain()
复制代码

最后我把设计方案整理了下,放在人际关系库里,想要玩玩个人量子破碎能量球,请!

相关文章
相关标签/搜索