2018-12-16 16:02:42 +08:00

432 lines
13 KiB
JavaScript

import UI from '../../ui/UI';
import AnimationGroup from '../../animation/AnimationGroup';
import Animation from '../../animation/Animation';
const STOP = 0;
const PLAY = 1;
const PAUSE = 2;
/**
* 时间面板
* @author tengge / https://github.com/tengge1
*/
function TimePanel(options) {
UI.Control.call(this, options);
this.app = options.app;
this.status = STOP;
this.sliderLeft = 0;
this.speed = 4;
this.isDragging = false;
};
TimePanel.prototype = Object.create(UI.Control.prototype);
TimePanel.prototype.constructor = TimePanel;
TimePanel.prototype.render = function () {
var data = {
xtype: 'div',
parent: this.parent,
cls: 'animation-panel',
children: [{
xtype: 'div',
cls: 'controls',
children: [{
xtype: 'iconbutton',
icon: 'icon-add',
onClick: this.onAddGroup.bind(this)
}, {
xtype: 'iconbutton',
icon: 'icon-delete',
onClick: this.onRemoveGroup.bind(this)
}, {
xtype: 'div',
style: {
width: '2px',
height: '20px',
borderLeft: '1px solid #aaa',
borderRight: '1px solid #aaa',
boxSizing: 'border-box',
margin: '5px 8px'
}
}, {
xtype: 'iconbutton',
icon: 'icon-backward',
onClick: this.onBackward.bind(this)
}, {
xtype: 'iconbutton',
id: 'btnPlay',
scope: this.id,
icon: 'icon-play',
onClick: this.onPlay.bind(this)
}, {
xtype: 'iconbutton',
id: 'btnPause',
scope: this.id,
icon: 'icon-pause',
style: {
display: 'none'
},
onClick: this.onPause.bind(this)
}, {
xtype: 'iconbutton',
icon: 'icon-forward',
onClick: this.onForward.bind(this)
}, {
xtype: 'iconbutton',
icon: 'icon-stop',
onClick: this.onStop.bind(this)
}, {
xtype: 'text',
id: 'time',
scope: this.id,
style: {
marginLeft: '8px',
color: '#555',
fontSize: '12px'
},
text: '00:00'
}, {
xtype: 'text',
id: 'speed',
scope: this.id,
style: {
marginLeft: '8px',
color: '#aaa',
fontSize: '12px'
},
text: 'X 1'
}, {
xtype: 'toolbarfiller'
}, {
xtype: 'text',
scope: this.id,
style: {
marginLeft: '8px',
color: '#aaa',
fontSize: '12px'
},
text: '说明:双击时间轴下方添加动画。'
}]
}, {
xtype: 'div',
cls: 'box',
children: [{
xtype: 'div',
cls: 'left-area',
id: 'groupInfo',
scope: this.id
}, {
xtype: 'div',
cls: 'right-area',
children: [{
xtype: 'timeline',
id: 'timeline',
cls: 'timeline',
scope: this.id
}, {
xtype: 'div',
cls: 'groups',
id: 'groups',
scope: this.id,
children: []
}, {
xtype: 'div',
cls: 'slider',
id: 'slider',
scope: this.id
}]
}]
}]
};
var control = UI.create(data);
control.render();
this.app.on(`appStarted.${this.id}`, this.onAppStarted.bind(this));
};
TimePanel.prototype.onAppStarted = function () {
var timeline = UI.get('timeline', this.id);
var groups = UI.get('groups', this.id);
timeline.updateUI();
groups.dom.style.width = timeline.dom.clientWidth + 'px';
groups.dom.addEventListener(`click`, this.onClick.bind(this));
groups.dom.addEventListener(`dblclick`, this.onDblClick.bind(this));
groups.dom.addEventListener(`mousedown`, this.onMouseDown.bind(this));
groups.dom.addEventListener(`mousemove`, this.onMouseMove.bind(this));
document.body.addEventListener(`mouseup`, this.onMouseUp.bind(this));
this.app.on(`animationChanged.${this.id}`, this.updateUI.bind(this));
this.app.on(`resetAnimation.${this.id}`, this.onResetAnimation.bind(this));
this.app.on(`startAnimation.${this.id}`, this.onPlay.bind(this));
};
TimePanel.prototype.updateUI = function () {
var animations = this.app.editor.animation.getAnimationGroups();
var groupInfo = UI.get('groupInfo', this.id);
var timeline = UI.get('timeline', this.id);
var groups = UI.get('groups', this.id);
while (groupInfo.dom.children.length) {
var child = groupInfo.dom.children[0];
groupInfo.dom.removeChild(child);
}
while (groups.dom.children.length) {
var child = groups.dom.children[0];
child.data = null;
groups.dom.removeChild(child);
}
animations.forEach(n => {
// 动画组信息区
var groupName = document.createElement('div');
groupName.className = 'group-info';
groupName.innerHTML = `<input type="checkbox" data-uuid="${n.uuid}" />${n.name}`;
groupInfo.dom.appendChild(groupName);
// 动画区
var group = document.createElement('div');
group.className = 'group';
group.setAttribute('droppable', true);
group.data = n;
group.addEventListener('dragenter', this.onDragEnterGroup.bind(this));
group.addEventListener('dragover', this.onDragOverGroup.bind(this));
group.addEventListener('dragleave', this.onDragLeaveGroup.bind(this));
group.addEventListener('drop', this.onDropGroup.bind(this));
groups.dom.appendChild(group);
n.animations.forEach(m => {
var item = document.createElement('div');
item.data = m;
item.className = 'item';
item.setAttribute('draggable', true);
item.setAttribute('droppable', false);
item.style.left = m.beginTime * timeline.scale + 'px';
item.style.width = (m.endTime - m.beginTime) * timeline.scale + 'px';
item.innerHTML = m.name;
item.addEventListener('dragstart', this.onDragStartAnimation.bind(this));
item.addEventListener('dragend', this.onDragEndAnimation.bind(this));
group.appendChild(item);
});
});
};
TimePanel.prototype.updateSlider = function () {
var timeline = UI.get('timeline', this.id);
var slider = UI.get('slider', this.id);
var time = UI.get('time', this.id);
var speed = UI.get('speed', this.id);
slider.dom.style.left = this.sliderLeft + 'px';
var animationTime = this.sliderLeft / timeline.scale;
var minute = ('0' + Math.floor(animationTime / 60)).slice(-2);
var second = ('0' + Math.floor(animationTime % 60)).slice(-2);
time.setValue(`${minute}:${second}`);
if (this.speed >= 4) {
speed.dom.innerHTML = `X ${this.speed / 4}`;
} else {
speed.dom.innerHTML = `X 1/${4 / this.speed}`;
}
this.app.call('animationTime', this, animationTime);
};
TimePanel.prototype.onAnimate = function () {
var timeline = UI.get('timeline', this.id);
this.sliderLeft += this.speed / 4;
if (this.sliderLeft >= timeline.dom.clientWidth) {
this.sliderLeft = 0;
}
this.updateSlider();
};
TimePanel.prototype.onAddGroup = function () {
var group = new AnimationGroup();
this.app.editor.animation.add(group);
this.updateUI();
};
TimePanel.prototype.onRemoveGroup = function () {
var inputs = document.querySelectorAll('.animation-panel .left-area input:checked');
var uuids = [];
inputs.forEach(n => {
uuids.push(n.getAttribute('data-uuid'));
});
if (uuids.length === 0) {
UI.msg('请勾选需要删除的组!');
return;
}
UI.confirm('询问', '删除组会删除组上的所有动画。是否删除?', (event, btn) => {
if (btn === 'ok') {
uuids.forEach(n => {
this.app.editor.animation.removeByUUID(n);
});
this.updateUI();
}
});
};
// ----------------------------------- 播放器事件 -------------------------------------------
TimePanel.prototype.onPlay = function () {
if (this.status === PLAY) {
return;
}
this.status = PLAY;
UI.get('btnPlay', this.id).dom.style.display = 'none';
UI.get('btnPause', this.id).dom.style.display = '';
this.app.on(`animate.${this.id}`, this.onAnimate.bind(this));
};
TimePanel.prototype.onPause = function () {
if (this.status === PAUSE) {
return;
}
this.status = PAUSE;
UI.get('btnPlay', this.id).dom.style.display = '';
UI.get('btnPause', this.id).dom.style.display = 'none';
this.app.on(`animate.${this.id}`, null);
this.updateSlider();
};
TimePanel.prototype.onForward = function () {
if (this.speed >= 16) {
return;
}
this.speed *= 2;
};
TimePanel.prototype.onBackward = function () {
if (this.speed <= 1) {
return;
}
this.speed /= 2;
};
TimePanel.prototype.onStop = function () {
if (this.status === STOP) {
return;
}
this.status = STOP;
UI.get('btnPlay', this.id).dom.style.display = '';
UI.get('btnPause', this.id).dom.style.display = 'none';
this.app.on(`animate.${this.id}`, null);
this.sliderLeft = 0;
this.updateSlider();
};
TimePanel.prototype.onResetAnimation = function () {
this.onStop();
this.speed = 4;
};
TimePanel.prototype.onClick = function (event) {
if (event.target.data.type === 'AnimationGroup') {
return;
}
this.app.call('tabSelected', this, 'animation');
this.app.call('animationSelected', this, event.target.data);
};
TimePanel.prototype.onDblClick = function (event) {
var timeline = UI.get('timeline', this.id);
if (event.target.data && event.target.data.type === 'AnimationGroup') {
event.stopPropagation();
var animation = new Animation({
beginTime: event.offsetX / timeline.scale,
endTime: (event.offsetX + 80) / timeline.scale
});
event.target.data.add(animation);
this.app.call('animationChanged', this);
}
};
TimePanel.prototype.onMouseDown = function () {
if (this.isDragging) {
return;
}
this.isDragging = true;
};
TimePanel.prototype.onMouseMove = function () {
};
TimePanel.prototype.onMouseUp = function () {
this.isDragging = false;
};
// ----------------------- 拖动动画事件 ---------------------------------------------
TimePanel.prototype.onDragStartAnimation = function (event) {
event.dataTransfer.setData('uuid', event.target.data.uuid);
event.dataTransfer.setData('offsetX', event.offsetX);
};
TimePanel.prototype.onDragEndAnimation = function (event) {
event.dataTransfer.clearData();
};
TimePanel.prototype.onDragEnterGroup = function (event) {
event.preventDefault();
};
TimePanel.prototype.onDragOverGroup = function (event) {
event.preventDefault();
};
TimePanel.prototype.onDragLeaveGroup = function (event) {
event.preventDefault();
};
TimePanel.prototype.onDropGroup = function (event) {
event.preventDefault();
var uuid = event.dataTransfer.getData('uuid');
var offsetX = event.dataTransfer.getData('offsetX');
var groups = this.app.editor.animation.getAnimationGroups();
var group = groups.filter(n => n.animations.findIndex(m => m.uuid === uuid) > -1)[0];
var animation = group.animations.filter(n => n.uuid === uuid)[0];
group.remove(animation);
var timeline = UI.get('timeline', this.id);
var length = animation.endTime - animation.beginTime;
animation.beginTime = (event.offsetX - offsetX) / timeline.scale;
animation.endTime = animation.beginTime + length;
if (event.target.data instanceof Animation) { // 拖动到其他动画上
event.target.parentElement.data.add(animation);
} else { // 拖动到动画组上
event.target.data.add(animation);
}
this.updateUI();
};
export default TimePanel;