利用 Proxy 代理与 Reflect 反射实现 mv 模型视图,实现一个 打怪升级 的小游戏“勇士之战”

利用 Proxy 代理与 Reflect 反射实现 mv 模型视图,多层数据动态渲染页面,模仿 vue3 双向绑定中 viewModel 核心功能,实现一个 打怪升级 的小游戏“勇士之战”。

数据模型层面

多层数据对象,需要监听对象里面任一属性的改变

const _model = {
  warrior: {
    id: "warrior_1",
    name: "凯萨",
    originalHealth: 100,
    health: 100,
    attack: 12,
    speed: 0.67,
    live: true
  },
  enemyArr: [{
    id: "enemy_1",
    name: "迦泰",
    originalHealth: 120,
    health: 120,
    attack: 15,
    speed: 0.4,
    live: true
  }, {
    id: "enemy_2",
    name: "欧罗",
    originalHealth: 80,
    health: 80,
    attack: 10,
    speed: 1.1,
    live: true
  }, {
    id: "enemy_3",
    name: "莉丝",
    originalHealth: 70,
    health: 70,
    attack: 8,
    speed: 2,
    live: true
  }],
  gameover: false,
  artUrl: "https://lf6-creative.dailygn.com/obj/tmg-gameres-static/build-template-assets/4a661d55935185b515f251e55765233c/action.png",
  art: {
    idle: "https://lf6-creative.dailygn.com/obj/tmg-gameres-static/build-template-assets/4a661d55935185b515f251e55765233c/action.png",
    play: "https://lf6-creative.dailygn.com/obj/tmg-gameres-static/build-template-assets/d58b07f4650e18939159658dd050e09d/action.gif"
  }
};

视图显示层面

根据数据的变化动态创建、修改删除dom,减少页面重绘与重排,提高性能

const _view = (_model) => {
  console.log("_model", _model);
  /**
   * 勇士数值
   */
  // 我方勇士
  const warrior = _model.warrior;
  if (warrior.health > 0) {
    setDiv(findDiv(warrior.id, "warrior"), `${warrior.name},生命值:${warrior.health},攻击力:${warrior.attack},攻速:${warrior.speed}`);
  } else {
    if (warrior.live) {
      stopFun();
      alert(`很遗憾☹,我方勇士 ${warrior.name} 战斗失败,继续努力`);
      warrior.live = false;
      _model.gameover = true;
      delDiv(warrior.id);
    }
  }
  // 对手勇士
  const enemyArr = _model.enemyArr;
  enemyArr.forEach((v, k) => {
    if (v.health > 0) { setDiv(findDiv(v.id, "enemy"), `${v.name},生命值:${v.health},攻击力:${v.attack},攻速:${v.speed}`); } else {
      if (v.live) {
        stopFun();
        const addHealth = Math.round(v.originalHealth / 3 * 2);
        const addAttack = v.attack > warrior.attack ? Math.round(v.attack / 3) : 0;
        const addSpeed = v.speed > warrior.speed ? Math.round(v.speed / 3) : 0;
        alert(`我方勇士 ${warrior.name} 战胜对手勇士 ${v.name} ,获得奖励,生命值:+${addHealth}${addAttack ? ",攻击力:+" + addAttack : ""}${addSpeed ? ",攻速:+" + addSpeed : ""}`);
        _model.warrior.health += addHealth;
        if (addAttack) _model.warrior.attack += addAttack;
        if (addSpeed) _model.warrior.speed += addSpeed;
        v.live = false;
        delDiv(v.id);
        delLabel(v);
      }
    }
  });
  // 战斗结束
  if (!enemyArr.map(v => v.live).includes(true) && !_model.gameover) {
    stopFun();
    alert(`恭喜☺,我方勇士 ${warrior.name} 在“勇士之战”中大获全胜`)
    _model.gameover = true;
  }
  /**
   * 勇士战场
   */
  // 勇士名称
  setDiv("self", warrior.name);
  // 选择列表
  selectList(_model.enemyArr);
  // 战斗场面
  actionArt(_model.artUrl);
};

数据模型与视图显示动态绑定(可通用的核心代码)

数据对象中的任一数据改变,立即触发视图显示方法,实现动态渲染页面

const mv = (obj, callback, pointer) => {
  let single = true;
  callback.call(pointer, obj);
  const proxy = (_obj, callback, pointer) =>
    new Proxy(_obj, {
      set(target, property, value, receiver) {
        if (target[property] !== value) setTimeout(() => {
          if (!single) callback.call(pointer, obj);
        }, 0);
        Reflect.set(target, property, value, receiver);
        return true;
      },
    });
  const recursion = (_obj) => {
    for (const key in _obj) {
      ["[object Object]", "[object Array]"].includes(Object.prototype.toString.call(_obj[key])) &&
        (_obj[key] = proxy(_obj[key], callback, pointer)) && recursion(_obj[key]);
    }
  }
  recursion(obj)
  setTimeout(() => {
    single = false;
  }, 0);
  return proxy(obj, callback, pointer);
};

游戏界面,在线体验地址,代码仓库地址

游戏界面

勇士之战

在线体验地址

点击前往在线体验地址

代码仓库地址

关于游戏玩法及更多详细的介绍,完整的示例代码,请前往游戏开源仓库查看

点击前往游戏开源代码仓库地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值