json和代码其实是一一对应的,二者完全等价,只是json可以动态加载,但无法用函数和编程等高级功能。
// csx写法
karas.render(
<canvas width={100} height={100}>
<div style={{color:'#F00'}}>Hello World</div>
</canvas>,
'#selector'
);
// json写法
karas.parse({
tagName: 'canvas',
props: {
width: 100,
height: 100,
},
children: [{
tagName: 'div',
props: {
style: {
color: '#F00',
},
},
children: ['Hello World'],
}],
}, '#selector');
json其实只包含4个基本key,对应CSX
写法的名称。
{
tagName: string, // 标签名
props?: Object, // props即属性,常见style和矢量属性在其内。
children?: Array<Object>, // 孩子节点
animate?: Object/Array<{ value: Object/Array, options: Object }>, // WAA执行动画的声明
}
例:
// csx写法
let root = karas.render(
<canvas width={100} height={100}>
<div style={{color:'#F00'}}>Hello World</div>
</canvas>,
'#selector'
);
root.animate([
{},
{
translateX: 100
},
], {
duration: 1000,
});
// json写法
karas.parse({
tagName: 'canvas',
props: {
width: 100,
height: 100,
},
children: [{
tagName: 'div',
props: {
style: {
color: '#F00',
},
},
children: ['Hello World'],
animate: [
{
value: [
{},
{
transateX: 100,
},
],
options: {
duration: 1000,
},
},
],
}],
}, '#selector');
json有压缩格式,即把常见的样式/动画的key简写别名,使得整体内容大小更短,但不易于阅读。缩写只针对了css样式的键名,以及动画的2个属性value
和options
,还有options
下的内容,动画中的关键帧同样是样式。因为是驼峰结构,所以普通情况下缩写就是驼峰单词的缩写形式。当出现冲突时,有专门覆盖定义:https://github.com/karasjs/karas/blob/master/src/parser/abbr.js 。所有缩写共享一个冲突定义,下面示例2种是等价的:
karas.parse({
tagName: 'div',
props: {
style: {
color: '#F00',
},
},
animate: [
{
value: [
{
translateX: 0,
},
{
translateX: 100,
},
],
options: {
duration: 1000,
iterations: 2,
},
},
],
});
// 缩写key
karas.parse({
tagName: 'div',
props: {
style: {
c: '#F00',
},
},
animate: [
{
v: [
{
tx: 0,
},
{
tx: 100,
},
],
o: {
d: 1000,
i: 2,
},
},
],
});
工具导出的json能看到library
字段,这是编辑器专用,为了复用,所有的元件(可理解为一个dom类)均在library中,且有唯一id
标识,json中使用libraryId
来引用并实例化元件。实例化的json有init
属性来覆盖props
属性,相当于创建对象并传入初始化参数。一般情况下,实例化无法覆盖children
和animate
,后面会有特殊方式。
let json = {
tagName: 'canvas',
children: [
{
libraryId: 0,
init: {
style: {
background: '#F00',
},
},
},
{
libraryId: 0,
init: {
style: {
background: '#00F',
},
},
},
],
library: {
id: 0,
tagName: 'div',
props: {
style: {
width: 100,
height: 100,
},
},
children: ['text'],
},
};
工具导出的json还能看到var-
开头的字段,会出现在props
和style
中,即插槽变量,在karas.parse()
时可根据id传入变量替换对应的属性。另外想要替换某个特殊字段如children
和animate
,会看到特殊的如var-children.0
的字段,数字标明索引。
let json = {
tagName: 'canvas',
children: [
{
libraryId: 0,
},
],
library: {
id: 0,
tagName: 'div',
props: {
style: {
background: '#F00',
'var-background': {
id: 'color',
desc: '自定义背景色',
},
},
},
children: [],
'var-children.0': {
id: 'custom',
desc: '自定义children',
},
},
};
karas.parse(json, {
vars: {
color: '#00F',
custom: 'text'
},
});
新的插槽定义变成了vars
标识,同时用子属性member
表达替换的成员属性,可为数组递归下去。特别的,json的直接子属性上定义的vars
可以替换library
中的内容,此时可以用id替代索引。
let json = {
tagName: 'canvas',
children: [
{
libraryId: 'lid',
},
],
library: [{
id: 'lid',
tagName: 'div',
props: {
style: {
background: '#F00',
vars: [{
id: 'color',
member: 'background', // 只有1个直接属性定义key即可
}],
},
},
children: [],
vars: [{
id: 'custom',
member: ['children', 0], // 需要替换children的第0个所以是个数组
}],
}, {
tagName: 'img',
props: {
src: 'xxx',
vars: {
id: 'url',
member: 'src', // 单个vars可以不用数组直接用对象
},
},
}],
vars: [{
id: 'lib',
member: ['library', 'lid',] // 特殊的可以用lid访问library的直接内容
}],
};
karas.parse(json, {
vars: {
color: '#00F',
custom: 'text',
url: 'xxx',
lib: {},
},
});
由于复用的需求,一个容器(比如div)包含有动画的节点(比如p),作为库元素library中的一员存在,被多次放入舞台中。希望它们里面的元素(img)以不同时间开始动画一段,可以在引用libraryId的同层声明areaStart和areaDuration。 这和AE的播放一段时间轴动画功能类似。
karas.render(
<svg>
{
karas.parse({
tagName: 'div',
children: [
{
libraryId: 1,
areaStart: 100, // 本节点和递归子节点开始时间都从100ms开始
areaDuration: 200, // 本节点和递归子节点播放到200ms就会认为结束一轮
init: {
style: {
left: 0,
top: 0,
}
}
},
{
libraryId: 1,
init: {
style: {
left: 20,
top: 20,
}
}
}
],
library: [
{
id: 0,
tagName: 'p',
props: {
style: {
position: 'absolute',
width: 20,
height: 20,
background: '#F00'
}
}
},
{
id: 1,
tagName: 'div',
props: {
style: {
position: 'absolute',
}
},
children: [
{
libraryId: 0,
init: {
style: {
left: 0,
top: 0,
}
},
animate: [
{
value: [
{},
{
translateX: 100,
}
],
options: {
duration: 1000,
fill: 'both',
}
}
]
}
]
}
]
})
}
</svg>
);
对于parse
的json内容,可以为包含根节点(canvas之类)也可以不包含由外部提供,render
可以包含parse
的json。json还可以包含fonts
信息,指明自定义字体名字和信息,但是没有加载逻辑,它要求字体必须是预先加载好且添加到页面中的(document.fonts.add)。
karas.render(
<canvas>
{
karas.parse(json)
}
</canvas>,
);
karas.parse({
tagName: 'canvas',
children: [],
fonts: [{
fontFamily: 'xxx',
"url": "xxx", // 加载的url
data: {
"emSquare": 2000,
"ascent": 1200,
"descent": 800,
"lineGap": 60
}
}]
});
特别的,对于loadAndParse
方法,json直接子属性新增fonts
和components
来通过url
定义远程加载的字体和自定义组件。字体的data
为字体信息。自定义组件的tagName
做了默认约定,需要自己执行同名注册,或暴露同名变量给全局访问自动注册。同时组件还有reload
申明强制加载,即便是发现已经注册过的同名组件。其中fonts
多支持了url
字段,可以加载并注册自定义字体。整体是个异步,只有全部成功后才会渲染。根节点也必须是canvas等,不能是div。
karas.loadAndParse(
{
"tagName": "canvas",
"props": {
"width": 100,
"height": 100
},
"children": [
{
"tagName": "Custom",
"props": {
"style": {
"fontFamily": "DINPro"
}
}
}
],
"fonts": [
{
"fontFamily": "DINPro",
"url": "xxx", // 加载的url
"data": {
"emSquare": 2000,
"ascent": 1200,
"descent": 800,
"lineGap": 60
}
}
],
"components": [
{
"tagName": "Custom",
"url": "xxx", // 加载的url
"reload": true // 强制加载,即便已经注册
}
]
},
'#selector',
{
callback(root) {
console.log(root);
}
}
);
另外,json直接子属性新增abbr
字段,当为false
时强制不使用缩写功能,等同于parse
的第2个参数传入abbr
为false
。建议始终使用,缩写已经不建议。没有的话会看到一个警告。
karas.parse({
tagName: 'div',
props: {},
children: [],
abbr: false, // 等同于下面的
}, {
abbr: false, // 等同于上面的
});
对于每个json,parse
的时候都进行复制一份解析,以避免对传入原始数据的修改,这会造成一些性能损失。如果确保只有1个实例且无需保护原有json数据,不进行复制操作,那么json中或者parse
的第3个options
参数可声明singleton
。
karas.parse({
tagName: 'div',
props: {},
children: [],
singleton: true, // 等同于下面的
}, {
singleton: true, // 等同于上面的
});