本项目致力于将绘图模式的 canvas 上下文 CanvasRenderingContext2D, 整理成面向对象的使用模式, 不再需要获取CanvasRenderingContext2D, 不再需要moveTo、lineTo, 不再需要beginPath、closePath, 不再需要面向过程编程。
当你需要开始一个canvas时, 你需要在页面中提供一个容器, 并提供指定的宽高, 如:
<div id="container" style="width: 500px; height: 300px;"></div>
运行命令npm run build
编译项目, 编译后的文件是/dist
文件夹下的Topoboard.js文件, 如果需要压缩后的文件, 请修改webpack.config.js
中的mode为production
, 重新编译。
if(window) {
window['TB'] = window['Topoboard'] = Topoboard;
}
if(typeof define == 'function' && define.amd) {
define('Topoboard', () => Topoboard);
}
let board = new Topoboard(document.getElementById('container'), ['click']);
创建图层, 后创建的图层覆盖在上面。
let bkLayer = board.newLayer('bk-layer');
let plLayer = board.newLayer('pl-layer');
let recLayer = board.newLayer('rec-layer');
let labelLayer = board.newLayer('label-layer');
作为丰富图形对象信息的一种重要参数组件, 数据模型是必要的。
let e = new TB.model.Expand(150, 100, 80, 80);
// Expand { x: 150, y: 100, w: 80, h: 80 };
// x,y 表示图形的基准坐标。w,h 表示图形的扩展宽高。可用于图片、矩形等图形对象的参数使用
let font = new TB.model.Font(18, '微软雅黑');
let f = font.getFont();
// '18px 微软雅黑'
let radial = new TB.model.Radial(0, 0, 5);
// Radial {x: 0, y: 0, r: 5}
let shadow = new TB.model.Shadow(0, 0, '#fff', 5);
// Shadow {x: 0, y: 0, color: '#fff', blur: 5}
let v = new TB.model.Vector(100, 100);
// Vector {x: 100, y: 100}
要绘制一个图形, 只需要声明一个对象, 并决定要fill还是stroke(Img除外)。相同图层下, 后绘制的图形覆盖在其他图形上面。
circle = new Topoboard.graphs.Circle({
layer: cirLayer,
radial: new TB.model.Vector(100, 100, 20),
width: 2,
style: 'red',
closePath: true,
shadow: new TB.model.Shadow(0, 0, '#fff', 5)
}).stroke();
new Topoboard.graphs.Img({
layer: bkLayer,
image: '1.png'
}).draw();
new Topoboard.graphs.PolyLine({
layer: plLayer,
path: [
new TB.model.Vector(30, 30),
new TB.model.Vector(30, 200),
new TB.model.Vector(450, 200)
],
width: 2,
style: 'lightblue',
closePath: false
}).stroke();
rect = new Topoboard.graphs.Rect({
layer: recLayer,
expand: new TB.model.Expand(150, 100, 80, 80),
lineWidth: 6,
style: '#fcc',
shadow: new TB.model.Shadow(0, 0, '#fff', 2)
}).fill();
let label = new Topoboard.graphs.Text({
layer: labelLayer,
position: new TB.model.Vector(prev.coor[0] - 5, prev.coor[1] - 20),
content: prev.label,
font: new TB.model.Font(18, '微软雅黑'),
style: 'chocolate'
}).fill();
事件模型仍旧基于浏览器的原有事件机制, 如click、mousemove、mouseleave等。
let board = new Topoboard(document.getElementById('container'), ['click', 'mousemove', 'mouseleave']);
rect.addEventListener('click', function(event) {
console.log(this, event.type);
// this指向rect对象实例, event是事件对象
return true; // 当几个graph,或者几个图层叠加时, 事件从上到下依次触发, 返回 true表示阻止事件传递
});
仅支持部分鼠标事件, 只有部分鼠标事件具有正确的行为。
img.json
文件, 用于保存所有依赖的图片路径和图片名称。{
"images": [
{"name": "bg", "url": "./img/bg.jpg"},
{"name": "box-bg", "url": "./img/box-bg.png"},
{"name": "cat1", "url": "./img/cat1.jpg"},
{"name": "cat2", "url": "./img/cat2.jpg"},
{"name": "cat3", "url": "./img/cat3.jpg"},
{"name": "cat4", "url": "./img/cat4.jpg"}
]
}
let imgManager = new Topoboard.ImgManager({imgJsonUrl: 'img.json'});
imgManager.load();
// callbacks 是事先自己定义好的函数
imgManager.onreadystatechange = function() {
//img.json 数据加载完成
if(this.readyState == Topoboard.ImgManager.STATE_RESOURCE_INFO_READY) {
callbacks.imgInfoReady(this.data.images.length);
}
//图片加载中, 每一个图片加载完成触发一次这种状态
else if(this.readyState == Topoboard.ImgManager.STATE_RESOURCE_LOADING) {
callbacks.imgLoading(this.count, this.data.images.length);
}
// 图片已全部加载完成
else if(this.readyState == Topoboard.ImgManager.STATE_RESOURCE_READY) {
callbacks.imgLoaded(this.imgs);
}
};
制作一个加载中动画。
let loading;
let loadingLayer = board.newLayer('loading-layer');
// 更新加载中信息内容
let callbacks = {
imgLoaded: function(imgs) {
// 结束加载中图层
loadingAni.stop(), loadingLayer.remove();
},
imgLoading: function(count, total) {
loading.count = count;
loading.text = 'loading: ' + count + '/' + total;
loading.content = loading.text + loading.dots;
loading.refresh();
loadingLayer.refresh();
board.refresh();
},
imgInfoReady: function(total) {
// 绘制加载中信息
loading = new Topoboard.graphs.Text({
layer: loadingLayer,
// position: new Topoboard.Vector(300, 300),
content: 'loading: 0/' + total,
font: new TB.model.Font(18, '微软雅黑'),
style: '#f40'
}).fill();
loading.count = 0;
loading.total = total;
loading.text = 'loading: 0/' + total;
}
};
// 500代表每隔多长时间才执行一帧动画
let loadingAni = new Topoboard.Animation(500);
let dot = 1;
loadingAni.addTask(function() {
if(! loading) {
return;
}
dot ++;
if(dot > 6) {
dot = 1;
}
let str = new Array(dot).fill(1).reduce(prev => prev + '.', '');
loading.dots = str;
loading.content = loading.text + loading.dots;
loading.refresh();
});
loadingAni.onenterframe = function() {
loadingLayer.refresh(); // 图层存在动画, 需要刷新
board.refresh(); // 画板存在动画, 需要刷新
};
loadingAni.start();
至此已经实现了一个加载中的动画, 其展示效果大概如下:
'loading:0/6.'
'loading:0/6..'
'loading:1/6..'
'loading:1/6...'
'loading:2/6....'
'loading:3/6.....'
// ...
loadingAni.stop();
loadingAni.restart();
当存在graph、layer修改时, 可以 通过board.refresh(true)
强制刷新所有layer和graph。应该注意的是,这样的操作是相当消耗性能的, 因为不论图层是否有变动, 图层及其下的所有graph都要重新绘制一次。
当board中存在某一个 graph 需要进行动画处理时, 应该尽量把它划分到单独图层中, 再进行精确刷新处理。比如像下面这种处理方式是比较高性能的处理方式:
graph.refresh();
layer.refresh();
board.refresh();
或者尽量把存在动画的所有 graph 划分到专有的layer中,然后采用如下 方式进行刷新:
layer.refresh(true);
board.refresh();
在此基础上还可以实现更多的功能, 发挥你的脑洞。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。