1.0.0 • Published 2 years ago

hcanvas v1.0.0

Weekly downloads
-
License
ISC
Repository
-
Last release
2 years ago

介绍

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都只启用一个定时器。