在使用vis时,碰到一个需求,需要将时间轴颜色设置为自定义颜色,如下图红色框
API中找了一下,可以通过.vis-background设置背景颜色
好像是不能满足需求,继续找,时间轴在上方,vis-top设置完
嗯。。。有一半满足需求了,剩下的怎么办?
好吧,翻源码吧 node_modules/vis-timeline/peer/esm/vis-timeline-graph2d.js
找到构造函数Timeline定义
function Timeline(container, items, groups, options) {
...
// Create the DOM, props, and emitter
_this._create(container);
...
}
/**
* Create a timeline visualization
* @constructor Core
*/
var Core = /*#__PURE__*/function () {
function Core() {
_classCallCheck(this, Core);
}
_createClass(Core, [{
key: "_create",
value:
/**
* Create the main DOM for the Core: a root panel containing left, right,
* top, bottom, content, and background panel.
* @param {Element} container The container element where the Core will
* be attached.
* @protected
*/
function _create(container) {
var _this = this,
_context,
_context2,
_context3;
this.dom = {};
this.dom.container = container;
this.dom.container.style.position = 'relative';
this.dom.root = document.createElement('div');
this.dom.background = document.createElement('div');
this.dom.backgroundVertical = document.createElement('div');
this.dom.backgroundHorizontal = document.createElement('div');
this.dom.centerContainer = document.createElement('div');
this.dom.leftContainer = document.createElement('div');
this.dom.rightContainer = document.createElement('div');
this.dom.center = document.createElement('div');
this.dom.left = document.createElement('div');
this.dom.right = document.createElement('div');
this.dom.top = document.createElement('div');
this.dom.bottom = document.createElement('div');
this.dom.shadowTop = document.createElement('div');
this.dom.shadowBottom = document.createElement('div');
this.dom.shadowTopLeft = document.createElement('div');
this.dom.shadowBottomLeft = document.createElement('div');
this.dom.shadowTopRight = document.createElement('div');
this.dom.shadowBottomRight = document.createElement('div');
this.dom.rollingModeBtn = document.createElement('div');
this.dom.loadingScreen = document.createElement('div');
this.dom.root.className = 'vis-timeline';
this.dom.background.className = 'vis-panel vis-background';
this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical';
this.dom.backgroundHorizontal.className = 'vis-panel vis-background vis-horizontal';
this.dom.centerContainer.className = 'vis-panel vis-center';
this.dom.leftContainer.className = 'vis-panel vis-left';
this.dom.rightContainer.className = 'vis-panel vis-right';
this.dom.top.className = 'vis-panel vis-top';
this.dom.bottom.className = 'vis-panel vis-bottom';
this.dom.left.className = 'vis-content';
this.dom.center.className = 'vis-content';
this.dom.right.className = 'vis-content';
this.dom.shadowTop.className = 'vis-shadow vis-top';
this.dom.shadowBottom.className = 'vis-shadow vis-bottom';
this.dom.shadowTopLeft.className = 'vis-shadow vis-top';
this.dom.shadowBottomLeft.className = 'vis-shadow vis-bottom';
this.dom.shadowTopRight.className = 'vis-shadow vis-top';
this.dom.shadowBottomRight.className = 'vis-shadow vis-bottom';
this.dom.rollingModeBtn.className = 'vis-rolling-mode-btn';
this.dom.loadingScreen.className = 'vis-loading-screen';
this.dom.root.appendChild(this.dom.background);
this.dom.root.appendChild(this.dom.backgroundVertical);
this.dom.root.appendChild(this.dom.backgroundHorizontal);
this.dom.root.appendChild(this.dom.centerContainer);
this.dom.root.appendChild(this.dom.leftContainer);
this.dom.root.appendChild(this.dom.rightContainer);
this.dom.root.appendChild(this.dom.top);
this.dom.root.appendChild(this.dom.bottom);
this.dom.root.appendChild(this.dom.rollingModeBtn);
this.dom.centerContainer.appendChild(this.dom.center);
this.dom.leftContainer.appendChild(this.dom.left);
this.dom.rightContainer.appendChild(this.dom.right);
this.dom.centerContainer.appendChild(this.dom.shadowTop);
this.dom.centerContainer.appendChild(this.dom.shadowBottom);
this.dom.leftContainer.appendChild(this.dom.shadowTopLeft);
this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft);
this.dom.rightContainer.appendChild(this.dom.shadowTopRight);
this.dom.rightContainer.appendChild(this.dom.shadowBottomRight);
// size properties of each of the panels
this.props = {
root: {},
background: {},
centerContainer: {},
leftContainer: {},
rightContainer: {},
center: {},
left: {},
right: {},
top: {},
bottom: {},
border: {},
scrollTop: 0,
scrollTopMin: 0
};
...
}
},...{
key: "redraw",
value: function redraw() {
this._redraw();
}
/**
* Redraw for internal use. Redraws all components. See also the public
* method redraw.
* @protected
*/
}, {
key: "_redraw",
value: function _redraw() {
var _context20;
this.redrawCount++;
var dom = this.dom;
if (!dom || !dom.container || dom.root.offsetWidth == 0) return; // when destroyed, or invisible
var resized = false;
var options = this.options;
var props = this.props;
updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates);
// update class names
if (options.orientation == 'top') {
availableUtils.addClassName(dom.root, 'vis-top');
availableUtils.removeClassName(dom.root, 'vis-bottom');
} else {
availableUtils.removeClassName(dom.root, 'vis-top');
availableUtils.addClassName(dom.root, 'vis-bottom');
}
if (options.rtl) {
availableUtils.addClassName(dom.root, 'vis-rtl');
availableUtils.removeClassName(dom.root, 'vis-ltr');
} else {
availableUtils.addClassName(dom.root, 'vis-ltr');
availableUtils.removeClassName(dom.root, 'vis-rtl');
}
// update root width and height options
dom.root.style.maxHeight = availableUtils.option.asSize(options.maxHeight, '');
dom.root.style.minHeight = availableUtils.option.asSize(options.minHeight, '');
dom.root.style.width = availableUtils.option.asSize(options.width, '');
var rootOffsetWidth = dom.root.offsetWidth;
// calculate border widths
props.border.left = 1;
props.border.right = 1;
props.border.top = 1;
props.border.bottom = 1;
// calculate the heights. If any of the side panels is empty, we set the height to
// minus the border width, such that the border will be invisible
props.center.height = dom.center.offsetHeight;
props.left.height = dom.left.offsetHeight;
props.right.height = dom.right.offsetHeight;
props.top.height = dom.top.clientHeight || -props.border.top;
props.bottom.height = Math.round(dom.bottom.getBoundingClientRect().height) || dom.bottom.clientHeight || -props.border.bottom;
// TODO: compensate borders when any of the panels is empty.
// apply auto height
// TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM)
var contentHeight = Math.max(props.left.height, props.center.height, props.right.height);
var autoHeight = props.top.height + contentHeight + props.bottom.height + props.border.top + props.border.bottom;
dom.root.style.height = availableUtils.option.asSize(options.height, "".concat(autoHeight, "px"));
// calculate heights of the content panels
props.root.height = dom.root.offsetHeight;
props.background.height = props.root.height;
var containerHeight = props.root.height - props.top.height - props.bottom.height;
props.centerContainer.height = containerHeight;
props.leftContainer.height = containerHeight;
props.rightContainer.height = props.leftContainer.height;
// calculate the widths of the panels
props.root.width = rootOffsetWidth;
props.background.width = props.root.width;
if (!this.initialDrawDone) {
props.scrollbarWidth = availableUtils.getScrollBarWidth();
}
var leftContainerClientWidth = dom.leftContainer.clientWidth;
var rightContainerClientWidth = dom.rightContainer.clientWidth;
if (options.verticalScroll) {
if (options.rtl) {
props.left.width = leftContainerClientWidth || -props.border.left;
props.right.width = rightContainerClientWidth + props.scrollbarWidth || -props.border.right;
} else {
props.left.width = leftContainerClientWidth + props.scrollbarWidth || -props.border.left;
props.right.width = rightContainerClientWidth || -props.border.right;
}
} else {
props.left.width = leftContainerClientWidth || -props.border.left;
props.right.width = rightContainerClientWidth || -props.border.right;
}
this._setDOM();
// update the scrollTop, feasible range for the offset can be changed
// when the height of the Core or of the contents of the center changed
var offset = this._updateScrollTop();
// reposition the scrollable contents
if (options.orientation.item != 'top') {
offset += Math.max(props.centerContainer.height - props.center.height - props.border.top - props.border.bottom, 0);
}
dom.center.style.transform = "translateY(".concat(offset, "px)");
// show shadows when vertical scrolling is available
var visibilityTop = props.scrollTop == 0 ? 'hidden' : '';
var visibilityBottom = props.scrollTop == props.scrollTopMin ? 'hidden' : '';
dom.shadowTop.style.visibility = visibilityTop;
dom.shadowBottom.style.visibility = visibilityBottom;
dom.shadowTopLeft.style.visibility = visibilityTop;
dom.shadowBottomLeft.style.visibility = visibilityBottom;
dom.shadowTopRight.style.visibility = visibilityTop;
dom.shadowBottomRight.style.visibility = visibilityBottom;
if (options.verticalScroll) {
dom.rightContainer.className = 'vis-panel vis-right vis-vertical-scroll';
dom.leftContainer.className = 'vis-panel vis-left vis-vertical-scroll';
dom.shadowTopRight.style.visibility = "hidden";
dom.shadowBottomRight.style.visibility = "hidden";
dom.shadowTopLeft.style.visibility = "hidden";
dom.shadowBottomLeft.style.visibility = "hidden";
dom.left.style.top = '0px';
dom.right.style.top = '0px';
}
if (!options.verticalScroll || props.center.height < props.centerContainer.height) {
dom.left.style.top = "".concat(offset, "px");
dom.right.style.top = "".concat(offset, "px");
dom.rightContainer.className = dom.rightContainer.className.replace(new RegExp('(?:^|\\s)' + 'vis-vertical-scroll' + '(?:\\s|$)'), ' ');
dom.leftContainer.className = dom.leftContainer.className.replace(new RegExp('(?:^|\\s)' + 'vis-vertical-scroll' + '(?:\\s|$)'), ' ');
props.left.width = leftContainerClientWidth || -props.border.left;
props.right.width = rightContainerClientWidth || -props.border.right;
this._setDOM();
}
// enable/disable vertical panning
var contentsOverflow = props.center.height > props.centerContainer.height;
this.hammer.get('pan').set({
direction: contentsOverflow ? Hammer.DIRECTION_ALL : Hammer.DIRECTION_HORIZONTAL
});
// set the long press time
this.hammer.get('press').set({
time: this.options.longSelectPressTime
});
// redraw all components
_forEachInstanceProperty(_context20 = this.components).call(_context20, function (component) {
resized = component.redraw() || resized;
});
var MAX_REDRAW = 5;
if (resized) {
if (this.redrawCount < MAX_REDRAW) {
this.body.emitter.emit('_change');
return;
} else {
console.log('WARNING: infinite loop in redraw?');
}
} else {
this.redrawCount = 0;
}
//Emit public 'changed' event for UI updates, see issue #1592
this.body.emitter.emit("changed");
}
/**
* sets the basic DOM components needed for the timeline\graph2d
*/
}, {
key: "_setDOM",
value: function _setDOM() {
var props = this.props;
var dom = this.dom;
props.leftContainer.width = props.left.width;
props.rightContainer.width = props.right.width;
var centerWidth = props.root.width - props.left.width - props.right.width;
props.center.width = centerWidth;
props.centerContainer.width = centerWidth;
props.top.width = centerWidth;
props.bottom.width = centerWidth;
// resize the panels
dom.background.style.height = "".concat(props.background.height, "px");
dom.backgroundVertical.style.height = "".concat(props.background.height, "px");
dom.backgroundHorizontal.style.height = "".concat(props.centerContainer.height, "px");
dom.centerContainer.style.height = "".concat(props.centerContainer.height, "px");
dom.leftContainer.style.height = "".concat(props.leftContainer.height, "px");
dom.rightContainer.style.height = "".concat(props.rightContainer.height, "px");
dom.background.style.width = "".concat(props.background.width, "px");
dom.backgroundVertical.style.width = "".concat(props.centerContainer.width, "px");
dom.backgroundHorizontal.style.width = "".concat(props.background.width, "px");
dom.centerContainer.style.width = "".concat(props.center.width, "px");
dom.top.style.width = "".concat(props.top.width, "px");
dom.bottom.style.width = "".concat(props.bottom.width, "px");
// reposition the panels
dom.background.style.left = '0';
dom.background.style.top = '0';
dom.backgroundVertical.style.left = "".concat(props.left.width + props.border.left, "px");
dom.backgroundVertical.style.top = '0';
dom.backgroundHorizontal.style.left = '0';
dom.backgroundHorizontal.style.top = "".concat(props.top.height, "px");
dom.centerContainer.style.left = "".concat(props.left.width, "px");
dom.centerContainer.style.top = "".concat(props.top.height, "px");
dom.leftContainer.style.left = '0';
dom.leftContainer.style.top = "".concat(props.top.height, "px");
dom.rightContainer.style.left = "".concat(props.left.width + props.center.width, "px");
dom.rightContainer.style.top = "".concat(props.top.height, "px");
dom.top.style.left = "".concat(props.left.width, "px");
dom.top.style.top = '0';
dom.bottom.style.left = "".concat(props.left.width, "px");
dom.bottom.style.top = "".concat(props.top.height + props.centerContainer.height, "px");
dom.center.style.left = '0';
dom.left.style.left = '0';
dom.right.style.left = '0';
}
/**
* Set a current time. This can be used for example to ensure that a client's
* time is synchronized with a shared server time.
* Only applicable when option `showCurrentTime` is true.
* @param {Date | string | number} time A Date, unix timestamp, or
* ISO date string.
*/
}]);
return Core;
}(); // turn Core into an event emitter
查看源码可知,先new Timeline,然后setOptions,setData,最后在创建dom、props、emitter。
这下思路就来了,先创建一个root下的子元素div,
const customLeft = document.createElement("div");
绑定到root元素下,添加自定义样式
timeline.dom.root.appendChild(customLeft);
customLeft.className = "vis-custom vis-left-top";
高度自行设置,此时宽度想要跟groups保持一致,查看源码得知,props中left的width为groups宽度
customLeft.style.width = timeline.props.left.width + "px";
如此,得到动态宽度(红色区域为自定义颜色)
完整代码
const container = document.getElementById("visualization");
const customLeft = document.createElement("div");
timeline = new Timeline(container);
timeline.setOptions({ ...options, ...times.value });
timeline.setData({ groups: groups.value, items: items.value });
timeline.dom.root.appendChild(customLeft);
customLeft.className = "vis-custom vis-left-top";
customLeft.style.width = timeline.props.left.width + "px";
timeline.on("doubleClick", timelineDoubleClick);