索引
HT的TargetedAnimation动画系统需要驱动目标Target进行变化,变化可能是某个对象属性,可能是某个Style属性,可能是某个Attribute属性,也可能是某个接口,亦或者更深的内部属性。
我们希望建立一个链接,将具体的变化跟规则名联系起来,这个规则名可以我们称之为Binding Rule。通过这种联系,我们就可以解耦上层动画系统状态系统跟下层目标具体的纷繁不一的内部细节,统一通过Binding Rule来驱动目标的变化。
目前在HT中,该绑定实际上是作用在两个系统里面:
TargetedAnimation动画系统State状态系统即:对于期望对于上述两个系统,希望通过统一一个上层语言Binding Rule来驱动目标的变化,而并不关心内部实现细节,不同细节将会在这一层进行抽象统一。
每个rule本身是一个对象,包含getter和setter两个属性,分别表示获取和设置的函数。形如:
{
getter : function(target) { return target[key]; },
setter : function(target, value) { target[key] = value; }
}
rule 也可以是一个形如func: function(name) { ..... return { getter : getterFunc, setter : setterFunc } }的函数,一般用于动态生成getter和setter,譬如一些可以前置的判断处理统一提前计算,根据name可能会有一些动态变化用func来处理。
{
func : function(name) {
if (name === 'hostX') {
return {
getter : function(target) { return target.getHost().getX(); },
setter : function(target, value) { target.getHost().setX(value); }
};
}
else if (name === 'hostY') {
return {
getter : function(target) { return target.getHost().getY(); },
setter : function(target, value) { target.getHost().setY(value); }
};
}
}
}
注册 rule 的方式分为两种,一种是具体rule,一种是rule pattern,我们会优先根据用户绑定属性,查找是否有完全命中的rule,如果存在则直接使用,如果不存在则按照rule pattern查找
rule的注册方式为:
ht.Binding.setBindingRule(name, rule): 用name注册一组rule规则,特殊的如果setBindingRule(name, undefined),则认为是删除ht.Binding.getBindingRule(name): 通过name获取rule规则ht.Binding.removeBindingRule(name): 通过name移除rule规则ht.Binding.getBindingRuleMap(): 获取所有注册的rule规则例如:
ht.Binding.setBindingRule('attr.x', {
getter : function(target) { return target.a('x') },
setter : function(target, value) { target.a('x', value); }
});
rule pattern的注册方式为:
ht.Binding.setBindingRulePattern(pattern, rule): 用pattern注册一组rule规则,特殊的如果setBindingRulePattern(pattern, undefined),则认为是删除ht.Binding.getBindingRulePattern(pattern): 通过pattern获取rule规则ht.Binding.removeBindingRulePattern(pattern): 通过pattern移除rule规则ht.Binding.getBindingRulePatternMap(): 获取所有注册的rule pattern规则例如:
ht.Binding.setBindingRulePattern(/^attr@/, {
func : function(name) {
var key = name.slice(5);
return {
getter : function(target) { return target.a(key); },
setter : function(target, value) { target.a(key, value); }
};
}
});
更推荐使用rule pattern,因为rule pattern可以避免大量的重复代码。
最终在计算某个name对应的rule时,规则如下:
name自身已经包含getter和setter或者包含func函数,是的话认为它已经是一个rule直接返回Binding.getBindingRule(name)来全匹配,如果存在则返回Binding.getBindingRulePattern(name)来Regex匹配,如果存在则返回func@name规则进行第一波兜底,此时会先判断是否存在target.name方法,如果存在则返回。不存在则使用filed@name规则来进行兜底// 为 gv 注册一个 state 属性,属性名为 side,对应的 Binding Rule 为 side 变化则变更下面 Node 的必要属性 gv.addStateBinding('side', { getter: function(target) { return target._side; }, setter: function(target, value) { value = ht.Math.clamp(value, startSide, endSide); target._side = value; polygonNode.s('shape.polygon.side', Math.round(value)); textNode.s('text', Math.round(value)); slideNode.setScaleX(value * ...); slidePinNode.setX(...); } }); // 后续应用层就可以简单的 setState('side', v) 来工作了 gv.setState('side', 5);
目前我们HT内置的rule pattern有:
style@xxx: 对应Node的Style属性,即getter为node.s('xxx'),setter为node.s('xxx', value)attr@xxx: 对应Node的Attribute属性,即getter为node.a('xxx'),setter为node.a('xxx', value)field@xxx: target的Field属性,即getter为target['xxx'],setter为target['xxx'] = value.xxx: 同Field,即target['xxx']state@aa.bb.cc: target的State属性,具体在State手册中展开介绍material@matname.matproperty: node的Material材质属性,譬如material@head.diffuse 表示node的材质列表中head材质的diffuse属性,考虑到材质名可能存在点号,所以使用material@规则material@matname#property,查找方式为 lastIndexOf('#')material@matname.property,查找方式为 lastIndexOf('.')pattern的形态,即自行定义具体的getter和setterfunc@aaBbCc: 目标身上的函数调用,优先判断target.aaBbCc()是否存在,如果存在则使用,否则使用target.getAaBbCc/target.setAaBbCcfuncFlatten@aaBbCc: 跟func@规则相当,不同在于,它会把 getter 出来的 { x , y, z? } { width, height, } 转换成平的 array [ ],并且 setter是通过 apply 来调用,即它会打平返回值成array便于动画系统使用具体示例:
style@shape3d.color
-> getter : node.s('shape3d.color')
-> setter : node.s('shape3d.color', value)
attr@count
-> getter : node.a('count')
-> setter : node.a('count', value)
field@width
-> getter : target.width
-> setter : target.width = value
.width
-> getter : target.width
-> setter : target.width = value
material@head.diffuse
-> getter : node.getMaterial('head', 'diffuse')
-> setter : node.setMaterial('head', 'diffuse', value)
func@tx
-> getter : target.tx()
-> setter : target.tx(value)
func@width
-> getter : target.getWidth()
-> setter : target.setWidth(value)
funcFlatten@size
-> getter : return [ target.getSize().width, target.getSize().height ]
-> setter : target.setSize(value[0], value[1])