自定义控件
为了进一步提供控件自由度,AHMI IDE提供了自定义控件的功能。
打开帮助,点击自定控件,打开自定义控件配置面板。
点击新建,出现空白的自定义控件选项。分别输入自定义控件配置的名称、图标-选中、图标未选中。
在代码编辑器中编写自定义控件对象,采用JavaScript语言。
自定义控件对象编写:
4.1 基本结构:
var widget = {
name:'GIF',
type:'MyGIF',
group:'显示类',
info:{
width:100,
height:100,
left:0,
top:0
},
tagType:'array',
actionTriggers:'',
texList:function(widget){
},
texConfig:function(widgetInfo){
},
triggers:{
},
draw:function(widget, options){
},
paint:function(widget, ctx, options){
},
render:function (widget,srcRootDir,dstDir,imgUrlPrefix,resolve, reject){
},
transWidget:function(targetWidget, wIdx){
}
}属性 含义 附加 name 控件显示名称 type 控件类型 group 控件组类型 显示类、触控类、其他类 info 控件属性 可配置的属性列表 tagType 变量类型 number、string、array actionTriggers 动作触发条件 Press、LongPress、
Release、EnterLowAlarm、
LeaveLowAlarm、EnterHighAlarm、
LeaveHighAlarm、TagChange、
SwipeLeft、SwipeRight、
SwipeTop、SwipeBottom等,用|分割texList 纹理列表 控件默认纹理 draw 数据更新 根据变量或者其他条件更新数据 paint 绘制逻辑 根据数据绘制控件样式 render 预处理逻辑 生成时预处理图片等资源 transWidget 控件转换逻辑 编辑到仿真时可以修改一些控件参数 4.2 控件属性
info:{
width:400,
height:400,
left:0,
top:0,
offset:{
default:0,
style:'none'
},
arrange:{
type:'selector',
title:{en:'Direction', zh: '方向'},
options:[{name:{en:'Vertical', zh: '垂直'},value:0},{name:{en:'Horizontal', zh: '水平'},value:1}],
default:0,
style:'short'
},
showNum:{
type:'number',
title:{en:'Show Num',zh:'显示个数'}[window.lang],
default:1,
style:'short',
limitFunc:function(newValue){
if(newValue < 1){
return {en:'Show num cannot be less than 1',zh:'显示个数至少是1'}[window.lang]
}
}
}
},控件的可配置属性可以是一个对象或者是单纯的数值或者字符串。如果是对象,可以精细地控制该属性。所有控件都应该有width、height、left、top属性。 当属性是对象时,其参数含义如下: | 属性 | 含义 | 附加 | | :----:| :----: | :----: | | type | 属性类型 | number、string、selector,selector需要配合options| | title | 属性显示名称 | 字符串/对象,对象时支持多语言 | | options | 选项 | 包含{name, value}的数组 | | default | 属性默认值 | | | style | 属性显示样式 | short占1/2行,long占一行,none不显示 | | limitFunc | 约束条件 | 判断当前值是否合法,不合法给出错误提示 |
4.3 纹理列表
```
texList:function(widget){
// return [{},{}]
var bgTex = {
name:'GIF纹理',
currentSliceIdx:0,
slices:[
{
name:'GIF图片',
imgSrc:'',
color:'rgba(255,255,255,1)'
}
]
}
return [bgTex]
},
```
每个纹理包含名称和slices属性,slices中包含了纹理切片。每个纹理切片都有name、imgSrc、color属性。
4.4 纹理配置
```
texConfig:function(widgetInfo){
switch(widgetInfo.tex.name){
case '元素':
return {
canAddNewSlice:true,
disableEditName:false
}
default:
return {
canAddNewSlice:false,
disableEditName:true
}
}
},
```
有时要根据控件信息来调整纹理配置,目前可以配置canAddNewSlice和disableEditName。其中canAddNewSlice决定是否可以添加新的纹理切片,disableEditName决定是否可以修改纹理名称。
4.5 triggers
```
triggers:{
changeTex:function(layer, widget,arg){
},
changeGeneralAttrs:function(layer, widget,arg){
function _getRandomColor(){
var r = _.random(64, 255);
var g = _.random(64, 255);
var b = _.random(64, 255);
return 'rgba(' + r + ',' + g + ',' + b + ',1.0)';
}
for(var key in arg.attrs){
var newValue = arg.attrs[key]
if(key == 'count'){
widget.info.nums = widget.info.nums.slice(0,newValue)
for(let i=0;i<newValue;i++){
if(widget.info.nums[i] === undefined){
widget.info.nums[i] = 1
}
}
widget.texList[1].slices = widget.texList[1].slices.slice(0,newValue)
for(let i=0;i<newValue;i++){
if(widget.texList[1].slices[i] === undefined){
widget.texList[1].slices[i] = {
name:{en:'Image',zh:'图片'}[window.lang],
imgSrc:'',
color:_getRandomColor()
}
}
}
}
}
},
changeWidgetSize:function(layer, widget,arg) {
var widgetWidth=arg.widgetWidth;
var widgetHeight=arg.WidgetHeight;
layer.set({scaleX:1,scaleY:1,width:widgetWidth,height:widgetHeight});
}
},
```
目前开放changeWidgetSize、changeTex、changeGeneralAttrs三个修改控件的回调函数。changeWidgetSize在修改控件尺寸时触发,用默认代码即可。changeTex在修改控件纹理时触发。changeGeneralAttrs则是修改info中控件属性时触发。
4.6 draw
```
draw:function(widget, options){
//tag to values
if(options && options.isEditing){
widget.curValue = widget.info.nums||[]
}else{
var tag = this.findTagByName(widget.tag)
if(tag){
var tagType = tag.elemType
var curValue = this.getValueByTagName(widget.tag, []);
widget.curValue = curValue
}else{
widget.curValue = []
}
}
var total = 0
for(var i=0;i<widget.curValue.length;i++){
total+= Math.max(0, widget.curValue[i])
}
widget.curValueArcs = []
if(total){
widget.curValue.forEach(function(v,i){
widget.curValueArcs[i] = v/total * Math.PI * 2
})
}
},
```
根据控件的变量或者其他条件,来更新数据。options有一个属性isEditing,表示当前是编辑状态还是仿真状态。一般编辑状态展示一些默认的数据。
4.7 paint
```
paint:function(widget, ctx, options){
var bgTex = {
image:this.getResourceByUrl(widget.texList[0].slices[0].imgSrc),
color:widget.texList[0].slices[0].color
}
if(bgTex.color){
ctx.fillStyle = bgTex.color
ctx.fillRect(0, 0 ,widget.info.width, widget.info.height)
}
if(bgTex.image){
ctx.drawImage(bgTex.image,0,0 ,widget.info.width,widget.info.height)
}
//paint cylinders
var radius = Math.min(widget.info.width, widget.info.height)/2
var startArc = ((widget.info.baseOffset||0)/180*Math.PI)
widget.curValueArcs = widget.curValueArcs || []
var minLen = Math.min(widget.info.width, widget.info.height)
widget.curValueArcs.forEach(function(arc, i){
ctx.save()
ctx.scale(widget.info.width/minLen, widget.info.height/minLen)
ctx.beginPath()
ctx.moveTo(minLen/2, minLen/2)
ctx.arc(minLen/2, minLen/2, radius, startArc, startArc + arc)
ctx.closePath()
ctx.clip()
var curColor = widget.texList[1].slices[i].color
var curImgSrc = widget.texList[1].slices[i].imgSrc
if(curColor){
ctx.fillStyle = curColor
ctx.fillRect(0, 0 , minLen, minLen)
}
var curImg = this.getResourceByUrl(curImgSrc)
if(curImg){
ctx.drawImage(curImg,0,0 ,minLen, minLen)
}
ctx.restore()
startArc += arc
}.bind(this))
},
```
从当前控件数据,采用Canvas来绘制控件。坐标原点为控件左上角。注意
var bgTex = {
image:this.getResourceByUrl(widget.texList[0].slices[0].imgSrc),
color:widget.texList[0].slices[0].color
}
其中getResourceByUrl为内置方法,从指定URL获取图片。
4.8 render
```
render:function (widget,srcRootDir,dstDir,imgUrlPrefix,resolve, reject){
var info = widget.info;
if (!!info){
//font
//trans each slide
var width = info.width;
var height = info.height;
var slices = []
widget.texList.forEach(function(tex){
slices = slices.concat(tex.slices)
})
var totalSlices = slices.length;
slices.forEach(function (curSlice,i) {
var canvas = new Canvas(width,height);
var ctx = canvas.getContext('2d');
ctx.clearRect(0,0,width,height);
ctx.save();
//render color
ctx.fillStyle = curSlice.color
ctx.fillRect(0,0, width, height)
//render image;
var imgSrc = curSlice.imgSrc;
if (imgSrc !== '' && typeof(imgSrc) !== 'undefined'){
var targetImageObj = this.getResourceByUrl(imgSrc)
ctx.drawImage(targetImageObj,0,0,width, height)
}
var imgName = widget.id.split('.').join('-');
var outputFilename = 'r-'+imgName +'-'+ i+'.png';
var outpath = dstDir +'/'+outputFilename;
canvas.output(outpath,function (err) {
if (err){
reject(err);
}else{
this.trackedRes.push(new ResTrack(imgSrc,curSlice.color,null,outputFilename,width,height,curSlice))
// console.log(_.cloneDeep(this.trackedRes))
//write widget
curSlice.originSrc = curSlice.imgSrc;
curSlice.imgSrc = (imgUrlPrefix||'')+outputFilename;
curSlice.onlyColor = onlyColor(imgSrc, curSlice.color, curSlice.text)
//if last trigger cb
totalSlices -= 1;
if (totalSlices<=0){
resolve()
}
}
}.bind(this));
ctx.restore();
}.bind(this));
}else{
resolve()
}
},
```
为了优化性能,生成时可能会预处理图片纹理,使得其符合实际显示大小。
```
this.trackedRes.push(new ResTrack(imgSrc,curSlice.color,null,outputFilename,width,height,curSlice))
```
这里根据图片的一些参数,来决定这张图是否被复用,以减少存储体积。
4.9 transWidget
```
transWidget:function(targetWidget, wIdx){
targetWidget.subType = Type.MyGIF
return targetWidget
},
```
从编辑状态到仿真状态可能有一些转换操作,可以放在transWidget中进行,例如控件的映射等。
- 使用自定义控件 保存好自定义控件配置后,刷新页面,会显示自定义控件选项对话框。选择需要加载的自定义控件就可以生效。对于不想生效的自定义控件,也可以在配置中禁用。