增加动画处理

This commit is contained in:
ival 2021-04-04 00:44:29 +08:00
parent 870852b97a
commit eb429506f5
8 changed files with 183 additions and 51 deletions

View File

@ -0,0 +1,76 @@
import Group from 'zrender/src/container/Group';
class Animate {
constructor(state) {
this.state = {...state}
this.shapeFactory = this.shape? this.shape.shapeFactory: null;
this.timer = null;
this.total = 0;
this.count = 0;
}
run(delay) {
this.total = this.total + delay;
if (this.total > this.state.time) {
clearTimeout(this.timer);
this.timer = setTimeout(this.__animate.bind(this), this.state.delay);
this.count = this.count + 1;
this.total = 0;
}
}
isLoop() {
return this.state.loop;
}
__animate() {
const shape = this.state.shape;
const frameList = this.state.frameList;
const mapView = this.__traverse(shape, {});
if(shape && frameList) {
const size = frameList.length;
const frame = frameList[this.count%size];
Object.keys(frame).forEach(name => {
const view = mapView[name];
const model = frame[name];
if (view && model) {
view.attr({shape: model.shape, style: model.style});
}
})
}
}
__traverse(group, map) {
group.eachChild(el => {
if (el instanceof Group) {
this.__traverse(el, map);
} else {
map[el.name] = el;
}
})
return map;
}
}
class AnimateHandle {
constructor(painter) {
this.animates = [];
}
onframe (delay) {
const animate = this.animates.shift();
if (animate) {
animate.run(delay);
if (animate.isLoop()) {
this.animates.push(animate);
}
}
}
animate(state) {
this.animates.push(new Animate(state));
}
}
export default AnimateHandle

View File

@ -44,6 +44,7 @@ class Element extends AbstractShape {
this.instance.origin = utils.createOrigin(this.instance); this.instance.origin = utils.createOrigin(this.instance);
this.instance.transform = utils.createTransform({scale: this.model.scale, position: this.model.position, rotation: this.model.rotation}); this.instance.transform = utils.createTransform({scale: this.model.scale, position: this.model.position, rotation: this.model.rotation});
this.add(this.instance); this.add(this.instance);
// this.instance.animate('style', true).when(1000, {fill: 'blue'}).when(2000, {fill: 'rellow'}).start();
} }
} }

View File

@ -60,13 +60,13 @@ function update2List(source, model, action, name='') {
return updateModel; return updateModel;
} }
class ShapeFactory extends Eventful { class ShapeFactory extends Eventful {
constructor(map) { constructor(map) {
super(); super();
this.$map = map; this.$map = map;
this.$painter = map.getPainter(); this.$painter = map.getPainter();
this.$controller = map.getController(); this.$controller = map.getController();
this.$zr = map.getZr();
this.border = new graphic.Rect(shapeStyleBuilder()); this.border = new graphic.Rect(shapeStyleBuilder());
@ -100,8 +100,9 @@ class ShapeFactory extends Eventful {
parseTemplates(list=[]) { parseTemplates(list=[]) {
list.forEach(el => { list.forEach(el => {
this.mapTemplate[el.type] = templateParser.parser(el); this.mapTemplate[el.type] = templateParser.parse(el);
}) })
console.log(this.mapTemplate);
} }
parse(source={}, take=None) { parse(source={}, take=None) {

View File

@ -3,8 +3,8 @@ class TemplateParser {
constructor() { constructor() {
} }
parser(template) { parse(template) {
const model = { return {
type: template.type, type: template.type,
name: template.name, name: template.name,
isActive: template.isActive, isActive: template.isActive,
@ -13,7 +13,6 @@ class TemplateParser {
mapState: this.parseState(template.stateList), mapState: this.parseState(template.stateList),
mapEvent: this.parseEvent(template.eventList), mapEvent: this.parseEvent(template.eventList),
} }
return model;
} }
parseShape(list=[], map={}) { parseShape(list=[], map={}) {

View File

@ -43,25 +43,32 @@ class JMap {
const optionHandler = this.setOption.bind(this); const optionHandler = this.setOption.bind(this);
this.draw = opts.draw; this.draw = opts.draw;
// 实例化zr
this.$zr = zrender.init(opts.dom, { renderer, devicePixelRatio, width, height, ...utils.deepClone(opts.config||{})}); this.$zr = zrender.init(opts.dom, { renderer, devicePixelRatio, width, height, ...utils.deepClone(opts.config||{})});
this.$zr.dom.setAttribute('tabIndex', -1); this.$zr.dom.setAttribute('tabIndex', -1);
this.$zr.dom.style.cursor = 'auto'; this.$zr.dom.style.cursor = 'auto';
// 实例化缩放偏移缩放参数
this.$option = new Option({ scaleRate: 1, offsetX: 0, offsetY: 0, ...utils.deepClone(opts.option||{})}, (dataZoom) => { this.$controller.trigger(events.DataZoom, dataZoom); }); // 缩放 this.$option = new Option({ scaleRate: 1, offsetX: 0, offsetY: 0, ...utils.deepClone(opts.option||{})}, (dataZoom) => { this.$controller.trigger(events.DataZoom, dataZoom); }); // 缩放
// 实例化绘图模块
this.$painter = new Painter(this); this.$painter = new Painter(this);
this.$painter.updateZrSize({width: this.$zr.getWidth(), height: this.$zr.getHeight()}); this.$painter.updateZrSize({width: this.$zr.getWidth(), height: this.$zr.getHeight()});
this.$painter.updateTransform(this.$option); this.$painter.updateTransform(this.$option);
// 实例化事件分发模块
this.$controller = new Controller(this); this.$controller = new Controller(this);
this.$controller.enable(); this.$controller.enable();
this.$controller.on(this.events.__Pan, optionHandler); this.$controller.on(this.events.__Pan, optionHandler);
this.$controller.on(this.events.__Zoom, optionHandler); this.$controller.on(this.events.__Zoom, optionHandler);
this.$eventEmitter = new Eventful(this); // 名声周期发射器
this.$eventEmitter = new Eventful({});
// 数据容器工厂
this.$shapeFactory = new ShapeFactory(this); this.$shapeFactory = new ShapeFactory(this);
// 状态处理器
this.$stateHandle = new StateHandle(this); this.$stateHandle = new StateHandle(this);
this.disable = function() { this.disable = function() {
@ -71,6 +78,10 @@ class JMap {
} }
setMap(templates=[], source={}, eventOpts={}) { setMap(templates=[], source={}, eventOpts={}) {
// 清楚数据
this.$shapeFactory.clear();
this.$painter.clear();
// 绑定事件 // 绑定事件
this.$controller.enable(eventOpts); this.$controller.enable(eventOpts);
@ -135,8 +146,6 @@ class JMap {
} }
repaint(source={}) { repaint(source={}) {
this.$shapeFactory.clear();
this.$painter.clear();
this.$shapeFactory.parse(source, shape => { this.$shapeFactory.parse(source, shape => {
if (shape) { if (shape) {
this.$painter.add(shape); this.$painter.add(shape);
@ -193,7 +202,7 @@ class JMap {
} }
update(list=[]) { update(list=[]) {
this.$painter.update(this.$stateHandle.update(list)); this.$painter.update(this.$stateHandle.update(this.$shapeFactory, list));
this.$eventEmitter.trigger(events.StateUpdate, list); this.$eventEmitter.trigger(events.StateUpdate, list);
return this; return this;
} }
@ -274,6 +283,9 @@ class JMap {
case events.DataLoaded: case events.DataLoaded:
this.$eventEmitter.on(events.DataLoaded, cb, context); this.$eventEmitter.on(events.DataLoaded, cb, context);
break; break;
case events.ViewLoaded:
this.$eventEmitter.on(events.ViewLoaded, cb, context);
break;
case events.StateLoaded: case events.StateLoaded:
this.$eventEmitter.on(events.StateLoaded, cb, context); this.$eventEmitter.on(events.StateLoaded, cb, context);
break; break;
@ -286,15 +298,16 @@ class JMap {
case events.OptionUpdate: case events.OptionUpdate:
this.$eventEmitter.on(events.OptionUpdate, cb, context); this.$eventEmitter.on(events.OptionUpdate, cb, context);
break; break;
case events.Reflect:
this.$controller.on(events.Reflect, _.throttle(cb, 200), context);
break;
case events.Selected: case events.Selected:
this.$controller.on(events.Selected, cb, context); this.$controller.on(events.Selected, cb, context);
break; break;
case events.ContextMenu: case events.ContextMenu:
this.$controller.on(events.ContextMenu, cb, context); this.$controller.on(events.ContextMenu, cb, context);
break; break;
case events.Reflect:
this.$controller.on(events.Reflect, _.throttle(cb, 200), context);
break;
case events.DataZoom: case events.DataZoom:
this.$controller.on(events.DataZoom, cb, context); this.$controller.on(events.DataZoom, cb, context);
break; break;
@ -318,6 +331,9 @@ class JMap {
case events.DataLoaded: case events.DataLoaded:
this.$eventEmitter.off(events.DataLoaded, cb, context); this.$eventEmitter.off(events.DataLoaded, cb, context);
break; break;
case events.ViewLoaded:
this.$eventEmitter.off(events.ViewLoaded, cb, context);
break;
case events.StateLoaded: case events.StateLoaded:
this.$eventEmitter.off(events.StateLoaded, cb, context); this.$eventEmitter.off(events.StateLoaded, cb, context);
break; break;
@ -330,15 +346,16 @@ class JMap {
case events.OptionUpdate: case events.OptionUpdate:
this.$eventEmitter.off(events.OptionUpdate, cb, context); this.$eventEmitter.off(events.OptionUpdate, cb, context);
break; break;
case events.Reflect:
this.$controller.off(events.Reflect, _.throttle(cb, 200));
break;
case events.Selected: case events.Selected:
this.$controller.off(events.Selected, cb); this.$controller.off(events.Selected, cb);
break; break;
case events.ContextMenu: case events.ContextMenu:
this.$controller.off(events.ContextMenu, cb); this.$controller.off(events.ContextMenu, cb);
break; break;
case events.Reflect:
this.$controller.off(events.Reflect, _.throttle(cb, 200));
break;
case events.DataZoom: case events.DataZoom:
this.$controller.off(events.DataZoom, cb); this.$controller.off(events.DataZoom, cb);
break; break;

View File

@ -2,28 +2,41 @@ import * as graphic from './core/graphic';
import shapeLayer from './constant/shapeLayer'; import shapeLayer from './constant/shapeLayer';
import Group from 'zrender/src/container/Group'; import Group from 'zrender/src/container/Group';
import TransformHandle from './transformHandle'; import TransformHandle from './transformHandle';
import AnimateHandle from './animateHandle';
class Painter extends Group { class Painter extends Group {
constructor(map) { constructor(map) {
super({name: `__Container__` }); super({name: `__Container__` });
// 添加父级图层
const zr = map.getZr();
zr.add(this);
// 初始图层 // 初始图层
this.initLevels(map); this.initLevels(map);
// 视图控制器 // 视图控制器
this.$transformHandle = new TransformHandle(this); this.$transformHandle = new TransformHandle(this);
// 动画处理器
this.$animateHandle = new AnimateHandle(this);
// 重新动画函数,加入钩子
this.onframe(zr);
}
onframe(zr) {
const onframe = zr.animation.onframe;
const animateHandle = this.$animateHandle;
zr.animation.onframe = (...args) => {
onframe.apply(zr.animation, args);
animateHandle.onframe(...args);
}
} }
initLevels(map) { initLevels(map) {
// 初始化图层对象 // 初始化图层对象
this.mapShapeLayer = {}; this.mapShapeLayer = {};
// 获取顶层图层
this.$zr = map.getZr();
// 添加父级图层
this.$zr.add(this);
// 创建select图层 // 创建select图层
this.mapShapeLayer[shapeLayer.Selecting] = new Group({ name: shapeLayer.Selecting }); this.mapShapeLayer[shapeLayer.Selecting] = new Group({ name: shapeLayer.Selecting });
@ -58,10 +71,10 @@ class Painter extends Group {
} }
} }
update(shape) { update(stateList=[]) {
if (shape) { stateList.forEach(state => {
// this.$animateHandle.animate(state);
} })
} }
updateTransform(opt) { updateTransform(opt) {
@ -100,6 +113,7 @@ class Painter extends Group {
destroy() { destroy() {
this.clear(); this.clear();
this.mapShapeLayer = {}; this.mapShapeLayer = {};
this.mapAnimate = {};
} }
} }

View File

@ -5,8 +5,24 @@ export default class StateHandle {
} }
parse(shapeFactory, state) { parse(shapeFactory, state) {
console.log(shapeFactory, state); const mapTemplate = shapeFactory.getMapTemplate();
return state; const template = mapTemplate[state.type];
const templateState = template.mapState[state.status];
return {
...state,
...templateState,
shape: shapeFactory.getShapeByCode(state.code),
frameList: templateState.frameList.map(frame => {
return Object.fromEntries(frame.map(el => {
const mapState = template.mapShape[el.name];
return [el.name, {
...el,
...mapState[el.status]
}]
}));
})
}
} }
update(shapeFactory, states=[]) { update(shapeFactory, states=[]) {
@ -17,7 +33,7 @@ export default class StateHandle {
// this.updateState(this.parse(shapeFactory, state)), // 处理自身 // this.updateState(this.parse(shapeFactory, state)), // 处理自身
// this.updateState(this.parse(shapeFactory, state)) // 处理依赖 // this.updateState(this.parse(shapeFactory, state)) // 处理依赖
]; ];
}, []); }, []).sort((a,b) => a.weight - b.weight);
} }
updateState(state={}) { updateState(state={}) {

View File

@ -93,7 +93,10 @@ export default {
}); });
Vue.prototype.$iscs = this.$iscs; Vue.prototype.$iscs = this.$iscs;
this.$iscs.on('viewLoaded', this.onUpdate, this);
this.$iscs.on('contextmenu', this.onContextMenu, this);
this.$iscs.on('selected', this.onSelected, this);
this.$iscs.on('keyboard', this.onKeyboard, this);
this.$iscs.setMap([ this.$iscs.setMap([
{ {
type: 'Device', type: 'Device',
@ -104,7 +107,10 @@ export default {
{ name: 'a', { name: 'a',
type: 'Rect', type: 'Rect',
shape: {}, shape: {},
style: {}, style: {
fill: 'red',
stroke: 'black'
},
stateList: [ stateList: [
{ status: 'st1', shape: {}, style:{ fill: 'yellow', stroke: 'black'} }, { status: 'st1', shape: {}, style:{ fill: 'yellow', stroke: 'black'} },
{ status: 'st2', shape: {}, style:{ fill: 'blue', stroke: 'black'} } { status: 'st2', shape: {}, style:{ fill: 'blue', stroke: 'black'} }
@ -113,7 +119,10 @@ export default {
{ name: 'b', { name: 'b',
type: 'Circle', type: 'Circle',
shape: {}, shape: {},
style: {}, style: {
fill: 'red',
stroke: 'black'
},
stateList: [ stateList: [
{ status: 'st1', shape: {}, style:{ fill: 'yellow', stroke: 'black'} }, { status: 'st1', shape: {}, style:{ fill: 'yellow', stroke: 'black'} },
{ status: 'st2', shape: {}, style:{ fill: 'blue', stroke: 'black'} } { status: 'st2', shape: {}, style:{ fill: 'blue', stroke: 'black'} }
@ -121,8 +130,8 @@ export default {
}, },
], ],
stateList: [ stateList: [
{ status: 's1', frameList: [[{name: 'a', status: 'st1'}, {name: 'b', status: 'st1'}], [{name: 'a', status: 'st2'}, {name: 'b', status: 'st2'}]], weight: 2, needDefault: false, loop: true }, { status: 's1', frameList: [[{name: 'a', status: 'st1'}, {name: 'b', status: 'st1'}], [{name: 'a', status: 'st2'}, {name: 'b', status: 'st2'}]], weight: 2, loop: true, delay: 2000, time: 200, needDefault: false },
{ status: 's2', frameList: [[{name: 'a', status: 'st2'}, {name: 'b', status: 'st1'}], [{name: 'a', status: 'st1'}, {name: 'b', status: 'st2'}]], weight: 2, needDefault: false, lopp: false } { status: 's2', frameList: [[{name: 'a', status: 'st2'}, {name: 'b', status: 'st1'}], [{name: 'a', status: 'st1'}, {name: 'b', status: 'st2'}]], weight: 1, loop: true, delay: 5000, time: 500, needDefault: true }
] ]
} }
], { ], {
@ -268,7 +277,7 @@ export default {
scale: [0.5, 0.5], scale: [0.5, 0.5],
position: [100, 100], position: [100, 100],
rotation: Math.PI/2, rotation: Math.PI/2,
composeCode: '1000', // composeCode: '1000',
}, },
{ {
code: '101', code: '101',
@ -277,17 +286,17 @@ export default {
scale: [1, 1], scale: [1, 1],
position: [200, 0], position: [200, 0],
rotation: 0, rotation: 0,
composeCode: '1000' // composeCode: '1000'
}, },
{ // {
code: '1000', // code: '1000',
type: 'Device', // type: 'Device',
scale: [1, 1], // scale: [1, 1],
position: [0, 0], // position: [0, 0],
rotation: 0, // rotation: 0,
elementCodes: ['100', '101'], // elementCodes: ['100', '101'],
composeCode: '' // composeCode: ''
} // }
] ]
}, { }, {
panEnable: true, panEnable: true,
@ -299,18 +308,17 @@ export default {
reflect: true reflect: true
}); });
this.$iscs.update([
{ status: 's1', frameList: [[{name: 'a', status: 'st1'}, {name: 'b', status: 'st1'}], [{name: 'a', status: 'st2'}, {name: 'b', status: 'st2'}]], weight: 2, needDefault: false, loop: true },
{ status: 's2', frameList: [[{name: 'a', status: 'st2'}, {name: 'b', status: 'st1'}], [{name: 'a', status: 'st1'}, {name: 'b', status: 'st2'}]], weight: 2, needDefault: false, lopp: false }
]);
this.$iscs.on('contextmenu', this.onContextMenu, this);
this.$iscs.on('selected', this.onSelected, this);
this.$iscs.on('keyboard', this.onKeyboard, this);
window.document.oncontextmenu = function () { window.document.oncontextmenu = function () {
return false; return false;
}; };
}, },
onUpdate(e) {
this.$iscs.update([
{ status: 's1', code: '100', type: 'Device' },
{ status: 's2', code: '101', type: 'Device' }
]);
},
// //
onKeyboard(hook) { onKeyboard(hook) {
console.log(hook); console.log(hook);