-
Notifications
You must be signed in to change notification settings - Fork 484
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor label model and logic in src/label.js
- Loading branch information
1 parent
028840c
commit aa97bbf
Showing
2 changed files
with
265 additions
and
224 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
'use strict'; | ||
|
||
import Chart from 'chart.js'; | ||
import utils from './utils'; | ||
import positioners from './positioners'; | ||
|
||
var helpers = Chart.helpers; | ||
|
||
function boundingRects(size, padding) { | ||
var th = size.height; | ||
var tw = size.width; | ||
var tx = -tw / 2; | ||
var ty = -th / 2; | ||
|
||
return { | ||
frame: { | ||
x: tx - padding.left, | ||
y: ty - padding.top, | ||
w: tw + padding.width, | ||
h: th + padding.height, | ||
}, | ||
text: { | ||
x: tx, | ||
y: ty, | ||
w: tw, | ||
h: th | ||
} | ||
}; | ||
} | ||
|
||
function getScaleOrigin(el) { | ||
var horizontal = el._model.horizontal; | ||
var scale = el._scale || (horizontal && el._xScale) || el._yScale; | ||
|
||
if (!scale) { | ||
return null; | ||
} | ||
|
||
if (scale.xCenter !== undefined && scale.yCenter !== undefined) { | ||
return {x: scale.xCenter, y: scale.yCenter}; | ||
} | ||
|
||
var pixel = scale.getBasePixel(); | ||
return horizontal ? | ||
{x: pixel, y: null} : | ||
{x: null, y: pixel}; | ||
} | ||
|
||
function getPositioner(el) { | ||
if (el instanceof Chart.elements.Arc) { | ||
return positioners.arc; | ||
} | ||
if (el instanceof Chart.elements.Point) { | ||
return positioners.point; | ||
} | ||
if (el instanceof Chart.elements.Rectangle) { | ||
return positioners.rect; | ||
} | ||
return positioners.fallback; | ||
} | ||
|
||
function coordinates(el, model, rect) { | ||
var point = model.positioner(el._view, model.anchor, model.align, model.origin); | ||
var vx = point.vx; | ||
var vy = point.vy; | ||
|
||
if (!vx && !vy) { | ||
// if aligned center, we don't want to offset the center point | ||
return {x: point.x, y: point.y}; | ||
} | ||
|
||
// include borders to the bounding rect | ||
var borderWidth = model.borderWidth || 0; | ||
var w = (rect.w + borderWidth * 2); | ||
var h = (rect.h + borderWidth * 2); | ||
|
||
// take in account the label rotation | ||
var rotation = model.rotation; | ||
var dx = Math.abs(w / 2 * Math.cos(rotation)) + Math.abs(h / 2 * Math.sin(rotation)); | ||
var dy = Math.abs(w / 2 * Math.sin(rotation)) + Math.abs(h / 2 * Math.cos(rotation)); | ||
|
||
// scale the unit vector (vx, vy) to get at least dx or dy equal to w or h respectively | ||
// (else we would calculate the distance to the ellipse inscribed in the bounding rect) | ||
var vs = 1 / Math.max(Math.abs(vx), Math.abs(vy)); | ||
dx *= vx * vs; | ||
dy *= vy * vs; | ||
|
||
// finally, include the explicit offset | ||
dx += model.offset * vx; | ||
dy += model.offset * vy; | ||
|
||
return { | ||
x: point.x + dx, | ||
y: point.y + dy | ||
}; | ||
} | ||
|
||
function drawFrame(ctx, rect, model) { | ||
var bgColor = model.backgroundColor; | ||
var borderColor = model.borderColor; | ||
var borderWidth = model.borderWidth; | ||
|
||
if (!bgColor && (!borderColor || !borderWidth)) { | ||
return; | ||
} | ||
|
||
ctx.beginPath(); | ||
|
||
helpers.canvas.roundedRect( | ||
ctx, | ||
Math.round(rect.x) - borderWidth / 2, | ||
Math.round(rect.y) - borderWidth / 2, | ||
Math.round(rect.w) + borderWidth, | ||
Math.round(rect.h) + borderWidth, | ||
model.borderRadius); | ||
|
||
ctx.closePath(); | ||
|
||
if (bgColor) { | ||
ctx.fillStyle = bgColor; | ||
ctx.fill(); | ||
} | ||
|
||
if (borderColor && borderWidth) { | ||
ctx.strokeStyle = borderColor; | ||
ctx.lineWidth = borderWidth; | ||
ctx.lineJoin = 'miter'; | ||
ctx.stroke(); | ||
} | ||
} | ||
|
||
function drawText(ctx, lines, rect, model) { | ||
var align = model.textAlign; | ||
var font = model.font; | ||
var lh = font.lineHeight; | ||
var color = model.color; | ||
var ilen = lines.length; | ||
var x, y, i; | ||
|
||
if (!ilen || !color) { | ||
return; | ||
} | ||
|
||
x = rect.x; | ||
y = rect.y + lh / 2; | ||
|
||
if (align === 'center') { | ||
x += rect.w / 2; | ||
} else if (align === 'end' || align === 'right') { | ||
x += rect.w; | ||
} | ||
|
||
ctx.font = model.font.string; | ||
ctx.fillStyle = color; | ||
ctx.textAlign = align; | ||
ctx.textBaseline = 'middle'; | ||
|
||
for (i = 0; i < ilen; ++i) { | ||
ctx.fillText( | ||
lines[i], | ||
Math.round(x), | ||
Math.round(y), | ||
Math.round(rect.w)); | ||
|
||
y += lh; | ||
} | ||
} | ||
|
||
var Label = function(el, index) { | ||
var me = this; | ||
|
||
me._el = el; | ||
me._index = index; | ||
me._model = null; | ||
}; | ||
|
||
helpers.extend(Label.prototype, { | ||
/** | ||
* @private | ||
*/ | ||
_modelize: function(ctx, lines, config, context) { | ||
var me = this; | ||
var index = me._index; | ||
var resolve = helpers.options.resolve; | ||
var font = utils.parseFont(resolve([config.font, {}], context, index)); | ||
|
||
return { | ||
align: resolve([config.align, 'center'], context, index), | ||
anchor: resolve([config.anchor, 'center'], context, index), | ||
backgroundColor: resolve([config.backgroundColor, null], context, index), | ||
borderColor: resolve([config.borderColor, null], context, index), | ||
borderRadius: resolve([config.borderRadius, 0], context, index), | ||
borderWidth: resolve([config.borderWidth, 0], context, index), | ||
color: resolve([config.color, Chart.defaults.global.defaultFontColor], context, index), | ||
font: font, | ||
lines: lines, | ||
offset: resolve([config.offset, 0], context, index), | ||
origin: getScaleOrigin(me._el), | ||
padding: helpers.options.toPadding(resolve([config.padding, 0], context, index)), | ||
positioner: getPositioner(me._el), | ||
rotation: resolve([config.rotation, 0], context, index) * (Math.PI / 180), | ||
size: utils.textSize(ctx, lines, font), | ||
textAlign: resolve([config.textAlign, 'start'], context, index) | ||
}; | ||
}, | ||
|
||
update: function(ctx, config, context) { | ||
var me = this; | ||
var model = null; | ||
var index = me._index; | ||
var value, label, lines; | ||
|
||
if (helpers.options.resolve([config.display, true], context, index)) { | ||
value = context.dataset.data[index]; | ||
label = helpers.valueOrDefault(helpers.callback(config.formatter, [value, context]), value); | ||
lines = helpers.isNullOrUndef(label) ? [] : utils.toTextLines(label); | ||
model = lines.length ? me._modelize(ctx, lines, config, context) : null; | ||
} | ||
|
||
me._model = model; | ||
}, | ||
|
||
draw: function(ctx) { | ||
var me = this; | ||
var model = me._model; | ||
var rects, center; | ||
|
||
if (!model) { | ||
return; | ||
} | ||
|
||
rects = boundingRects(model.size, model.padding); | ||
center = coordinates(me._el, model, rects.frame); | ||
|
||
ctx.save(); | ||
ctx.translate(Math.round(center.x), Math.round(center.y)); | ||
ctx.rotate(model.rotation); | ||
|
||
drawFrame(ctx, rects.frame, model); | ||
drawText(ctx, model.lines, rects.text, model); | ||
|
||
ctx.restore(); | ||
} | ||
}); | ||
|
||
export default Label; |
Oops, something went wrong.