【问题标题】:Dynamic Healthbars - Dynamic Observables and Dependencies动态 Healthbars - 动态 Observables 和依赖项
【发布时间】:2017-05-11 07:38:40
【问题描述】:

这个问题会有点具体,所以请随意更广泛地回答,或者指出正确的方向。

几天前我选择了 Knockout.js,因为它解决了我之前在我的应用程序中遇到的一个问题,不过话虽这么说,但对一个库如此陌生往往会带来更多问题......

我正在尝试为 GM(游戏大师)应用程序制作一个简单的 PC(可玩角色)管理器。我从一个 json 对象数组(后来从一个 db)加载所有 PC 信息,并将它们放在 Bootstrap Tab Panel 中以便于维护。

我想创建的第一件事是为每个玩家创建一个健康栏,并在侧面有一个用于增加和减少生命值的控件,以及一个 X/Y 简单文本视图。这些组件中的每一个都应该独立于另一个玩家的组件,并且都应该根据输入框中的值进行动态更新。

我遇到的问题是考虑淘汰赛而不是简单的 javascript。我稍后会解释。

这是我的代码:

~ 带有数据的 JSON 数组:

    var initialData = [
    {
        id: 0,
        pcName: "Player 1",
        hp: 12,
        curHp: 12,
        name: "Dudebro One",
        playerClass: "Ranger",
        level: 1,
        background: "",
        race: "Elf - Wood",
        Alignment: "",
        exp: 700,
        inspiration: 0,
        proficiencyBonus: 0
    },
    {
        id: 1,
        pcName: "Player 2",
        hp: 10,
        curHp: 10,
        name: "Brodude Two",
        playerClass: "Fighter",
        level: 1,
        background: "Soldier",
        race: "Gnome",
        Alignment: "",
        exp: 700,
        inspiration: 0,
        proficiencyBonus: 0
    }
];

(注意:出于可读性目的,数据不完整。还假设n的玩家人数,而不是2)

~ Knockout 代码(远未完成):

var PCModel = function (pcs) {
    var self = this;

    var currentHp = ko.observable(10);
    var maximumHp = ko.observable(10);

    self.pcsList = ko.observableArray(ko.utils.arrayMap(pcs, function (pc) {
        return {
            id: pc.id, pcName: pc.pcName, hp: pc.hp, curHp: pc.curHp, name: pc.name, playerClass: pc.playerClass, level: pc.level, background: pc.background, race: pc.race, Alignment: pc.Alignment, exp: pc.exp,
            inspiration: pc.inspiration, proficiencyBonus: pc.proficiencyBonus
        };
    }));

    //TODO: REMOVE (Note: Here just for testing).
    self.myFunction= function(pc){
        currentHp--;
    };

    self.getHpPercentage = function (pc) {
        var hpRound = Math.round((pc.curHp / pc.hp) * 100);
        return hpRound + "%"
    }

    self.hpClass = function (pc) {
        var hp = currentHp() / maximumHp() * 100;
        if (hp >= 70) {
            return 'progress-bar-success';
        } else if (hp < 70 && hp >= 30) {
            return 'progress-bar-warning';
        } else if (hp < 30) {
            return 'progress-bar-danger';
        }
    };
};
ko.applyBindings(new PCModel(initialData));

现在让我解释一下自己。我知道要动态更新事物,我需要定义可观察对象或手动订阅事物。我决定走可观察的路线。我的问题是var maximumHp = ko.observable(10);,例如,在我的情况下没有意义,因为我在 json 数组中拥有我需要的所有数据。此外,maximumHp 对于正在查看/处理的 CURRENT 对象必须如此。我不知道如何定义...

~我的带有绑定的 html 代码(同样,远未完成):

<div style="float:right; margin-top:25px; width: 65vw;">
<div class="container">
    <ul class="nav nav-tabs" id="sortable" data-bind="foreach: pcsList">

        <li data-bind="css: {active: $index() == 0 }" >
            <a data-bind="attr: {href: '#tab' + id}, text: pcName" data-toggle="tab"></a>
        </li>
    </ul>
    <div class="container-border-cup">
        <div class="tab-content " data-bind="foreach: pcsList">

            <div class="tab-pane tabbed-content-style" data-bind="attr: {id: 'tab' + id}, css: {active: $index() == 0 }">
                <h3 data-bind="text: name"></h3>

                <div style="float:left; font-size:15pt; font-weight:500; line-height:35px; padding-right:20px; min-width:100px;">
                    <span data-bind="text: curHp"></span>/<span data-bind="text: hp"></span>
                </div>

                <button data-bind="click: $root.myFunction">
                    Click me
                </button>

                <div class="progress" style="width: 50%; height: 35px; float:left">
                    <div class="progress-bar" style="float:left;" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" data-bind="text: $root.getHpPercentage($data), attr:{class: 'progress-bar ' + $root.hpClass($data)}, style:{width: $root.getHpPercentage($data)}"></div>
                </div>


                <div class="input-group" style="padding-left:20px">
                    <span class="input-group-btn">
                        <input type="button" class="btn btn-danger" id="btnToggleHP" value="-" style="width: 35px; font-weight:900;" />
                    </span>
                    <input id="inModHP" style="width:70px" type="text" class="form-control" value="0">
                    <span class="input-group-btn" style="float:left">
                        <input type="button" class="btn btn-default" id="btnGoHP" value="Go" />
                    </span>
                </div>
            </div>
        </div>
    </div>
</div>
</div>

前面的所有代码都会产生以下效果:

(别担心 ulgy 按钮,那只是用于测试的按钮)。理想情况下,最终,用户将能够单击红色减号按钮,并在该按钮和绿色加号之间切换,以识别从健康池中添加或减去。点击 Go 按钮应该会级联更新所有内容。

虽然我可能会拼凑出一些最终可行的东西,但我想在此过程中学习一些好的做法。

感谢您阅读我的问题小说,如果您愿意放弃答案,则更加感谢!

【问题讨论】:

    标签: javascript json knockout.js


    【解决方案1】:

    如果您实施的行为与每个玩家相关,您应该将逻辑放在PlayerViewModel 中,并为每个玩家设置一个模型。您可以在另一个视图模型中跟踪模型的集合。我们就叫它App吧。

    App,我们只做一件事

    1. 获取 json 数组并为其中的每个对象创建一个 new PlayerViewModel

    当我们创建new Player 时,我们会做几件事:

    1. 首先,我们将所有静态 json 属性复制到我们的新播放器实例。您可以手动执行此操作,就像您在代码中所做的那样,也可以自动执行此操作,就像我对 Object.assign 所做的那样。

    2. 然后,我们覆盖一些属性以使用ko.observables。 您希望能够在 UI 中更改的每个属性都必须是可观察的。因此,如果您希望能够更改名称,则还必须将其添加为 observable。

    3. 现在我们也可以添加我们自己的可观察值和计算值。例如:如果玩家可以更改curHp,我们可以自动计算一个HP百分比。

    4. 最后,每个Player 通过Player.prototype 获得一组函数。例如:有一个函数将input 字段中的值添加到curHp

    现在,由于所有信息和逻辑都在 Player 模型中,您可以轻松地进行数据绑定,而无需将引用传递给玩家,也无需使用 $parent$root

    var Player = function(playerData) {
      // Make a copy of all properties to the new player
      // These are static properties, suitably for one-way
      // data-binding.
      Object.assign(this, playerData);
      
      // Create observables for two-way properties
      // (properties you want to change via the UI)
      this.curHp = ko.observable(playerData.curHp);
      this.subtractValue = ko.observable(0);
      
      // Create computed values for the UI
      this.hpPercentage = ko.pureComputed(function() {
        return (this.curHp() / playerData.hp * 100).toFixed(1);
      }, this);
      
      this.healthWarning = ko.pureComputed(function() {
        var hpPerc = this.hpPercentage();
        if (hpPerc === 0) return "Dead";
        if (hpPerc < 30) return "Watch out";
        if (hpPerc < 70) return "Steady";
        return "OK";
      }, this);
    };
    
    // Add functions
    Player.prototype.addHp = function() {
      var val = parseFloat(this.subtractValue());
      var newHP = Math.min(
        Math.max(this.curHp() + val, 0), this.hp);
      
      this.curHp(newHP);
    };
    
    // A helper constructor
    Player.create = function(playerJson) {
      return new Player(playerJson);
    };
    
    var App = function(jsonData) {
      this.players = jsonData.map(Player.create);
    };
    
    
    var initialData=[{id:0,pcName:"Player 1",hp:12,curHp:4,name:"Dudebro One",playerClass:"Ranger",level:1,background:"",race:"Elf - Wood",Alignment:"",exp:700,inspiration:0,proficiencyBonus:0},{id:1,pcName:"Player 2",hp:10,curHp:7,name:"Brodude Two",playerClass:"Fighter",level:1,background:"Soldier",race:"Gnome",Alignment:"",exp:700,inspiration:0,proficiencyBonus:0}];
    
    ko.applyBindings(new App(initialData));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    
    <h2>Players</h2>
    <ul data-bind="foreach: players">
      <li>
        <h3 data-bind="text: name"></h3>
        <h4 data-bind="text: healthWarning"></h4>
        <input disabled type="range" min="0" data-bind="value: curHp, attr: { max: hp }">
        <input type="number" min="-10" max="10" data-bind="value: subtractValue">
        <button data-bind="click: addHp">update HP</button>
        <span data-bind="text: hpPercentage() + '%'"></span>
      </li>
    </ul>

    【讨论】:

    • 我会给你接受的答案,因为这确实符合我的要求,但我找到了不同的解决方案。我最终决定做的是,当手动复制 json 属性时,我基于pc.curHppc.hp 制作了 maxhp 和 curhp 可观察对象,如下所示:obCurHp: ko.observable(pc.curHp)。然后,我只是使用所述可观察值创建了计算百分比和进度条宽度(引导程序)的函数。这样,如果 observables 发生变化,依赖关系也会发生变化,因此一切都会级联更新。不过感谢您的建议!
    • 我觉得这基本上就是我试图解释的内容。例如,在我的代码示例中,我写道:this.curHp = ko.observable(playerData.curHp);。无论如何,很高兴你想通了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-23
    • 1970-01-01
    • 2020-12-13
    • 1970-01-01
    • 1970-01-01
    • 2014-10-06
    • 2020-02-12
    相关资源
    最近更新 更多