添加项目文件。

This commit is contained in:
jl
2023-01-13 15:30:20 +08:00
parent 40ed216831
commit bf208bde56
834 changed files with 470902 additions and 0 deletions

View File

@ -0,0 +1,84 @@
'use strict';
import H from './../../parts/Globals.js';
import './../../parts/Utilities.js';
import controllableMixin from './controllableMixin.js';
import ControllablePath from './ControllablePath.js';
/**
* A controllable circle class.
*
* @constructor
* @mixes Annotation.controllableMixin
* @memberOf Annotation
*
* @param {Highcharts.Annotation} annotation an annotation instance
* @param {Object} options a shape's options
**/
function ControllableCircle(annotation, options) {
this.init(annotation, options);
}
/**
* A map object which allows to map options attributes to element attributes.
*/
ControllableCircle.attrsMap = H.merge(ControllablePath.attrsMap, {
r: 'r'
});
H.merge(
true,
ControllableCircle.prototype,
controllableMixin, /** @lends Annotation.ControllableCircle# */ {
/**
* @type 'circle'
*/
type: 'circle',
render: function (parent) {
var attrs = this.attrsFromOptions(this.options);
this.graphic = this.annotation.chart.renderer
.circle(0, -9e9, 0)
.attr(attrs)
.add(parent);
controllableMixin.render.call(this);
},
redraw: function (animation) {
var position = this.anchor(this.points[0]).absolutePosition;
if (position) {
this.graphic[animation ? 'animate' : 'attr']({
x: position.x,
y: position.y,
r: this.options.r
});
} else {
this.graphic.attr({
x: 0,
y: -9e9
});
}
this.graphic.placed = Boolean(position);
controllableMixin.redraw.call(this, animation);
},
translate: function (dx, dy) {
this.translatePoint(dx, dy, 0);
},
/**
* Set the radius.
*
* @param {number} r a radius to be set
*/
setRadius: function (r) {
this.options.r = r;
}
}
);
export default ControllableCircle;

View File

@ -0,0 +1,92 @@
'use strict';
import H from './../../parts/Globals.js';
import './../../parts/Utilities.js';
import controllableMixin from './controllableMixin.js';
import ControllableLabel from './ControllableLabel.js';
/**
* A controllable image class.
*
* @class
* @mixes Annotation.controllableMixin
* @memberOf Annotation
*
* @param {Highcharts.Annotation} annotation - an annotation instance
* @param {Object} options a controllable's options
**/
function ControllableImage(annotation, options) {
this.init(annotation, options);
}
/**
* @typedef {Object} Annotation.ControllableImage.AttrsMap
* @property {string} width=width
* @property {string} height=height
* @property {string} zIndex=zIndex
*/
/**
* A map object which allows to map options attributes to element attributes
*
* @type {Annotation.ControllableImage.AttrsMap}
*/
ControllableImage.attrsMap = {
width: 'width',
height: 'height',
zIndex: 'zIndex'
};
H.merge(
true,
ControllableImage.prototype,
controllableMixin, /** @lends Annotation.ControllableImage# */ {
/**
* @type 'image'
*/
type: 'image',
render: function (parent) {
var attrs = this.attrsFromOptions(this.options),
options = this.options;
this.graphic = this.annotation.chart.renderer
.image(options.src, 0, -9e9, options.width, options.height)
.attr(attrs)
.add(parent);
this.graphic.width = options.width;
this.graphic.height = options.height;
controllableMixin.render.call(this);
},
redraw: function (animation) {
var anchor = this.anchor(this.points[0]),
position = ControllableLabel.prototype.position.call(
this,
anchor
);
if (position) {
this.graphic[animation ? 'animate' : 'attr']({
x: position.x,
y: position.y
});
} else {
this.graphic.attr({
x: 0,
y: -9e9
});
}
this.graphic.placed = Boolean(position);
controllableMixin.redraw.call(this, animation);
},
translate: function (dx, dy) {
this.translatePoint(dx, dy, 0);
}
});
export default ControllableImage;

View File

@ -0,0 +1,441 @@
'use strict';
import H from './../../parts/Globals.js';
import './../../parts/Utilities.js';
import './../../parts/SvgRenderer.js';
import controllableMixin from './controllableMixin.js';
import MockPoint from './../MockPoint.js';
/**
* A controllable label class.
*
* @class
* @mixes Annotation.controllableMixin
* @memberOf Annotation
*
* @param {Highcharts.Annotation} annotation an annotation instance
* @param {Object} options a label's options
**/
function ControllableLabel(annotation, options) {
this.init(annotation, options);
}
/**
* Shapes which do not have background - the object is used for proper
* setting of the contrast color.
*
* @type {Array<String>}
*/
ControllableLabel.shapesWithoutBackground = ['connector'];
/**
* Returns new aligned position based alignment options and box to align to.
* It is almost a one-to-one copy from SVGElement.prototype.align
* except it does not use and mutate an element
*
* @param {Object} alignOptions
* @param {Object} box
* @return {Annotation.controllableMixin.Position} aligned position
*/
ControllableLabel.alignedPosition = function (alignOptions, box) {
var align = alignOptions.align,
vAlign = alignOptions.verticalAlign,
x = (box.x || 0) + (alignOptions.x || 0),
y = (box.y || 0) + (alignOptions.y || 0),
alignFactor,
vAlignFactor;
if (align === 'right') {
alignFactor = 1;
} else if (align === 'center') {
alignFactor = 2;
}
if (alignFactor) {
x += (box.width - (alignOptions.width || 0)) / alignFactor;
}
if (vAlign === 'bottom') {
vAlignFactor = 1;
} else if (vAlign === 'middle') {
vAlignFactor = 2;
}
if (vAlignFactor) {
y += (box.height - (alignOptions.height || 0)) / vAlignFactor;
}
return {
x: Math.round(x),
y: Math.round(y)
};
};
/**
* Returns new alignment options for a label if the label is outside the
* plot area. It is almost a one-to-one copy from
* Series.prototype.justifyDataLabel except it does not mutate the label and
* it works with absolute instead of relative position.
*
* @param {Object} label
* @param {Object} alignOptions
* @param {Object} alignAttr
* @return {Object} justified options
**/
ControllableLabel.justifiedOptions = function (
chart,
label,
alignOptions,
alignAttr
) {
var align = alignOptions.align,
verticalAlign = alignOptions.verticalAlign,
padding = label.box ? 0 : (label.padding || 0),
bBox = label.getBBox(),
off,
options = {
align: align,
verticalAlign: verticalAlign,
x: alignOptions.x,
y: alignOptions.y,
width: label.width,
height: label.height
},
x = alignAttr.x - chart.plotLeft,
y = alignAttr.y - chart.plotTop;
// Off left
off = x + padding;
if (off < 0) {
if (align === 'right') {
options.align = 'left';
} else {
options.x = -off;
}
}
// Off right
off = x + bBox.width - padding;
if (off > chart.plotWidth) {
if (align === 'left') {
options.align = 'right';
} else {
options.x = chart.plotWidth - off;
}
}
// Off top
off = y + padding;
if (off < 0) {
if (verticalAlign === 'bottom') {
options.verticalAlign = 'top';
} else {
options.y = -off;
}
}
// Off bottom
off = y + bBox.height - padding;
if (off > chart.plotHeight) {
if (verticalAlign === 'top') {
options.verticalAlign = 'bottom';
} else {
options.y = chart.plotHeight - off;
}
}
return options;
};
/**
* @typedef {Object} Annotation.ControllableLabel.AttrsMap
* @property {string} backgroundColor=fill
* @property {string} borderColor=stroke
* @property {string} borderWidth=stroke-width
* @property {string} zIndex=zIndex
* @property {string} borderRadius=r
* @property {string} padding=padding
*/
/**
* A map object which allows to map options attributes to element attributes
*
* @type {Annotation.ControllableLabel.AttrsMap}
*/
ControllableLabel.attrsMap = {
backgroundColor: 'fill',
borderColor: 'stroke',
borderWidth: 'stroke-width',
zIndex: 'zIndex',
borderRadius: 'r',
padding: 'padding'
};
H.merge(
true,
ControllableLabel.prototype,
controllableMixin, /** @lends Annotation.ControllableLabel# */ {
/**
* Translate the point of the label by deltaX and deltaY translations.
* The point is the label's anchor.
*
* @param {number} dx translation for x coordinate
* @param {number} dy translation for y coordinate
**/
translatePoint: function (dx, dy) {
controllableMixin.translatePoint.call(this, dx, dy, 0);
},
/**
* Translate x and y position relative to the label's anchor.
*
* @param {number} dx translation for x coordinate
* @param {number} dy translation for y coordinate
**/
translate: function (dx, dy) {
this.options.x += dx;
this.options.y += dy;
},
render: function (parent) {
var options = this.options,
attrs = this.attrsFromOptions(options),
style = options.style;
this.graphic = this.annotation.chart.renderer
.label(
'',
0,
-9e9,
options.shape,
null,
null,
options.useHTML,
null,
'annotation-label'
)
.attr(attrs)
.add(parent);
if (!this.annotation.chart.styledMode) {
if (style.color === 'contrast') {
style.color = this.annotation.chart.renderer.getContrast(
ControllableLabel.shapesWithoutBackground.indexOf(
options.shape
) > -1 ? '#FFFFFF' : options.backgroundColor
);
}
this.graphic
.css(options.style)
.shadow(options.shadow);
}
if (options.className) {
this.graphic.addClass(options.className);
}
this.graphic.labelrank = options.labelrank;
controllableMixin.render.call(this);
},
redraw: function (animation) {
var options = this.options,
text = this.text || options.format || options.text,
label = this.graphic,
point = this.points[0],
show = false,
anchor,
attrs;
label.attr({
text: text ?
H.format(
text,
point.getLabelConfig(),
this.annotation.chart.time
) :
options.formatter.call(point, this)
});
anchor = this.anchor(point);
attrs = this.position(anchor);
show = attrs;
if (show) {
label.alignAttr = attrs;
attrs.anchorX = anchor.absolutePosition.x;
attrs.anchorY = anchor.absolutePosition.y;
label[animation ? 'animate' : 'attr'](attrs);
} else {
label.attr({
x: 0,
y: -9e9
});
}
label.placed = Boolean(show);
controllableMixin.redraw.call(this, animation);
},
/**
* All basic shapes don't support alignTo() method except label.
* For a controllable label, we need to subtract translation from
* options.
*/
anchor: function () {
var anchor = controllableMixin.anchor.apply(this, arguments),
x = this.options.x || 0,
y = this.options.y || 0;
anchor.absolutePosition.x -= x;
anchor.absolutePosition.y -= y;
anchor.relativePosition.x -= x;
anchor.relativePosition.y -= y;
return anchor;
},
/**
* Returns the label position relative to its anchor.
*
* @param {Annotation.controllableMixin.Anchor} anchor
* @return {Annotation.controllableMixin.Position|null} position
*/
position: function (anchor) {
var item = this.graphic,
chart = this.annotation.chart,
point = this.points[0],
itemOptions = this.options,
anchorAbsolutePosition = anchor.absolutePosition,
anchorRelativePosition = anchor.relativePosition,
itemPosition,
alignTo,
itemPosRelativeX,
itemPosRelativeY,
showItem =
point.series.visible &&
MockPoint.prototype.isInsidePane.call(point);
if (showItem) {
if (itemOptions.distance) {
itemPosition = H.Tooltip.prototype.getPosition.call(
{
chart: chart,
distance: H.pick(itemOptions.distance, 16)
},
item.width,
item.height,
{
plotX: anchorRelativePosition.x,
plotY: anchorRelativePosition.y,
negative: point.negative,
ttBelow: point.ttBelow,
h: anchorRelativePosition.height ||
anchorRelativePosition.width
}
);
} else if (itemOptions.positioner) {
itemPosition = itemOptions.positioner.call(this);
} else {
alignTo = {
x: anchorAbsolutePosition.x,
y: anchorAbsolutePosition.y,
width: 0,
height: 0
};
itemPosition = ControllableLabel.alignedPosition(
H.extend(itemOptions, {
width: item.width,
height: item.height
}),
alignTo
);
if (this.options.overflow === 'justify') {
itemPosition = ControllableLabel.alignedPosition(
ControllableLabel.justifiedOptions(
chart,
item,
itemOptions,
itemPosition
),
alignTo
);
}
}
if (itemOptions.crop) {
itemPosRelativeX = itemPosition.x - chart.plotLeft;
itemPosRelativeY = itemPosition.y - chart.plotTop;
showItem =
chart.isInsidePlot(
itemPosRelativeX,
itemPosRelativeY
) &&
chart.isInsidePlot(
itemPosRelativeX + item.width,
itemPosRelativeY + item.height
);
}
}
return showItem ? itemPosition : null;
}
});
/* ********************************************************************** */
/**
* General symbol definition for labels with connector
*/
H.SVGRenderer.prototype.symbols.connector = function (x, y, w, h, options) {
var anchorX = options && options.anchorX,
anchorY = options && options.anchorY,
path,
yOffset,
lateral = w / 2;
if (H.isNumber(anchorX) && H.isNumber(anchorY)) {
path = ['M', anchorX, anchorY];
// Prefer 45 deg connectors
yOffset = y - anchorY;
if (yOffset < 0) {
yOffset = -h - yOffset;
}
if (yOffset < w) {
lateral = anchorX < x + (w / 2) ? yOffset : w - yOffset;
}
// Anchor below label
if (anchorY > y + h) {
path.push('L', x + lateral, y + h);
// Anchor above label
} else if (anchorY < y) {
path.push('L', x + lateral, y);
// Anchor left of label
} else if (anchorX < x) {
path.push('L', x, y + h / 2);
// Anchor right of label
} else if (anchorX > x + w) {
path.push('L', x + w, y + h / 2);
}
}
return path || [];
};
export default ControllableLabel;

View File

@ -0,0 +1,173 @@
'use strict';
import H from './../../parts/Globals.js';
import './../../parts/Utilities.js';
import controllableMixin from './controllableMixin.js';
import markerMixin from './markerMixin.js';
// See TRACKER_FILL in highcharts.src.js
var TRACKER_FILL = 'rgba(192,192,192,' + (H.svg ? 0.0001 : 0.002) + ')';
/**
* A controllable path class.
*
* @class
* @mixes Annotation.controllableMixin
* @mixes Annotation.markerMixin
* @memberOf Annotation
*
* @param {Highcharts.Annotation}
* @param {Object} options a path's options object
**/
function ControllablePath(annotation, options) {
this.init(annotation, options);
}
/**
* @typedef {Object} Annotation.ControllablePath.AttrsMap
* @property {string} dashStyle=dashstyle
* @property {string} strokeWidth=stroke-width
* @property {string} stroke=stroke
* @property {string} fill=fill
* @property {string} zIndex=zIndex
*/
/**
* A map object which allows to map options attributes to element attributes
*
* @type {Annotation.ControllablePath.AttrsMap}
*/
ControllablePath.attrsMap = {
dashStyle: 'dashstyle',
strokeWidth: 'stroke-width',
stroke: 'stroke',
fill: 'fill',
zIndex: 'zIndex'
};
H.merge(
true,
ControllablePath.prototype,
controllableMixin, /** @lends Annotation.ControllablePath# */ {
/**
* @type 'path'
*/
type: 'path',
setMarkers: markerMixin.setItemMarkers,
/**
* Map the controllable path to 'd' path attribute
*
* @return {Array<(string|number)>} a path's d attribute
*/
toD: function () {
var d = this.options.d;
if (d) {
return typeof d === 'function' ?
d.call(this) :
d;
}
var points = this.points,
len = points.length,
showPath = len,
point = points[0],
position = showPath && this.anchor(point).absolutePosition,
pointIndex = 0,
dIndex = 2,
command;
d = position && ['M', position.x, position.y];
while (++pointIndex < len && showPath) {
point = points[pointIndex];
command = point.command || 'L';
position = this.anchor(point).absolutePosition;
if (command === 'Z') {
d[++dIndex] = command;
} else {
if (command !== points[pointIndex - 1].command) {
d[++dIndex] = command;
}
d[++dIndex] = position.x;
d[++dIndex] = position.y;
}
showPath = point.series.visible;
}
return showPath ? this.chart.renderer.crispLine(
d, this.graphic.strokeWidth()
) : null;
},
shouldBeDrawn: function () {
return controllableMixin.shouldBeDrawn.call(this) ||
Boolean(this.options.d);
},
render: function (parent) {
var options = this.options,
attrs = this.attrsFromOptions(options);
this.graphic = this.annotation.chart.renderer
.path(['M', 0, 0])
.attr(attrs)
.add(parent);
if (options.className) {
this.graphic.addClass(options.className);
}
this.tracker = this.annotation.chart.renderer
.path(['M', 0, 0])
.addClass('highcharts-tracker-line')
.attr({
zIndex: 2
})
.add(parent);
if (!this.annotation.chart.styledMode) {
this.tracker.attr({
'stroke-linejoin': 'round', // #1225
stroke: TRACKER_FILL,
fill: TRACKER_FILL,
'stroke-width': this.graphic.strokeWidth() +
options.snap * 2
});
}
controllableMixin.render.call(this);
H.extend(this.graphic, {
markerStartSetter: markerMixin.markerStartSetter,
markerEndSetter: markerMixin.markerEndSetter
});
this.setMarkers(this);
},
redraw: function (animation) {
var d = this.toD(),
action = animation ? 'animate' : 'attr';
if (d) {
this.graphic[action]({ d: d });
this.tracker[action]({ d: d });
} else {
this.graphic.attr({ d: 'M 0 ' + -9e9 });
this.tracker.attr({ d: 'M 0 ' + -9e9 });
}
this.graphic.placed = this.tracker.placed = Boolean(d);
controllableMixin.redraw.call(this, animation);
}
}
);
export default ControllablePath;

View File

@ -0,0 +1,85 @@
import H from '../../parts/Globals.js';
import '../../parts/Utilities.js';
import controllableMixin from './controllableMixin.js';
import ControllablePath from './ControllablePath.js';
/**
* A controllable rect class.
*
* @class
* @mixes Annotation.controllableMixin
* @memberOf Annotation
*
* @param {Highcharts.Annotation} annotation an annotation instance
* @param {Object} options a rect's options
**/
function ControllableRect(annotation, options) {
this.init(annotation, options);
}
/**
* @typedef {Annotation.ControllablePath.AttrsMap}
* Annotation.ControllableRect.AttrsMap
* @property {string} width=width
* @property {string} height=height
*/
/**
* A map object which allows to map options attributes to element attributes
*
* @type {Annotation.ControllableRect.AttrsMap}
*/
ControllableRect.attrsMap = H.merge(ControllablePath.attrsMap, {
width: 'width',
height: 'height'
});
H.merge(
true,
ControllableRect.prototype,
controllableMixin, /** @lends Annotation.ControllableRect# */ {
/**
* @type 'rect'
*/
type: 'rect',
render: function (parent) {
var attrs = this.attrsFromOptions(this.options);
this.graphic = this.annotation.chart.renderer
.rect(0, -9e9, 0, 0)
.attr(attrs)
.add(parent);
controllableMixin.render.call(this);
},
redraw: function (animation) {
var position = this.anchor(this.points[0]).absolutePosition;
if (position) {
this.graphic[animation ? 'animate' : 'attr']({
x: position.x,
y: position.y,
width: this.options.width,
height: this.options.height
});
} else {
this.attr({
x: 0,
y: -9e9
});
}
this.graphic.placed = Boolean(position);
controllableMixin.redraw.call(this, animation);
},
translate: function (dx, dy) {
this.translatePoint(dx, dy, 0);
}
}
);
export default ControllableRect;

View File

@ -0,0 +1,408 @@
'use strict';
import H from './../../parts/Globals.js';
import './../../parts/Utilities.js';
import './../../parts/Tooltip.js';
import ControlPoint from './../ControlPoint.js';
import MockPoint from './../MockPoint.js';
/**
* It provides methods for handling points, control points
* and points transformations.
*
* @mixin
* @memberOf Annotation
*/
var controllableMixin = {
/**
* Init the controllable
*
* @param {Annotation} annotation - an annotation instance
* @param {Object} options - options specific for controllable
**/
init: function (annotation, options) {
this.annotation = annotation;
this.chart = annotation.chart;
this.options = options;
this.points = [];
this.controlPoints = [];
this.linkPoints();
this.addControlPoints();
},
/**
* Redirect attr usage on the controllable graphic element.
**/
attr: function () {
this.graphic.attr.apply(this.graphic, arguments);
},
/**
* Get the controllable's points options.
*
* @return {Array<PointLikeOptions>} - an array of points' options.
*
*/
getPointsOptions: function () {
var options = this.options;
return options.points || (options.point && H.splat(options.point));
},
/**
* Utility function for mapping item's options
* to element's attribute
*
* @param {Object} options
* @return {Object} mapped options
**/
attrsFromOptions: function (options) {
var map = this.constructor.attrsMap,
attrs = {},
key,
mappedKey,
styledMode = this.chart.styledMode;
for (key in options) {
mappedKey = map[key];
if (
mappedKey &&
(
!styledMode ||
['fill', 'stroke', 'stroke-width']
.indexOf(mappedKey) === -1
)
) {
attrs[mappedKey] = options[key];
}
}
return attrs;
},
/**
* @typedef {Object} Annotation.controllableMixin.Position
* @property {number} x
* @property {number} y
*/
/**
* An object which denotes an anchor position
*
* @typedef Annotation.controllableMixin.AnchorPosition
* Annotation.controllableMixin.Position
* @property {number} height
* @property {number} width
*/
/**
* An object which denots a controllable's anchor positions
* - relative and absolute.
*
* @typedef {Object} Annotation.controllableMixin.Anchor
* @property {Annotation.controllableMixin.AnchorPosition} relativePosition
* @property {Annotation.controllableMixin.AnchorPosition} absolutePosition
*/
/**
* Returns object which denotes anchor position - relative and absolute.
*
* @param {Annotation.PointLike} point a point like object
* @return {Annotation.controllableMixin.Anchor} a controllable anchor
*/
anchor: function (point) {
var plotBox = point.series.getPlotBox(),
box = point.mock ?
point.toAnchor() :
H.Tooltip.prototype.getAnchor.call({
chart: point.series.chart
}, point),
anchor = {
x: box[0] + (this.options.x || 0),
y: box[1] + (this.options.y || 0),
height: box[2] || 0,
width: box[3] || 0
};
return {
relativePosition: anchor,
absolutePosition: H.merge(anchor, {
x: anchor.x + plotBox.translateX,
y: anchor.y + plotBox.translateY
})
};
},
/**
* Map point's options to a point-like object.
*
* @param {Annotation.MockPoint.Options} pointOptions point's options
* @param {Annotation.PointLike} point a point like instance
* @return {Annotation.PointLike|null} if the point is
* found/set returns this point, otherwise null
*/
point: function (pointOptions, point) {
if (pointOptions && pointOptions.series) {
return pointOptions;
}
if (!point || point.series === null) {
if (H.isObject(pointOptions)) {
point = new MockPoint(
this.chart,
this,
pointOptions
);
} else if (H.isString(pointOptions)) {
point = this.chart.get(pointOptions) || null;
} else if (typeof pointOptions === 'function') {
var pointConfig = pointOptions.call(point, this);
point = pointConfig.series ?
pointConfig :
new MockPoint(
this.chart,
this,
pointOptions
);
}
}
return point;
},
/**
* Find point-like objects based on points options.
*
* @return {Array<Annotation.PointLike>} an array of point-like objects
*/
linkPoints: function () {
var pointsOptions = this.getPointsOptions(),
points = this.points,
len = (pointsOptions && pointsOptions.length) || 0,
i,
point;
for (i = 0; i < len; i++) {
point = this.point(pointsOptions[i], points[i]);
if (!point) {
points.length = 0;
return;
}
if (point.mock) {
point.refresh();
}
points[i] = point;
}
return points;
},
/**
* Add control points to a controllable.
*/
addControlPoints: function () {
var controlPointsOptions = this.options.controlPoints;
(controlPointsOptions || []).forEach(
function (controlPointOptions, i) {
var options = H.merge(
this.options.controlPointOptions,
controlPointOptions
);
if (!options.index) {
options.index = i;
}
controlPointsOptions[i] = options;
this.controlPoints.push(
new ControlPoint(this.chart, this, options)
);
},
this
);
},
/**
* Check if a controllable should be rendered/redrawn.
*
* @return {boolean} whether a controllable should be drawn.
*/
shouldBeDrawn: function () {
return Boolean(this.points.length);
},
/**
* Render a controllable.
**/
render: function () {
this.controlPoints.forEach(function (controlPoint) {
controlPoint.render();
});
},
/**
* Redraw a controllable.
*
* @param {boolean} animation
**/
redraw: function (animation) {
this.controlPoints.forEach(function (controlPoint) {
controlPoint.redraw(animation);
});
},
/**
* Transform a controllable with a specific transformation.
*
* @param {string} transformation a transformation name
* @param {number} cx origin x transformation
* @param {number} cy origin y transformation
* @param {number} p1 param for the transformation
* @param {number} p2 param for the transformation
**/
transform: function (transformation, cx, cy, p1, p2) {
if (this.chart.inverted) {
var temp = cx;
cx = cy;
cy = temp;
}
this.points.forEach(function (point, i) {
this.transformPoint(transformation, cx, cy, p1, p2, i);
}, this);
},
/**
* Transform a point with a specific transformation
* If a transformed point is a real point it is replaced with
* the mock point.
*
* @param {string} transformation a transformation name
* @param {number} cx origin x transformation
* @param {number} cy origin y transformation
* @param {number} p1 param for the transformation
* @param {number} p2 param for the transformation
* @param {number} i index of the point
*
**/
transformPoint: function (transformation, cx, cy, p1, p2, i) {
var point = this.points[i];
if (!point.mock) {
point = this.points[i] = MockPoint.fromPoint(point);
}
point[transformation](cx, cy, p1, p2);
},
/**
* Translate a controllable.
*
* @param {number} dx translation for x coordinate
* @param {number} dy translation for y coordinate
**/
translate: function (dx, dy) {
this.transform('translate', null, null, dx, dy);
},
/**
* Translate a specific point within a controllable.
*
* @param {number} dx translation for x coordinate
* @param {number} dy translation for y coordinate
* @param {number} i index of the point
**/
translatePoint: function (dx, dy, i) {
this.transformPoint('translate', null, null, dx, dy, i);
},
/**
* Rotate a controllable.
*
* @param {number} cx origin x rotation
* @param {number} cy origin y rotation
* @param {number} radians
**/
rotate: function (cx, cy, radians) {
this.transform('rotate', cx, cy, radians);
},
/**
* Scale a controllable.
*
* @param {number} cx origin x rotation
* @param {number} cy origin y rotation
* @param {number} sx scale factor x
* @param {number} sy scale factor y
*/
scale: function (cx, cy, sx, sy) {
this.transform('scale', cx, cy, sx, sy);
},
/**
* Set control points' visibility.
*
* @param {boolean} [visible]
*/
setControlPointsVisibility: function (visible) {
this.controlPoints.forEach(function (controlPoint) {
controlPoint.setVisibility(visible);
});
},
/**
* Destroy a controllable.
*/
destroy: function () {
if (this.graphic) {
this.graphic = this.graphic.destroy();
}
if (this.tracker) {
this.tracker = this.tracker.destroy();
}
this.controlPoints.forEach(function (controlPoint) {
controlPoint.destroy();
});
this.chart = null;
this.points = null;
this.controlPoints = null;
this.options = null;
if (this.annotation) {
this.annotation = null;
}
},
/**
* Update a controllable.
*
* @param {Object} newOptions
*/
update: function (newOptions) {
var annotation = this.annotation,
options = H.merge(true, this.options, newOptions),
parentGroup = this.graphic.parentGroup;
this.destroy();
this.constructor(annotation, options);
this.render(parentGroup);
this.redraw();
}
};
export default controllableMixin;

View File

@ -0,0 +1,213 @@
'use strict';
import H from './../../parts/Globals.js';
import './../../parts/Chart.js';
import './../../parts/Utilities.js';
import './../../parts/SvgRenderer.js';
/**
* Options for configuring markers for annotations.
*
* An example of the arrow marker:
* <pre>
* {
* arrow: {
* id: 'arrow',
* tagName: 'marker',
* refY: 5,
* refX: 5,
* markerWidth: 10,
* markerHeight: 10,
* children: [{
* tagName: 'path',
* attrs: {
* d: 'M 0 0 L 10 5 L 0 10 Z',
* strokeWidth: 0
* }
* }]
* }
* }
* </pre>
* @type {Object}
* @sample highcharts/annotations/custom-markers/
* Define a custom marker for annotations
* @sample highcharts/css/annotations-markers/
* Define markers in a styled mode
* @since 6.0.0
* @apioption defs
*/
var defaultMarkers = {
arrow: {
tagName: 'marker',
render: false,
id: 'arrow',
refY: 5,
refX: 9,
markerWidth: 10,
markerHeight: 10,
children: [{
tagName: 'path',
d: 'M 0 0 L 10 5 L 0 10 Z', // triangle (used as an arrow)
strokeWidth: 0
}]
},
'reverse-arrow': {
tagName: 'marker',
render: false,
id: 'reverse-arrow',
refY: 5,
refX: 1,
markerWidth: 10,
markerHeight: 10,
children: [{
tagName: 'path',
// reverse triangle (used as an arrow)
d: 'M 0 5 L 10 0 L 10 10 Z',
strokeWidth: 0
}]
}
};
H.SVGRenderer.prototype.addMarker = function (id, markerOptions) {
var options = { id: id };
var attrs = {
stroke: markerOptions.color || 'none',
fill: markerOptions.color || 'rgba(0, 0, 0, 0.75)'
};
options.children = markerOptions.children.map(function (child) {
return H.merge(attrs, child);
});
var marker = this.definition(H.merge(true, {
markerWidth: 20,
markerHeight: 20,
refX: 0,
refY: 0,
orient: 'auto'
}, markerOptions, options));
marker.id = id;
return marker;
};
var createMarkerSetter = function (markerType) {
return function (value) {
this.attr(markerType, 'url(#' + value + ')');
};
};
/**
* @mixin
*/
var markerMixin = {
markerEndSetter: createMarkerSetter('marker-end'),
markerStartSetter: createMarkerSetter('marker-start'),
/*
* Set markers.
*
* @param {Controllable} item
*/
setItemMarkers: function (item) {
var itemOptions = item.options,
chart = item.chart,
defs = chart.options.defs,
fill = itemOptions.fill,
color = H.defined(fill) && fill !== 'none' ?
fill :
itemOptions.stroke,
setMarker = function (markerType) {
var markerId = itemOptions[markerType],
def,
predefinedMarker,
key,
marker;
if (markerId) {
for (key in defs) {
def = defs[key];
if (
markerId === def.id && def.tagName === 'marker'
) {
predefinedMarker = def;
break;
}
}
if (predefinedMarker) {
marker = item[markerType] = chart.renderer
.addMarker(
(itemOptions.id || H.uniqueKey()) + '-' +
predefinedMarker.id,
H.merge(predefinedMarker, { color: color })
);
item.attr(markerType, marker.attr('id'));
}
}
};
['markerStart', 'markerEnd'].forEach(setMarker);
}
};
// In a styled mode definition is implemented
H.SVGRenderer.prototype.definition = function (def) {
var ren = this;
function recurse(config, parent) {
var ret;
H.splat(config).forEach(function (item) {
var node = ren.createElement(item.tagName),
attr = {};
// Set attributes
H.objectEach(item, function (val, key) {
if (
key !== 'tagName' &&
key !== 'children' &&
key !== 'textContent'
) {
attr[key] = val;
}
});
node.attr(attr);
// Add to the tree
node.add(parent || ren.defs);
// Add text content
if (item.textContent) {
node.element.appendChild(
H.doc.createTextNode(item.textContent)
);
}
// Recurse
recurse(item.children || [], node);
ret = node;
});
// Return last node added (on top level it's the only one)
return ret;
}
return recurse(def);
};
H.addEvent(H.Chart, 'afterGetContainer', function () {
this.options.defs = H.merge(defaultMarkers, this.options.defs || {});
H.objectEach(this.options.defs, function (def) {
if (def.tagName === 'marker' && def.render !== false) {
this.renderer.addMarker(def.id, def);
}
}, this);
});
export default markerMixin;