juijs-chart
Version:
SVG-based JUI chart that can be used in the browser and Node.js. Support many types of charts. (Dashboard, Map, Topology, Full 3D)
257 lines (219 loc) • 11.9 kB
JavaScript
import jui from '../../main.js';
export default {
name: "chart.brush.canvas.equalizercolumn",
extend: "chart.brush.canvas.core",
component: function () {
const _ = jui.include("util.base");
const CanvasEqualizerColumnBrush = function() {
let zeroY, bar_width, is_reverse;
this.getTargetSize = function() {
let width = this.axis.x.rangeBand();
if(this.brush.size > 0) {
return this.brush.size;
} else {
let size = width - this.brush.outerPadding * 2;
return (size < this.brush.minSize) ? this.brush.minSize : size;
}
}
this.getBarElement = function(dataIndex, targetIndex) {
let style = this.getBarStyle(),
color = this.color(targetIndex),
value = this.getData(dataIndex)[this.brush.target[targetIndex]],
active = this.brush.active,
opacity = 1;
if ((_.typeCheck("array", active) && !active.includes(dataIndex)) ||
(_.typeCheck("integer", active) && active !== dataIndex)) {
opacity = style.disableOpacity;
}
return {
fill : color,
"fill-opacity": opacity,
stroke : style.borderColor,
"stroke-width" : style.borderWidth,
"stroke-opacity" : style.borderOpacity,
hidden: value == 0
};
}
this.getBarStyle = function() {
return {
borderColor: this.chart.theme("barBorderColor"),
borderWidth: this.chart.theme("barBorderWidth"),
borderOpacity: this.chart.theme("barBorderOpacity"),
borderRadius: this.chart.theme("barBorderRadius"),
disableOpacity: this.chart.theme("barDisableBackgroundOpacity"),
circleColor: this.chart.theme("barPointBorderColor")
}
}
this.isErrorColumn = function(i) {
const error = this.brush.error;
if ((_.typeCheck("array", error) && !error.includes(i)) ||
(_.typeCheck("integer", error) && error !== i) || error === null) {
return false;
}
return true;
}
this.drawBefore = function() {
zeroY = this.axis.y(0);
bar_width = this.getTargetSize();
is_reverse = this.axis.get("y").reverse;
}
this.draw = function() {
const targets = this.brush.target,
padding = this.brush.innerPadding,
band = this.axis.y.rangeBand(),
unit = band / (this.brush.unit * padding),
height = unit + padding,
translateY = (is_reverse) ? 0 : -unit;
this.eachData(function(data, i) {
let offsetX = this.offset("x", i),
startX = offsetX - bar_width / 2,
startY = this.axis.y(0),
y = startY,
value = 0,
stackList = [];
for(let j = 0; j < targets.length; j++) {
let yValue = data[targets[j]] + value,
endY = this.axis.y(yValue),
targetHeight = Math.abs(startY - endY),
targetY = targetHeight;
if (!this.isErrorColumn(i)) {
while(targetY >= height) {
let r = _.extend(this.getBarElement(i, j), {
x : startX,
y : y + translateY,
width : bar_width,
height : unit
});
targetY -= height;
y += (is_reverse) ? height : -height;
this.canvas.save();
this.canvas.globalAlpha = r["fill-opacity"];
this.canvas.beginPath();
this.canvas.fillStyle = r.fill;
this.canvas.strokeStyle = r.stroke;
this.canvas.strokeOpacity = r["stroke-opacity"];
this.canvas.lineWidth = r["stroke-width"];
this.canvas.rect(r.x, r.y, r.width, r.height);
this.canvas.fill();
this.canvas.restore();
stackList.push(r);
}
} else {
let size = Math.min(this.axis.x.rangeBand(), this.axis.area("height")) * 0.4;
let height = this.axis.area("height") * 0.5;
let tick = size * 0.3;
let startX = offsetX - size / 2;
let fontSize = height / 5;
let yt = y - tick;
let yht = y - height - tick;
let round = 5;
this.canvas.save();
this.canvas.beginPath();
this.canvas.fillStyle = this.chart.theme("equalizerColumnErrorBackgroundColor");
this.canvas.moveTo(offsetX, y);
this.canvas.lineTo(startX, yt);
this.canvas.lineTo(startX, yht + round);
this.canvas.arcTo(startX, yht, startX + round, yht, round);
this.canvas.lineTo(startX + size - round, yht);
this.canvas.arcTo(startX + size, yht, startX + size, yht + round, round);
this.canvas.lineTo(startX + size, yt);
this.canvas.fill();
this.canvas.save();
this.canvas.font = `${fontSize}px ${this.chart.theme("fontFamily")}`;
this.canvas.translate(offsetX, y - height - tick);
this.canvas.rotate(Math.PI / 2);
this.canvas.textAlign = "center";
this.canvas.fillStyle = this.chart.theme("equalizerColumnErrorFontColor");
this.canvas.fillText(this.brush.errorText, height / 1.75, fontSize / 3, height);
this.canvas.restore();
}
startY = endY;
value = yValue;
}
if(stackList.length > 0) {
this.chart.setCache(`equalizer_${i}`, stackList.length == 0 ? null : stackList[stackList.length - 1]);
this.chart.setCache(`raycast_area_${i}`, {
x1: stackList[0].x,
x2: stackList[0].x + stackList[0].width,
y2: this.axis.y(this.axis.y.min()),
y1: stackList[stackList.length - 1].y
});
}
});
this.drawAnimation();
}
this.drawAnimation = function() {
const MAX_DISTANCE = 8; // 애니메이션 움직인 최대 반경 (0px ~ 10px)
const UP_SEC_PER_MOVE = 20; // 초당 20픽셀 이동
const DOWN_SEC_PER_MOVE = 30; // 초당 30픽셀 이동
const TOP_PADDING = -3;
const TOTAL_PADDING = -8;
this.eachData(function (data, i) {
if (!this.isErrorColumn(i)) {
const r = this.chart.getCache(`equalizer_${i}`);
let total = 0;
for(let j = 0; j < this.brush.target.length; j++) {
total += data[this.brush.target[j]];
}
if(r != null) {
const tpf = this.chart.getCache(`tpf`, 1);
const status = this.chart.getCache(`equalizer_move_${i}`, { direction: -1, distance: 0 });
const speed = status.direction == -1 ? UP_SEC_PER_MOVE : DOWN_SEC_PER_MOVE;
status.distance += status.direction * speed * tpf;
// 애니메이션-바 방향 벡터 설정
if(Math.abs(status.distance) >= MAX_DISTANCE) {
status.direction = 1;
} else if(status.distance >= 0) {
status.direction = -1;
}
// 애니메이션-바 최소/최대 위치 설정
if(status.distance < -MAX_DISTANCE) {
status.distance = -MAX_DISTANCE;
} else if(status.distance > 0) {
status.distance = 0;
}
const ry = r.y + status.distance + TOP_PADDING;
this.canvas.save();
this.canvas.globalAlpha = r["fill-opacity"];
this.canvas.strokeStyle = r.fill;
this.canvas.lineWidth = r.height * 0.7;
this.canvas.beginPath();
this.canvas.moveTo(r.x, ry);
this.canvas.lineTo(r.x + r.width, ry);
this.canvas.closePath();
this.canvas.stroke();
this.canvas.fillStyle = this.chart.theme("barFontColor");
this.canvas.font = this.chart.theme("barFontSize") + "px";
this.canvas.textAlign = "center";
this.canvas.textBaseline = "middle";
this.canvas.fillText(total, r.x + r.width/2, ry + TOTAL_PADDING);
this.canvas.fill();
this.canvas.restore();
this.chart.setCache(`equalizer_move_${i}`, status);
}
}
});
}
}
CanvasEqualizerColumnBrush.setup = function() {
return {
/** @cfg {Number} [size=0] Set a fixed size of the bar. */
size: 0,
/** @cfg {Number} [minSize=0] Sets the minimum size as it is not possible to draw a bar when the value is 0. */
minSize: 0,
/** @cfg {Number} [outerPadding=15] Determines the outer margin of a stack bar. */
outerPadding: 15,
/** @cfg {Number} [innerPadding=1] Determines the inner margin of a bar. */
innerPadding: 1,
/** @cfg {Number} [unit=5] Determines the reference value that represents the color.*/
unit: 1,
/** @cfg {Number | Array} [active=null] Activates the bar of an applicable index. */
active: null,
/** @cfg {Number | Array} [active=null] Activates the bar of an applicable index. */
error: null,
errorText: "Stopped"
};
}
return CanvasEqualizerColumnBrush;
}
}