|
|
|
(function (root, factory) {
|
|
|
|
// if (typeof define === 'function' && define.amd) {
|
|
|
|
// // AMD. Register as an anonymous module.
|
|
|
|
// define([], function () {
|
|
|
|
// return (root.returnExportsGlobal = factory());
|
|
|
|
// });
|
|
|
|
// } else if (typeof exports === 'object') {
|
|
|
|
// // Node. Does not work with strict CommonJS, but
|
|
|
|
// // only CommonJS-like enviroments that support module.exports,
|
|
|
|
// // like Node.
|
|
|
|
// module.exports = factory();
|
|
|
|
// } else {
|
|
|
|
root['Chartist.plugins.zoom'] = factory();
|
|
|
|
// }
|
|
|
|
}(this, function () {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Chartist.js zoom plugin.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
(function (window, document, Chartist) {
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var defaultOptions = {
|
|
|
|
// onZoom
|
|
|
|
// resetOnRightMouseBtn
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Chartist.plugins = Chartist.plugins || {};
|
|
|
|
Chartist.plugins.zoom = function (options) {
|
|
|
|
|
|
|
|
options = Chartist.extend({}, defaultOptions, options);
|
|
|
|
|
|
|
|
return function zoom(chart) {
|
|
|
|
|
|
|
|
if (!(chart instanceof Chartist.Line)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var rect, svg, axisX, axisY, chartRect;
|
|
|
|
var downPosition;
|
|
|
|
var onZoom = options.onZoom;
|
|
|
|
var ongoingTouches = [];
|
|
|
|
|
|
|
|
chart.on('draw', function (data) {
|
|
|
|
var type = data.type;
|
|
|
|
if (type === 'line' || type === 'bar' || type === 'area' || type === 'point') {
|
|
|
|
data.element.attr({
|
|
|
|
'clip-path': 'url(#zoom-mask)'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
chart.on('created', function (data) {
|
|
|
|
axisX = data.axisX;
|
|
|
|
axisY = data.axisY;
|
|
|
|
chartRect = data.chartRect;
|
|
|
|
svg = data.svg._node;
|
|
|
|
rect = data.svg.elem('rect', {
|
|
|
|
x: 10,
|
|
|
|
y: 10,
|
|
|
|
width: 100,
|
|
|
|
height: 100,
|
|
|
|
}, 'ct-zoom-rect');
|
|
|
|
hide(rect);
|
|
|
|
|
|
|
|
var defs = data.svg.querySelector('defs') || data.svg.elem('defs');
|
|
|
|
var width = chartRect.width();
|
|
|
|
var height = chartRect.height();
|
|
|
|
|
|
|
|
defs
|
|
|
|
.elem('clipPath', {
|
|
|
|
id: 'zoom-mask'
|
|
|
|
})
|
|
|
|
.elem('rect', {
|
|
|
|
x: chartRect.x1,
|
|
|
|
y: chartRect.y2,
|
|
|
|
width: width,
|
|
|
|
height: height,
|
|
|
|
fill: 'white'
|
|
|
|
});
|
|
|
|
|
|
|
|
svg.addEventListener('mousedown', onMouseDown);
|
|
|
|
svg.addEventListener('mouseup', onMouseUp);
|
|
|
|
svg.addEventListener('mousemove', onMouseMove);
|
|
|
|
svg.addEventListener('touchstart', onTouchStart);
|
|
|
|
svg.addEventListener('touchmove', onTouchMove);
|
|
|
|
svg.addEventListener('touchend', onTouchEnd);
|
|
|
|
svg.addEventListener('touchcancel', onTouchCancel);
|
|
|
|
});
|
|
|
|
|
|
|
|
function copyTouch(touch) {
|
|
|
|
var p = position(touch, svg);
|
|
|
|
p.id = touch.identifier;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
function ongoingTouchIndexById(idToFind) {
|
|
|
|
for (var i = 0; i < ongoingTouches.length; i++) {
|
|
|
|
var id = ongoingTouches[i].id;
|
|
|
|
if (id === idToFind) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
function onTouchStart(event) {
|
|
|
|
var touches = event.changedTouches;
|
|
|
|
for (var i = 0; i < touches.length; i++) {
|
|
|
|
ongoingTouches.push(copyTouch(touches[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ongoingTouches.length > 1) {
|
|
|
|
rect.attr(getRect(ongoingTouches[0], ongoingTouches[1]));
|
|
|
|
show(rect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function onTouchMove(event) {
|
|
|
|
var touches = event.changedTouches;
|
|
|
|
for (var i = 0; i < touches.length; i++) {
|
|
|
|
var idx = ongoingTouchIndexById(touches[i].identifier);
|
|
|
|
ongoingTouches.splice(idx, 1, copyTouch(touches[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ongoingTouches.length > 1) {
|
|
|
|
rect.attr(getRect(ongoingTouches[0], ongoingTouches[1]));
|
|
|
|
show(rect);
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function onTouchCancel(event) {
|
|
|
|
removeTouches(event.changedTouches);
|
|
|
|
}
|
|
|
|
|
|
|
|
function removeTouches(touches) {
|
|
|
|
for (var i = 0; i < touches.length; i++) {
|
|
|
|
var idx = ongoingTouchIndexById(touches[i].identifier);
|
|
|
|
if (idx >= 0) {
|
|
|
|
ongoingTouches.splice(idx, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function onTouchEnd(event) {
|
|
|
|
if (ongoingTouches.length > 1) {
|
|
|
|
zoomIn(getRect(ongoingTouches[0], ongoingTouches[1]));
|
|
|
|
}
|
|
|
|
removeTouches(event.changedTouches);
|
|
|
|
hide(rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
function onMouseDown(event) {
|
|
|
|
if (event.button === 0) {
|
|
|
|
downPosition = position(event, svg);
|
|
|
|
rect.attr(getRect(downPosition, downPosition));
|
|
|
|
show(rect);
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var reset = function () {
|
|
|
|
chart.options.axisX.highLow = null;
|
|
|
|
chart.options.axisY.highLow = null;
|
|
|
|
chart.update(chart.data, chart.options);
|
|
|
|
};
|
|
|
|
|
|
|
|
function onMouseUp(event) {
|
|
|
|
if (event.button === 0) {
|
|
|
|
var box = getRect(downPosition, position(event, svg));
|
|
|
|
zoomIn(box);
|
|
|
|
downPosition = null;
|
|
|
|
hide(rect);
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
else if (options.resetOnRightMouseBtn && event.button === 2) {
|
|
|
|
reset();
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function zoomIn(rect) {
|
|
|
|
if (rect.width > 5 && rect.height > 5) {
|
|
|
|
var x1 = rect.x - chartRect.x1;
|
|
|
|
var x2 = x1 + rect.width;
|
|
|
|
var y2 = chartRect.y1 - rect.y;
|
|
|
|
var y1 = y2 - rect.height;
|
|
|
|
|
|
|
|
var xLow = project(x1, axisX);
|
|
|
|
var xHigh = project(x2, axisX);
|
|
|
|
var yLow = project(y1, axisY);
|
|
|
|
var yHigh = project(y2, axisY);
|
|
|
|
|
|
|
|
var explb = chart.options.explicitBounds;
|
|
|
|
if (!_.isUndefined(explb)) {
|
|
|
|
if (!_.isUndefined(explb.xLow)) xLow = Math.max(explb.xLow, xLow);
|
|
|
|
if (!_.isUndefined(explb.xHigh)) xHigh = Math.min(explb.xHigh, xHigh);
|
|
|
|
if (!_.isUndefined(explb.yLow)) yLow = Math.max(explb.yLow, yLow);
|
|
|
|
if (!_.isUndefined(explb.yHigh)) yHigh = Math.min(explb.yHigh, yHigh);
|
|
|
|
}
|
|
|
|
|
|
|
|
chart.options.axisX.highLow = {low: xLow, high: xHigh};
|
|
|
|
chart.options.axisY.highLow = {low: yLow, high: yHigh};
|
|
|
|
|
|
|
|
chart.update(chart.data, chart.options);
|
|
|
|
onZoom && onZoom(chart, reset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function onMouseMove(event) {
|
|
|
|
if (downPosition) {
|
|
|
|
var point = position(event, svg);
|
|
|
|
rect.attr(getRect(downPosition, point));
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
function hide(rect) {
|
|
|
|
rect.attr({style: 'display:none'});
|
|
|
|
}
|
|
|
|
|
|
|
|
function show(rect) {
|
|
|
|
rect.attr({style: 'display:block'});
|
|
|
|
}
|
|
|
|
|
|
|
|
function getRect(firstPoint, secondPoint) {
|
|
|
|
var x = firstPoint.x;
|
|
|
|
var y = firstPoint.y;
|
|
|
|
var width = secondPoint.x - x;
|
|
|
|
var height = secondPoint.y - y;
|
|
|
|
if (width < 0) {
|
|
|
|
width = -width;
|
|
|
|
x = secondPoint.x;
|
|
|
|
}
|
|
|
|
if (height < 0) {
|
|
|
|
height = -height;
|
|
|
|
y = secondPoint.y;
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
x: x,
|
|
|
|
y: y,
|
|
|
|
width: width,
|
|
|
|
height: height
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function position(event, svg) {
|
|
|
|
return transform(event.clientX, event.clientY, svg);
|
|
|
|
}
|
|
|
|
|
|
|
|
function transform(x, y, svgElement) {
|
|
|
|
var svg = svgElement.tagName === 'svg' ? svgElement : svgElement.ownerSVGElement;
|
|
|
|
var matrix = svg.getScreenCTM();
|
|
|
|
var point = svg.createSVGPoint();
|
|
|
|
point.x = x;
|
|
|
|
point.y = y;
|
|
|
|
point = point.matrixTransform(matrix.inverse());
|
|
|
|
return point || {x: 0, y: 0};
|
|
|
|
}
|
|
|
|
|
|
|
|
function project(value, axis) {
|
|
|
|
var max = axis.bounds.max;
|
|
|
|
var min = axis.bounds.min;
|
|
|
|
if (axis.scale && axis.scale.type === 'log') {
|
|
|
|
var base = axis.scale.base;
|
|
|
|
return Math.pow(base,
|
|
|
|
value * baseLog(max / min, base) / axis.axisLength) * min;
|
|
|
|
}
|
|
|
|
return (value * axis.bounds.range / axis.axisLength) + min;
|
|
|
|
}
|
|
|
|
|
|
|
|
function baseLog(val, base) {
|
|
|
|
return Math.log(val) / Math.log(base);
|
|
|
|
}
|
|
|
|
|
|
|
|
}(window, document, Chartist));
|
|
|
|
return Chartist.plugins.zoom;
|
|
|
|
|
|
|
|
}));
|