索引
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
和setter
func@aaBbCc
: 目标身上的函数调用,优先判断target.aaBbCc()
是否存在,如果存在则使用,否则使用target.getAaBbCc/target.setAaBbCc
funcFlatten@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])