hcanvas v1.0.0
介绍
hcanvas是什么?
简单来讲hcanvas就是在canvas上实现了一套浏览器渲染引擎,可以将html css显示到canvas上,并且提供了dom操作、动画、注册事件等功能。
hcanvas的api及操作方式都是前端开发者最熟悉的html、css、事件、dom操作、动画等,对于前端开发者来说学习成本几乎为0。
hcanvas非常的轻量,没有任何npm依赖,所有逻辑都是在框架中实现。
应用场景
如果你的项目有丰富动画互动效果,为保证前端性能需要在canvas上实现时
如果想在canvas上展示复杂的布局,但苦于操作canvas复杂的api时
如果你时间紧急,挑选canvas框架发现其他框架学习成本较高时
如果你项目的体量没有达到需要动用游戏引擎那么厚重的框架时
hcanvas是个很好的选择。
安装
hcanvas使用umd规范打包,可以用多种方式引用。
npm方式
npm install hcanvas --save
import hcanvas from 'hcanvas';
let hc = new hcanvas({
//options
});
script引入
<script src="./dist/hcanvas.js"></script>
<script>
let hc = new hcanvas({
//options
});
</script>
其他规范
CommonJs、CMD、AMD都可以
使用
准备好一个canvas元素,css定义好尺寸
<canvas id="canvasDom"></canvas>
创建hcanvas实例
let classes = {
divBox: {
width: 500,
height: 500,
backgroundColor: '#0000ff',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
},
div: {
width: 100,
height: 100,
},
span: {
color: '#ffffff',
width: 100,
height: 100,
fontSize: 20,
lineHeight: 100,
textAlign: 'center'
},
};
let hc = new hcanvas({
canvasDom: document.getElementById('canvasDom'),
initElement: `
<root>
<div class="divBox">
<div id="divDom" class="div" style="background-color: #ff0000">
<span class="span">Aaa</span>
</div>
</div>
</root>
`,
classes: classes
});
就这么简单。
classes为一个class样式集合对象,子对象为一个类,属性就是css属性,如遇到带有-的属性,就转为驼峰的形式,如果嫌这样写着麻烦,可以自行实现loader转换。
样式也可以写在标签内,标签内样式不用转换-,按照css规则写即可。
initElement为初始化的dom元素,根元素用root标签。
initElement可以像例子中这样使用字符串,也可以使用hcanvas dom对象(此dom对象指的是hcanvas内部dom对象,非html的dom,下文有介绍)。
如嫌initElement写着麻烦并且没有语法高亮,可将html放到编辑器可识别的文件里,然后自行实现loader转换。
hcanvas没有实现所有html标签,虽然只实现了几个,但绝对够用。本例中用到了div和span,后面还有其他标签介绍。
效果如下:
布局
hcanvas采用flex布局,未实现其他布局方式,且display属性默认为flex,可以不指定。
可以不使用布局,不使用的方式就是在样式中指定x、y属性,有了x、y属性就会跳出布局。x、y相对于父元素左上角。
flex布局的所有内容均已实现,请放心使用。
示例
let classes2 = {
divBox: {
width: 500,
height: 500,
backgroundColor: '#0000ff',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column'
},
div1: {
backgroundColor: '#ff0000',
width: 100,
height: 100,
},
div2: {
backgroundColor: '#00ff00',
width: 100,
height: 100,
},
div3: {
backgroundImage: 'http://xxx.xxx.com/xxx.png',//图片地址
width: 100,
height: 100,
marginTop: 10
},
divNoLayout: {
width: 100,
height: 100,
x: 10,
y: 10,
backgroundColor: '#ffff00',
}
};
<root>
<div class="divBox">
<div class="div1"></div>
<div class="div2"></div>
<div class="div3"></div>
<div class="divNoLayout"></div>
</div>
</root>
效果如下,黄色div使用了x、y脱离了布局。div3使用了背景图片和margin
dom操作
api操作
hcanvas实现了一系列dom操作api,推荐使用api操作
let hc = new hcanvas({
canvasDom: document.getElementById('canvasDom'),
initElement: `
<root>
<div id="divBox" class="divBox" style="width:500">
<div class="div1"></div>
<div class="div2"></div>
<div id="div3" class="div3"></div>
</div>
</root>
`,
classes: classes
});
let divBoxDom = hc.getElementById('divBox');//获取元素
let div3Dom = hc.getElementById('div3');
divBoxDom.append(`<div class="div2"></div>`);//添加元素
div3Dom.addClass('div4');//添加类
div3Dom.removeClass('div4');//删除类
div3Dom.remove();//删除元素
是不是很眼熟?没错getElementById参考了浏览器api,append、remove、addClass、removeClass参考了jquery的dom操作api。
hcanvas dom操作
如果将上例的divBoxDom输出一下,你会看到他是这样的。
{
type: 'div',
id: 'divBox',
class: 'divBox',
style: {
width: 500
},
children:[
{
type: 'div',
class: 'div1',
//很多属性...
},
//很多属性...
],
//很多属性...
}
这就是之前提到的hcanvas dom对象,它就是一个js对象,你可以用你所知道的任何js方法操作它。你可以改样式,改类,改子节点,任何修改都会更新到canvas上。
如果你愿意的话,你可以写一个转换插件,将vue或react的虚拟dom转换为hcanvas dom,那么理论上hcanvas就可以渲染vue和react了。
元素及样式支持
<root></root><!--根元素,你可以把它当成html的body-->
<div></div><!--div,你懂的-->
<span>文本</span><!--用于显示文本标签,目前文本只能放到span里,与html不同,这里的span是块级-->
<img src="http://xxx.xxx.xxx/xxx.png"></img><!--图片,与html的img差不多,不同是这里的img可以拥有子元素,可以当容器使用-->
<plg pointArr="[[0,0],[100,0],[100,100],[0,100]]"></plg><!--多边形,pointArr为二维数组,代表多边形每个点的坐标,此坐标相对于plg自身基点origin-->
<fra duration="500" keyframe='[{"percent":0,"src":"图片地址"},{"percent":50,"src":"图片地址"},{"percent":100,"src":"图片地址"}]'></fra><!--帧动画,duration为帧动画执行时间,单位毫秒。keyframe为数组,里面的元素是关键帧,percent为关键帧百分比。帧动画需要配合js方法使用,下面有介绍。-->
样式支持:
flex(flex所有相关属性都支持,不一一列举了)、width、height、margin(left top right bottom)、padding(left top right bottom)、background-color、background-image(使用方法与css有区别,直接写图片地址)、opacity、color、x、y、white-space、font-size、font-family、font-weight、line-height、text-align、rotate(不同于css的rotate,这里不用放到transform,直接这样写rotate:45)、origin(同rotate,直接写origin:50% 50%)、scale(同rotate,直接写scale:0.5 0.5)
事件
hcanvas暂时采用dom0级事件,支持阻止冒泡,扩展了事件对象
let hc = new hcanvas({
canvasDom: document.getElementById('canvasDom'),
initElement: `
<root>
<div class="divBox">
<div id="div1" class="div1">
<div id="div2" class="div2"></div>
</div>
</div>
</root>
`,
classes: classes
});
let domDiv1 = hc.getElementById('div1');
domDiv1.onclick = function () {
console.log('click div1');
};
let domDiv2 = hc.getElementById('div2');
domDiv2.onclick = function (e) {
console.log('click div2');
console.log(e);
e.stopPropagation();
};
上例中e.stopPropagation();有阻止冒泡的作用,点击div2,只输出div2。如果去掉stopPropagation,则会冒泡,输出div2 div1
上例中的事件对象如下
{
target:{},//触发事件的dom
clientX: 10,//相对于canvas的坐标
clientY: 10,//相对于canvas的坐标
offsetX: 10,//相对于target的坐标
offsetY: 10,//相对于target的坐标
}
多边形的事件触发已经过精准的计算,请放心使用。
动画
hcanvas实现了两种动画,一种是动画api(位移、形变、旋转、缩放等改变样式的动画),一种是帧动画。
动画api
let hc = new hcanvas({
canvasDom: document.getElementById('canvasDom'),
initElement: `
<root>
<div class="divBox">
<div id="div" class="div">
<span class="span">Aaa</span>
</div>
</div>
</root>
`,
classes: classes
});
let domDiv = hc.getElementById('div');
domDiv.onclick = function () {
hc.animate(
domDiv, //触发元素
{//变化的样式
x: 150,
rotate: 120,
},
1000, //执行时间
[.3, 1.33, .49, 1.56],//贝塞尔曲线
0,//延迟时间
() => {//回调函数
console.log('end');
}
);
};
帧动画
let hc = new hcanvas({
canvasDom: document.getElementById('canvasDom'),
initElement: `
<root>
<div class="divBox">
<fra id="fra" duration="1000"
keyframe='[
{"percent":0,"src":"图片地址"},
...
{"percent":50,"src":"图片地址"},
...
{"percent":100,"src":"图片地址"}
]'>
</fra>
</div>
</root>
`,
classes: classes
});
let domFra = hc.getElementById('fra');
//帧动画执行时间在元素的duration属性里
domFra.onclick = function () {
domFra.play(
[.05, .89, .1, .95],//贝塞尔曲线
100, //延时时间
() => {//回调函数
console.log('end');
}
);
};
上面两个动画的例子放到一起,效果如下
移动端适配
适配移动端非常简单,只需在配置里加上rem:true即可
let classes = {
divBox: {
width: 750,
height: 750,
backgroundColor: '#0000ff',
},
div: {
width: 375,
height: 100,
backgroundColor: '#ff0000',
},
};
let hc = new hcanvas({
rem: true,
canvasDom: document.getElementById('canvasDom'),
initElement: `
<root>
<div class="divBox">
<div class="div">
</div>
</div>
</root>
`,
classes: classes
});
效果如下图,以设计稿为750位基准,红色div宽度375正好占据一半
性能优化
hcanvas做了如下性能优化
响应式渲染:hcanvas在内部监听dom元素变化,只有dom元素发生改变时才会触发渲染。
动画队列:当某个元素的动画需要执行多套时(比如多组变化执行时间不一样),或者同时有多个元素在动,请放心的执行多个动画api,hcanvas启用了动画队列机制,无论多少个动画,hcanvas都只启用一个定时器。
2 years ago