共计 17149 个字符,预计需要花费 43 分钟才能阅读完成。
根据上一篇转盘抽奖,在开发消消乐游戏上又扩展了一下目录结构
变更如下:
新增 mvc 思想
新增事件派发机制
添加波动平均算法
添加费雪耶兹算法
添加时间控制器
取消精灵构建类
导演类 dirctor 变成 mvc 入口
游戏运行移到控制器 control 里面
需要技能
1、pixi.js 和 tweenMax.js。(这两个主要用在视图层,开发游戏精灵,也可以用原生 canvas 代替)2、初步了解一下 mvc 的模式
目录结构
Timer
时间控制器,主要是封装一下游戏运行状态和对 requestAnimation 进行封装
// 时间控制器
class Timer {constructor() {this.showSpirt = [];
this.START = 1; // 开始
this.END = 2; // 结束
this.PAUSE = 3; // 暂停
this.ERROR = 4; // 异常
this.state = this.START;
this.lastTime = 0;
this.timer = null;
this.timeDown = null;
this.totalTime = 30;
}
run(fn) {
var self = this;
var requestAnimation =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame;
function ani() {fn(self.timeDown);
if (self.state != self.END) {requestAnimation(ani);
}
}
this.timeDown = new Date().getTime() + this.totalTime * 1000;
requestAnimation(ani);
}
}
export default Timer;
Index
游戏入口主要是初始化游戏和懒加载添加所需插件
import Director from './director';
import $loader from '../../../common/util/loader';
import Loading from '../../../core/comp/loading/loading';
/*
* popHappy 消消乐
* */
class Game {constructor(dataStore, res) {
this.gameManager = dataStore.gameManager;
// 数据层, 保存游戏全部数据
this.dataStore = dataStore;
this.resource = res;
this.$container = dataStore.$container;
this.load = Loading.getInstance(dataStore.$gameConfig.container);
this.load.hideLoading();}
addJs() {return Promise.all([$loader.$loaderPixi(), $loader.$loaderTweenMax()]);
}
// 游戏开始运行
start() {
// 导演实例,游戏执行核心
this.load.showLoading();
this.addJs().then(_ => {this.load.hideLoading();
this.director = new Director(this.dataStore);
this.director.enter();});
}
}
export default Game;
Config
const config = {
containWidth: 660, // 容器宽度
containHeight: 950, // 容器高度
containPaddingLeft: 20, // 左边填充值
containPaddingTop: 20, // 上面填充值
containY: 100, // 容器 Y 轴坐标
containCol: 6, // 网格的列数量
containRow: 9, // 网格的行数量
containColMargin: 6, // 网格列之间距离
containRowMargin: 4, // 网格行之间距离
spirtWidth: 110, // 精灵元素宽度
spirtHeight: 106 // 精灵元素高度
};
export default config;
Event
事件派发机制,主要是派发事件,用做组件通信
/**
* @ author: leeenx
* @ 事件封装
* @ object.on(event, fn) // 监听一个事件
* @ object.off(event, fn) // 取消监听
* @ object.once(event, fn) // 只监听一次事件
* @ object.dispacth(event, arg) // 触发一个事件
*/
export default class Events {constructor() {
// 定义的事件与回调
this.defineEvent = {};}
// 注册事件
register(event, cb) {if (!this.defineEvent[event]) {this.defineEvent[event] = [cb];
} else {this.defineEvent[event].push(cb);
}
}
// 派遣事件
dispatch(event, arg) {if (this.defineEvent[event]) {
/* eslint-disable */
{
for (let i = 0, len = this.defineEvent[event].length;
i < len;
++i
) {this.defineEvent[event][i] &&
this.defineEvent[event][i](arg);
}
}
}
}
// on 监听
on(event, cb) {return this.register(event, cb);
}
// off 方法
off(event, cb) {if (this.defineEvent[event]) {if (typeof cb == 'undefined') {delete this.defineEvent[event]; // 表示全部删除
} else {
// 遍历查找
for (let i = 0, len = this.defineEvent[event].length;
i < len;
++i
) {if (cb == this.defineEvent[event][i]) {this.defineEvent[event][i] = null; // 标记为空 - 防止 dispath 长度变化
// 延时删除对应事件
setTimeout(() => this.defineEvent[event].splice(i, 1),
0
);
break;
}
}
}
}
}
// once 方法,监听一次
once(event, cb) {let onceCb = () => {cb && cb();
this.off(event, onceCb);
};
this.register(event, onceCb);
}
}
Dirctor
导演类,初始化 mvc,游戏初始化布局入口,同时监听游戏结束业务逻辑。
import Model from './core/Model';
import View from './core/View';
import Control from './core/Control';
import Director from '../../comp/director/director';
class EqxDir extends Director {constructor(dataStore) {let { gameManager, $gameConfig} = dataStore;
super(gameManager);
this.dataStore = dataStore;
this.$gameConfig = $gameConfig;
// 初始化 mvc
this.model = new Model();
this.view = new View(dataStore);
// mv 由 c 控制
this.constrol = new Control(this.model, this.view);
this.event = this.constrol.event;
// 监听游戏结束,请求提交分数接口
this.event.on('game-over', score => {this.gameOver(score);
});
}
enter() {this.constrol.enter();
}
}
export default EqxDir;
View
视图层:
通过 pixi.js 初始化布局页面效果
通过 tweenMax.js 对精灵做动画效果处理
updated 函数,监听 model 数据变化来处理视图显示
import config from '../config';
import HOST from '../../../../common/host';
import {
tapstart,
tapmove,
tapend
} from '../../../../core/common/util/compaty';
export default class View {constructor(dataStore) {// dataStore.$container.find('canvas').remove();
this.gameJson = dataStore.gameJson;
this.$gameConfig = dataStore.$gameConfig;
this.width = this.setCanvas(dataStore.$gameConfig.container).width; // 设置容器宽高
this.height = this.setCanvas(dataStore.$gameConfig.container).height; // 设置容器宽高
let app = new PIXI.Application({
width: this.width,
height: this.height,
// backgroundColor: 0xff0000,
resolution: 1
});
Object.assign(this, app);
this.view = app.view;
dataStore.$container.prepend(app.view);
// 表格尺寸
this.gridWidth = config.containWidth;
this.gridHeight = config.containHeight;
// 表格的行列数
this.col = config.containCol;
this.row = config.containRow;
// spirte
this.spriteWidth = config.spirtWidth;
this.spriteHeight = config.spirtHeight;
// 砖块数组
this.tiles = new Array(config.containRow * config.containCol);
// 游戏背景
let emptySprite = PIXI.Sprite.fromImage(HOST.FILE + this.gameJson.staticSpirts.BGIMG.imgUrl);
emptySprite.width = this.width;
emptySprite.height = this.gameJson.staticSpirts.BGIMG.height;
emptySprite.position.x = 0;
emptySprite.position.y = 0;
this.stage.addChild(emptySprite);
// 绘制游戏区域
this.area = new PIXI.Container();
this.area.width = 660;
this.area.height = 950;
this.area.x = 45;
this.area.y = 150;
// 绘制一个矩形
let rect1 = new PIXI.Graphics();
rect1.beginFill(0x000000, 0.6);
rect1.lineStyle();
rect1.drawRect(0, 0, this.area._width, this.area._height);
rect1.endFill();
this.area.addChild(rect1);
this.area.mask = rect1;
// 绘制遮罩
let rect2 = new PIXI.Graphics();
rect2.beginFill(0x000000, 0.6);
rect2.lineStyle();
rect2.drawRect(0, 0, this.area._width, this.area._height);
rect2.endFill();
this.area.addChild(rect2);
// 游戏单独一个容器
this.game = new PIXI.Container();
// 添加到舞台
this.game.addChild(this.area);
// 添加到舞台
this.stage.addChild(this.game);
// this.paused
this.paused = true;
this.stage.addChild(this.drawScore(), this.drawTimer());
// 添加点击事件
this.addClick();
// 添加监控
this.addWatch();
this.total = 0;
this.time = 30;
}
init() {
// 添加监控时间事件
this.event.on('view-time', time => {this.time = time;});
// 显示游戏界面
this.showGame();
// 开启点击
this.area.interactive = true;
// 显示砖块
this.area.renderable = true;
let arr = this.tiles.map((tile, index) => {let { col, row} = this.getColAndRow(tile.index);
/* eslint-disable */
return this.topToDown.call(this, col, row, tile, index);
});
Promise.all(arr).then(() => {
// 派发下掉动作完成,开启消消乐功能
this.event.dispatch('view-start');
});
}
addWatch() {
Reflect.defineProperty(this, 'total', {get: () => this._total || 0,
set: value => {
this._total = value;
this.scoreLabel.text = value;
}
});
Reflect.defineProperty(this, 'time', {get: () => this._time || 30,
set: value => {
this._time = value;
this.timeLabel.text = value;
}
});
}
drawScore() {
// 绘制头像,分数组合和透明矩形
return scoreC;
}
drawTimer() {
// 绘制时间,文本和遮罩
return scoreC;
}
addClick() {
let isClick = false,
initX,
initY,
initTime,
cScale = this.$gameConfig['cScale'] || 1;
// 添加移动开始事件
this.view.addEventListener(tapstart, event => {if (this.paused === true) return;
initX = event.offsetX / cScale - this.area.x;
initY = event.targetTouches[0].clientY - this.area.y;
initTime = new Date().getTime();
});
this.view.addEventListener(tapmove, event => {
// 暂停不触发事件, 移动过程中,不出发移动事件
if (this.paused === true) return;
let time = new Date().getTime();
if (time - initTime >= 30) {
// 移动只触发一次
if (isClick == true) return;
isClick = true;
// let x = event.offsetX / cScale - this.area.x;
// let y = event.offsetY / cScale - this.area.y;
let x = event.targetTouches[0].clientX - this.area.x;
let y = event.targetTouches[0].clientY - this.area.y;
let angel = getAngel({x: initX, y: initY}, {x, y});
let orientation = 0;
if (angel >= -45 && angel < 45) {orientation = 3;} else if (angel >= -135 && angel < -45) {orientation = 0;} else if (angel >= 45 && angel < 135) {orientation = 1;} else {orientation = 2;}
let col = (initX / this.spriteWidth) >> 0,
row = (initY / this.spriteHeight) >> 0;
let position = col * this.row + row;
this.event.dispatch('view-tap', { position, orientation});
}
});
this.view.addEventListener(tapend, function(event) {
// 暂停不触发事件
setTimeout(() => {isClick = false;}, 600);
});
// 计算角度
function getAngel(origin, target) {let rX = target['x'] - origin['x'];
let rY = target['y'] - origin['y'];
let angel = (Math.atan2(rY, rX) / Math.PI) * 180;
return angel;
}
}
// 初始化下掉动画
topToDown(col, row, tile, i) {
return new Promise(resolve => {
TweenMax.to(tile.sprite, 0.5, {
x: col * this.spriteWidth + this.spriteWidth / 2,
y: row * this.spriteHeight + this.spriteHeight / 2,
delay: ((i / this.col) >> 0) * 0.05,
ease: Linear.easeNone,
onComplete: () => {resolve();
}
});
});
}
// 获取当前砖块的横纵位置
getColAndRow(index) {// let { index} = tile;
let col = (index / this.row) >> 0;
let row = index % this.row;
return {col, row};
}
// 生成对应的精灵
generateSpirt(clr = 5) {
let imgObj = [HOST.FILE + this.gameJson.dynamicSpirts[0],
HOST.FILE + this.gameJson.dynamicSpirts[1],
HOST.FILE + this.gameJson.dynamicSpirts[2],
HOST.FILE + this.gameJson.dynamicSpirts[3],
HOST.FILE + this.gameJson.dynamicSpirts[4]
];
/* eslint-disalbe */
let sprite = new PIXI.Sprite.fromImage(imgObj[clr]);
sprite.width = this.spriteWidth;
sprite.height = this.spriteHeight;
sprite.x = 280;
sprite.anchor.x = 0.5;
sprite.anchor.y = 0.5;
return sprite;
}
// 更新砖块
update({originIndex, index, clr, removed, score, type}) {if (originIndex === undefined || clr === undefined) return;
let tile = this.tiles[originIndex];
// tile 不存在,生成对应砖块
if (tile === undefined) {this.tiles[originIndex] = tile = {sprite: this.generateSpirt(clr),
clr,
originIndex,
index,
removed: false
};
// 添加到舞台
this.area.addChild(tile.sprite);
}
if (tile.removed !== removed) {this.bomb(removed, tile, index);
}
// index 当前索引发生改变,表示位置发生改变
if (tile.index !== index) {this.updateTileIndex(tile, index, type);
}
// tile 存在,判断颜色是否一样
else if (tile.clr !== clr) {this.updateTileClr(tile, clr);
}
}
// 砖块位置变化
updateTileIndex(tile, index, type) {let { col, row} = this.getColAndRow(index || tile.originIndex);
let x = col * this.spriteWidth;
let y = row * this.spriteHeight;
if (type == 2) {
// 交换位置
TweenMax.to(tile.sprite, 0.2, {
x: x + this.spriteWidth / 2,
y: y + this.spriteHeight / 2,
ease: Linear.easeNone
});
} else if (tile.index < index) {
// 游戏过程,未消除的砖块下落
TweenMax.to(tile.sprite, 0.2, {
x: x + this.spriteWidth / 2,
y: y + this.spriteHeight / 2,
delay: 0,
ease: Linear.easeNone
});
}
tile.index = index;
}
// 颜色发生改变
updateTileClr(tile, clr) {if (clr === undefined) return;
tile.sprite = this.generateSpirt(clr);
tile.clr = clr;
}
// 消除砖块和添加砖块
bomb(removed, tile, index) {if (removed === true) {
// 游戏过程,有动画 缩小
TweenMax.to(tile.sprite, 0.2, {
width: 0,
height: 0,
ease: Linear.easeNone,
onComplete: () => {this.area.removeChild(tile.sprite);
tile.sprite.width = this.spriteWidth;
tile.sprite.height = this.spriteHeight;
tile.removed = removed;
this.total += 3;
}
});
} else {
// 从上倒下下落动画
this.area.addChild(tile.sprite);
let {col, row} = this.getColAndRow(index);
let x = col * this.spriteWidth;
let y = row * this.spriteHeight;
// 游戏过程,有动画
TweenMax.fromTo(
tile.sprite,
0.2,
{
x: x + this.spriteWidth / 2,
y:
-this.spriteHeight * (this.row - row) +
this.spriteHeight / 2,
delay: 0,
ease: Linear.easeNone
},
{
x: x + this.spriteWidth / 2,
y: y + this.spriteHeight / 2,
delay: 0,
ease: Linear.easeNone,
onComplete: () => {tile.removed = removed;}
}
);
}
}
// 显示游戏界面
showGame() {this.game.renderable = true;}
// 设置容器宽高
setCanvas($container) {
let container =
$container.selector == 'body' ? $container : $container.parent();
let w = container.width();
let h = container.height();
let width = 750;
let height = (h * width) / w;
return {width, height};
}
// 暂停按钮
stop() {this.paused = true;}
// 恢复渲染
resume() {this.paused = false;}
}
Model
数据层,主要做数据处理,包括砖块数量、打散砖块、改变位置、计算消除砖块
import quickWave from '../libs/quickWave';
import shuffle from '../libs/shuffle';
import config from '../config';
export default class Model {constructor() {
// 行列数
this.row = config.containRow;
this.col = config.containCol;
// 表格总数 6*9
this.gridCellCount = config.containCol * config.containRow;
// 砖块
this.tiles = new Array(this.gridCellCount);
for (let i = 0; i < this.gridCellCount; ++i) {this.tiles[i] = {
// 是否移除
removed: false
};
}
// 游戏状态
this.state = true;
}
// 填充数组 ---- count 表示几种颜色
init() {
// 色砖小计数
let subtotal = 0;
// 波动均分色块
let arr = quickWave(5, 4, 4); // 此处可优化,业务逻辑可以放在均分内部
arr.forEach((count, clr) => {
count += 11;
// 色砖数量
while (count-- > 0) {let tile = this.tiles[subtotal++];
tile.clr = clr;
}
});
// 打散 tiles
shuffle(this.tiles);
// 存入 grid
this.grid = this.tiles.map((tile, index) => {
// 实时索引
tile.index = index;
// 原索引
tile.originIndex = index;
// 默认在舞台上
tile.removed = false;
// 欲消除状态
tile.status = false;
// 默认是消除换位
tile.type = 1;
return tile;
});
}
// 消除砖块
is() {let newGrid = [...this.grid];
// 竖消,判断砖块欲消除状态
for (let i = 0; i < this.col; i++) {let xBox = newGrid.splice(0, this.row);
this.setxBox(xBox);
}
// 横消,判断砖块欲消除状态
for (let i = 0; i < this.row; i++) {let xBox = [];
for (let j = 0; j < this.row * this.col; j += this.row) {xBox.push(this.grid[i + j]);
}
this.setxBox(xBox);
}
// 通过欲消除状态,改变在舞台的呈现形式 status 赋值给 removed
this.grid.forEach(tile => {tile.removed = tile.status;});
// 消除砖块后,砖块的 index 值改变
this.changeIndex();}
setxBox(arr) {
// 把欲消除的内容 status 标记为 true
for (let i = 0; i < 5; i++) {let rBox = [];
let xBox = [];
let len = arr.length;
arr.forEach((tile, index) => {if (tile.clr == i && index != len - 1) {
// 不是最后一位,同一种颜色 push 到欲消除数组
xBox.push(tile);
} else if (
tile.clr == i &&
index == len - 1 &&
xBox.length >= 2
) {
// 最后一位,并且内部可消除满足 3 个 放到欲消除数组,同时合并到结果数组里面
xBox.push(tile);
rBox = [...rBox, ...xBox];
} else if (xBox.length < 3) {
// 删除欲消除数组
xBox.length = 0;
} else {
// 把消除数组放到结果数组里
rBox = [...rBox, ...xBox];
xBox.length = 0;
}
});
if (rBox.length > 2) {
rBox.forEach(tile => {tile.status = true;});
}
}
}
// 改变 index
changeIndex() {
// 竖直移动
let newGrid = [];
for (let i = 0; i < this.col; i++) {let xBox = this.grid.splice(0, this.row);
newGrid = [...newGrid, ...this.setIBox(xBox)];
}
this.timer && clearTimeout(this.timer);
// 等消失之后在重新计算
this.timer = setTimeout(() => {this.grid = newGrid.map((tile, index) => {if (tile.removed == true) {tile.clr = (Math.random() * 5) >> 0;
this.paused = true;
}
// 默认在舞台上
tile.removed = false;
// tile.originIndex = index;
tile.status = false;
tile.type = 1;
// 实时索引
tile.index = index;
return tile;
});
if (this.paused == true && this.state) {setTimeout(() => {this.is();
this.paused = false;
}, 500);
} else {this.move = true;}
}, 300);
}
// 把每一列的消除项添坑,并重新导出
setIBox(arr) {
let len = arr.length;
let newArr = [];
for (let i = len - 1; i >= 0; i--) {if (arr[i].removed == true) {newArr.unshift(arr.splice(i, 1)[0]);
}
}
arr = [...newArr, ...arr];
return arr;
}
// 更改两个点坐标
setTileDoubleIndex({position, orientation}) {let obj = { 0: -1, 1: 1, 2: -9, 3: 9};
let one = position; // 目标位置
let two = position + obj[orientation]; // 被交换位置
let topBorder = parseInt(one / this.row) * this.row; // 上边界
let bottomBorder = parseInt(one / this.row) * this.row + this.row; // 底边界
// 判断替换不能出边界,不能超过总边界,如果是上下方向,不能超过当前上下边界
if (
two < 0 ||
two > 53 ||
((orientation == 0 || orientation == 1) &&
(two < topBorder || two >= bottomBorder))
) {return;}
// 两个砖块交换 index,let tileOneIndex = this.grid[one].index;
let tileTwoIndex = this.grid[two].index;
this.grid[one].type = 2;
this.grid[one].index = tileTwoIndex;
this.grid[two].type = 2;
this.grid[two].index = tileOneIndex;
let tile = this.grid[one];
this.grid[one] = this.grid[two];
this.grid[two] = tile;
// 校验每一个是否有消除状态
if (this.checkOne(one) || this.checkOne(two)) {setTimeout(() => {this.is();
this.paused = false;
this.move = false;
}, 500);
} else {
// 不能消除,把替换的位置,在替换回来
setTimeout(() => {let tileOneIndex = this.grid[one].index;
let tileTwoIndex = this.grid[two].index;
this.grid[one].type = 2;
this.grid[one].index = tileTwoIndex;
this.grid[two].type = 2;
this.grid[two].index = tileOneIndex;
let tile = this.grid[one];
this.grid[one] = this.grid[two];
this.grid[two] = tile;
this.paused = true;
this.move = true;
}, 200);
}
}
/**
* 检测单个是否可以消除
*/
checkOne(position) {let clr = this.grid[position].clr;
let obj = {0: -1, 1: 1, 2: -9, 3: 9};
let fanObj = {0: 1, 1: 0, 2: 3, 3: 2};
let topBorder = parseInt(position / this.row) * this.row;
let bottomBorder = parseInt(position / this.row) * this.row + this.row;
let statue = false;
let index = 1;
// 方向判断是否可以消除
function getOri(position, orientation, step) {
// 满足 3 个跳出递归
if (index >= 3) {return;}
let two = position + obj[orientation] * step;
if (
two < 0 ||
two > 53 ||
((orientation == 0 || orientation == 1) &&
(two < topBorder || two >= bottomBorder))
) {// 如果出边界不处理} else if (this.grid[two].clr == clr) {
index++;
getOri.call(this, this.grid[two].index, orientation, 1);
getOri.call(this, this.grid[two].index, fanObj[orientation], 2);
}
}
for (let i in obj) {
index = 0;
getOri.call(this, position, i, 1);
if (index >= 3) {statue = true;}
}
// 返回当前,校验状态
return statue;
}
/**
* @ 检查是否死局
* @ 非死局会返回一个索引值
* @ 死局返回 false
*/
check() {if (this.tileCount === 0) return false;
return true;
}
}
Control
包括:监听每一个砖块属性变化、注册游戏结束事件、初始化 view 和 model
import Event from '../libs/Event';
import Timer from '../timer';
import {changeTimeStamp} from '../../../common/timeDown';
export default class Control {constructor(model, view) {
this.model = model;
this.view = view;
// event 事件
this.event = new Event();
// view 与 control 共享一个 event
this.view.event = this.event;
// timer
let timer = new Timer();
// 数据绑定: model.tiles -> view.tiles
model.tiles.forEach(tile => {
Reflect.defineProperty(tile, 'index', {
set: value => {if (value === tile._index) return false;
Reflect.set(tile, '_index', value);
// 与 view 同步数据
view.update(tile);
},
get: () => Reflect.get(tile, '_index')
});
Reflect.defineProperty(tile, 'clr', {
set: value => {if (value === tile._clr) return false;
Reflect.set(tile, '_clr', value);
// 与 view 同步数据
view.update(tile);
},
get: () => Reflect.get(tile, '_clr')
});
Reflect.defineProperty(tile, 'removed', {
set: value => {if (value === tile._removed) return false;
Reflect.set(tile, '_removed', value);
// 与 view 同步数据
view.update(tile);
},
get: () => Reflect.get(tile, '_removed') || false
});
});
// 监听 model 数据运行格式
Reflect.defineProperty(model, 'move', {
set: value => {if (value === model._paused) return false;
Reflect.set(model, '_move', value);
// 与 view 同步数据
if (value) {this.resume();
} else {this.stop();
}
},
get: () => Reflect.get(model, '_move') || false
});
// 监听点击事件
this.event.on('view-tap', moveObj => {
// 暂停状态下锁屏
if (this.paused === true) return;
// 消除 model 的砖块
model.setTileDoubleIndex(moveObj);
});
// 开启消消乐功能
this.event.on('view-start', () => {setTimeout(() => {this.model.is();
timer.run(timeDown => {let data = changeTimeStamp(timeDown);
let time = 0;
if (data) {time = data.sec + '.' + data.ms.toString().substr(0, 2);
} else {if (model.move === true) {
timer.state = timer.END;
time = '0.00';
model.state = false;
model.move = false;
// 派发游戏结束
this.event.dispatch('game-over', view.total);
}
}
this.event.dispatch('view-time', time);
});
}, 500);
});
}
// 初关卡
init() {
// 默认五个颜色
this.model.init();
// 砖块动画
this.view.init();}
// 指定关数
enter() {this.init();
}
// 恢复游戏
resume() {
// 恢复渲染
this.view.resume();
// 标记恢复
this.paused = false;
}
// 暂停游戏
stop() {
// 恢复渲染
this.view.stop();
// 标记恢复
this.paused = true;
}
}
波动平均算法
主要是快速分配方法,每次动态获取当前数值的波峰和波谷
参考文献:https://aotu.io/notes/2018/01…
费雪耶兹算法
快速随机,如果用 sort 做随机,第一:时间复杂度高,第二:并不算真正的随机
/*
@ Fisher–Yates(费雪耶兹算法)*/
export default function shuffle(a) {for (let i = a.length; i; i--) {let j = Math.floor(Math.random() * i);
[a[i - 1], a[j]] = [a[j], a[i - 1]];
}
return a;
}
正文完
发表至: javascript
2019-08-31