Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#1129] HeatMap > 축 step 타입 면적에 따른 label 간격 조정 & range block 단위로 선택 #1142

Merged
merged 8 commits into from
Apr 22, 2022
16 changes: 12 additions & 4 deletions docs/views/heatMap/api/heatMap.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,19 @@ const chartData =
#### heatMapColor
| 이름 | 타입 | 디폴트 | 설명 | 종류(예시) |
|------------ |-----------|---------|-------------------------|---------------------------------------------------|
| min | number | | min color | '#FFFFFF' |
| max | number | | max color | '#5586EB' |
| min | Hex, RGB, RGBA Code(String) | '#FFFFFF' | min color | |
| max | Hex, RGB, RGBA Code(String) | '#5586EB' | max color | |
| categoryCnt | number | 5 | color min - max 그라데이션 분류 개수 | |
| border | string | '#FF0000' | series item border color 지정 | |
| error | string | '#FFFFFF' | series error color (value가 -1인 경우 error로 인식) | |
| stroke | object | ([상세](#stroke)) | series stroke 지정 | |
| error | Hex, RGB, RGBA Code(String) | '#FFFFFF' | series error color (value가 -1인 경우 error로 인식) | |

##### stroke
| 이름 | 타입 | 디폴트 | 설명 | 종류(예시) |
| --- | ---- | ----- | --- | ----------|
| show | boolean | false | stroke 사용 여부 | |
| color | Hex, RGB, RGBA Code(String) | '#FFFFFF' | stroke color 지정 | |
| lineWidth | number | 1 | stroke 선 굵기 지정 | |
| opacity | number | 1 | stroke opacity 지정 | 0.1 ~ 1 |

### 3. resize-timeout
- Default : 0
Expand Down
1 change: 0 additions & 1 deletion docs/views/heatMap/example/Event.vue
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ import { onMounted, reactive, ref } from 'vue';
}],
axesY: [{
type: 'step',
autoScaleRatio: 0.1,
showGrid: false,
}],
selectItem: {
Expand Down
4 changes: 2 additions & 2 deletions docs/views/heatMap/example/Time.vue
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ import { onBeforeUnmount, reactive, ref, watch } from 'vue';
rangeMode: true,
}],
heatMapColor: {
min: '#e1fbad',
max: '#5b904b',
min: '#E1FBAD',
max: '#5B904B',
categoryCnt: 4,
stroke: {
show: true,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "evui",
"version": "3.3.14",
"version": "3.3.15",
"description": "A EXEM Library project",
"author": "exem <dev_client@ex-em.com>",
"license": "MIT",
Expand Down
3 changes: 2 additions & 1 deletion src/components/chart/Chart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
await watch(() => props.data, (chartData) => {
const newData = getNormalizedData(chartData);
const isUpdateSeries = !isEqual(newData.series, evChart.data.series)
|| !isEqual(newData.groups, evChart.data.groups);
|| !isEqual(newData.groups, evChart.data.groups)
|| props.options.type === 'heatMap';
evChart.data = cloneDeep(newData);
evChart.update({
updateSeries: isUpdateSeries,
Expand Down
118 changes: 108 additions & 10 deletions src/components/chart/element/element.heatmap.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ class HeatMap {
drawItem(ctx, x, y, w, h) {
ctx.beginPath();
if (this.stroke.show) {
ctx.fillRect(x, y, w, h);
ctx.strokeRect(x, y, w, h);
ctx.fillRect(x, y, w, h);
} else {
const aliasPixel = Util.aliasPixel(1);
ctx.fillRect(
Expand Down Expand Up @@ -148,8 +148,11 @@ class HeatMap {
if (this.colorAxis[colorIndex].show) {
ctx.fillStyle = Util.colorStringToRgba(item.dataColor, opacity);
if (this.stroke.show) {
const { color, lineWidth } = this.stroke;
ctx.strokeStyle = Util.colorStringToRgba(color, opacity);
const { color, lineWidth, opacity: sOpacity } = this.stroke;
ctx.strokeStyle = Util.colorStringToRgba(
color,
opacity === 1 ? sOpacity : opacity,
);
ctx.lineWidth = lineWidth;
xp += (lineWidth * 1.5);
yp += (lineWidth * 1.5);
Expand Down Expand Up @@ -216,7 +219,7 @@ class HeatMap {
const centerX = x + (w / 2);
const centerY = y + (h / 2);

if (vw >= w || formattedTxt < 0) {
if (vw >= w || vh >= h || formattedTxt < 0) {
return;
}

Expand Down Expand Up @@ -267,13 +270,10 @@ class HeatMap {
const y1 = yp;
const y2 = yp + h;

return ((x1 <= xsp && x2 >= xsp) && (y1 <= ysp && y2 >= ysp))
|| ((x1 <= xep && x2 >= xep) && (y1 <= ysp && y2 >= ysp))
|| ((x1 <= xsp && x2 >= xsp) && (y1 <= yep && y2 >= yep))
|| ((x1 <= xep && x2 >= xep) && (y1 <= yep && y2 >= yep))
|| ((x1 >= xsp && x1 <= xep) && (y1 >= ysp && y1 <= yep))
return ((x1 >= xsp && x1 <= xep) && (y1 >= ysp && y1 <= yep))
|| ((x1 >= xsp && x1 <= xep) && (y2 >= ysp && y2 <= yep))
|| ((x2 >= xsp && x2 <= xep) && (y1 >= ysp && y1 <= yep));
|| ((x2 >= xsp && x2 <= xep) && (y1 >= ysp && y1 <= yep))
|| ((x2 >= xsp && x2 <= xep) && (y2 >= ysp && y2 <= yep));
});
}

Expand Down Expand Up @@ -352,6 +352,104 @@ class HeatMap {

return item;
}

findBlockRange({ xcp, xep, ycp, yep, range }) {
const labels = this.labels;

const blockRange = {
xsp: Math.min(xcp, xep),
ysp: Math.min(ycp, yep),
width: Math.ceil(Math.abs(xep - xcp)),
height: Math.ceil(Math.abs(yep - ycp)),
};

if (labels.x.length && labels.y.length) {
const { x1, x2, y1, y2 } = range;
const gapX = (x2 - x1) / labels.x.length;
const gapY = (y2 - y1) / labels.y.length;

const point = {
xsp: xcp,
xep,
ysp: ycp,
yep,
};

const setPoint = (dir, target, key) => {
let itemPoint;
let gap;
let startPoint;

if (dir === 'x') {
gap = gapX;
startPoint = x1;
} else {
gap = gapY;
startPoint = y1;
}

const findItem = labels[dir].findIndex((item, index) => {
itemPoint = Math.round(startPoint + (gap * index)) + Util.aliasPixel(1);
return itemPoint <= target && target <= itemPoint + gap;
});

if (findItem > -1) {
point[key] = ['xsp', 'ysp'].includes(key) ? itemPoint : itemPoint + gap;
}
};

setPoint('x', Math.min(xcp, xep), 'xsp');
setPoint('x', Math.max(xcp, xep), 'xep');
setPoint('y', Math.min(ycp, yep), 'ysp');
setPoint('y', Math.max(ycp, yep), 'yep');

blockRange.xsp = Math.min(point.xsp, point.xep);
blockRange.ysp = Math.min(point.ysp, point.yep);
blockRange.width = Math.abs(point.xep - point.xsp);
blockRange.height = Math.abs(point.yep - point.ysp);
}

return blockRange;
}

findSelectionRange(rangeInfo) {
const { xcp, ycp, width, height, range } = rangeInfo;

let selectionRange = null;

const { x1, x2, y1, y2 } = range;
const { x: labelX, y: labelY } = this.labels;

if (labelX.length && labelY.length) {
const gapX = (x2 - x1) / labelX.length;
const gapY = (y2 - y1) / labelY.length;

const xsp = xcp;
const xep = xcp + width;
const ysp = ycp;
const yep = ycp + height;

const xIndex = {
min: Math.floor((xsp - x1) / gapX),
max: Math.floor((xep - x1 - gapX) / gapX),
};

const lastIndexY = labelY.length - 1;
const yIndex = {
min: lastIndexY - Math.floor((yep - y1 - gapY) / gapY),
max: lastIndexY - Math.floor((ysp - y1) / gapY),
};

selectionRange = {
xMin: labelX[xIndex.min],
xMax: labelX[xIndex.max],
yMin: labelY[yIndex.min],
yMax: labelY[yIndex.max],
};
}

return selectionRange;
}
}

export default HeatMap;
12 changes: 10 additions & 2 deletions src/components/chart/model/model.store.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,14 +422,21 @@ const modules = {
const colorOpt = this.options.heatMapColor;
const categoryCnt = colorOpt.categoryCnt;

let minValue;
let maxValue = 0;

let isExistError = false;
data.forEach(({ o: value }) => {
if (maxValue < value) {
maxValue = value;
maxValue = Math.max(maxValue, value);
}

if (value < 0) {
isExistError = true;
} else if (minValue === undefined) {
minValue = value;
} else {
minValue = Math.min(minValue, value);
}
});

Expand All @@ -443,8 +450,9 @@ const modules = {
}

return {
min: minValue,
max: maxValue,
interval: Math.ceil(maxValue / categoryCnt),
interval: Math.ceil((maxValue - minValue) / categoryCnt),
existError: isExistError,
};
},
Expand Down
45 changes: 40 additions & 5 deletions src/components/chart/plugins/plugins.interaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,21 @@ const modules = {
yep = aOffsetY;
}

dragInfo.xsp = Math.min(xcp, xep);
dragInfo.ysp = type === 'scatter' || type === 'heatMap' ? Math.min(ycp, yep) : aRange.y1;
dragInfo.width = Math.ceil(Math.abs(xep - xcp));
dragInfo.height = type === 'scatter' || type === 'heatMap'
if (type === 'heatMap') {
const rangeInfo = { xcp, xep, ycp, yep, range: aRange };
const { xsp, ysp, width, height } = this.getDragInfoForHeatMap(rangeInfo);
dragInfo.xsp = xsp;
dragInfo.ysp = ysp;
dragInfo.width = width;
dragInfo.height = height;
} else {
dragInfo.xsp = Math.min(xcp, xep);
dragInfo.ysp = type === 'scatter' ? Math.min(ycp, yep) : aRange.y1;
dragInfo.width = Math.ceil(Math.abs(xep - xcp));
dragInfo.height = type === 'scatter'
? Math.ceil(Math.abs(yep - ycp))
: aRange.y2 - aRange.y1;
}

this.overlayClear();
this.drawSelectionArea(dragInfo);
Expand All @@ -296,7 +305,9 @@ const modules = {
const args = {
e,
data: this.findSelectedItems(dragInfo),
range: this.getSelectionRage(dragInfo),
range: type === 'heatMap'
? this.getSelectionRangeForHeatMap(dragInfo)
: this.getSelectionRage(dragInfo),
};

this.dragInfoBackup = defaultsDeep({}, dragInfo);
Expand Down Expand Up @@ -639,6 +650,30 @@ const modules = {

return targetValue / total;
},

getDragInfoForHeatMap(range) {
const sId = Object.keys(this.seriesList)[0];
return this.seriesList[sId].findBlockRange(range);
},

getSelectionRangeForHeatMap(range) {
const dataRangeX = this.axesSteps.x.length ? this.axesSteps.x[0] : null;
const dataRangeY = this.axesSteps.y.length ? this.axesSteps.y[0] : null;

if (!dataRangeX || !dataRangeY) {
return null;
}

const sId = Object.keys(this.seriesList)[0];
const { xMin, xMax, yMin, yMax } = this.seriesList[sId].findSelectionRange(range);

return {
xMin: xMin ?? dataRangeX.graphMin,
xMax: xMax ?? dataRangeX.graphMax,
yMin: yMin ?? dataRangeY.graphMin,
yMax: yMax ?? dataRangeY.graphMax,
};
},
};

export default modules;
13 changes: 7 additions & 6 deletions src/components/chart/plugins/plugins.legend.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,14 @@ const modules = {
Object.values(seriesList).forEach((series) => {
if (!series.isExistGrp && series.showLegend) {
const { colorAxis, valueOpt } = series;
const { max, interval, existError } = valueOpt;
const { min, max, interval, existError } = valueOpt;
const endIndex = colorAxis.length - 1;
colorAxis.forEach((colorItem, index) => {
const minValue = interval * index;
const maxValue = index === colorAxis.length - 1
? max : minValue + interval - 1;
const name = existError && index === colorAxis.length - 1
? 'error' : `${minValue} - ${maxValue}`;
const minValue = min + (interval * index);
const maxValue = index === endIndex
? max : minValue + interval;
const name = existError && index === endIndex ? 'error' : `${minValue} - ${maxValue}`;

this.addLegend({
cId: colorItem.id,
color: colorItem.value,
Expand Down
4 changes: 1 addition & 3 deletions src/components/chart/plugins/plugins.tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ const modules = {
}

// 3. Draw value
let formattedTxt;
let formattedTxt = itemValue;
if (opt.formatter) {
formattedTxt = opt.formatter({
x: xValue,
Expand All @@ -419,8 +419,6 @@ const modules = {

if ((!opt.formatter || typeof formattedTxt !== 'string') && itemValue !== 'error') {
formattedTxt = numberWithComma(itemValue);
} else {
formattedTxt = itemValue;
}

ctx.textAlign = 'right';
Expand Down
Loading