import * as PIXI from 'pixi.js';
import Text from './text';
import { EasingFunctions } from './variable';
/**
* Chart.js가 아닌 자체적으로 생성한 chart에서 사용하기 위한 Tooltip 클래스
* @alias Tooltip
*/
export default class Tooltip {
/*
mode
0 = hide
1 = show
direction
-1 = left
1 = right
*/
/**
* Tooltip을 띄우기 위해 PIXI의 Graphics, Text를 생성하고 필요한 값을 셋팅
* @param {PIXI} pixi PIXI 오브젝트
*/
constructor(pixi) {
/**
* @description PIXI 오브젝트
* @type { PIXI }
*/
this.pixi = pixi;
/**
* @description Tooltip의 배경을 위한 PIXI.Graphics 오브젝트
* @type { PIXI.Graphics }
*/
this.graphics = new PIXI.Graphics();
this.graphics.zOrder = -999;
pixi.stage.enableSort = true;
pixi.stage.addChild(this.graphics);
/**
* @description Tooltip의 title 텍스트를 위한 PIXI.Text 오브젝트
* @type { PIXI.Text }
*/
this.title = new Text(pixi, 'title', {
fill: '#ffffff',
fontWeight: 'bold',
});
this.title.object.zOrder = this.graphics.zOrder - 1;
/**
* @description Tooltip의 value 텍스트를 위한 PIXI.Text의 오브젝트
* @type { PIXI.Text }
*/
this.value = new Text(pixi, 'value', {
fill: '#ffffff',
});
this.value.object.zOrder = this.graphics.zOrder - 1;
this.graphics.alpha = 0;
this.title.object.alpha = 0;
this.value.object.alpha = 0;
/**
* @description Tooltip의 투명도 값 0.0 ~ 1.0
* @type { number }
*/
this.alpha = 0;
/**
* @description Tooltip이 투명도 0에서 1까지 걸리는 시간 (단위 : 초)
* @type { number }
*/
this.duration = 0.2;
/**
* @description duration에 따른 초당 설정해야하는 투명도 값
* @type { number }
*/
this.velocity = 1 / 60 / this.duration;
/**
* @description Tooltip 표현 여부 0 = 숨김, 1 = 표현
* @type { number }
*/
this.mode = 0;
/**
* @description Tooltip의 안쪽 여백
* @type { object }
* @property { number } width 좌우 여백
* @property { number } height 상하 여백
*/
this.padding = {
width: 10,
height: 5,
};
/**
* @description Tooltip 말풍선 모양에서 삼각형의 크기
* @type { number }
*/
this.triangleWidth = 5;
/**
* @description Tooltip 생성 방향 1 = 오른쪽, -1 = 왼쪽
* @type { number }
*/
this.direction = 1;
/**
* @description Tooltip x 좌표
* @type { number }
*/
this.x = 110;
/**
* @description Tooltip y 좌표
* @type { number }
*/
this.y = 110;
/**
* @description Tooltip value에 표시될 단위
* @type { string }
*/
this.unit = '';
const textScale = 0.5 * pixi.renderer.width * (1 / 339);
this.title.object.scale.set(textScale > 1 ? 1 : textScale);
this.value.object.scale.set(textScale > 1 ? 1 : textScale);
this.setPosition(this.x, this.y);
}
/**
* Tooltip의 상태를 변경하여 사용자에게 보여주는 함수
* @memberof Tooltip
* @instance
*/
show() {
if (this.updateInterval === undefined) {
this.updateInterval = setInterval(() => this.update(), 16);
}
this.mode = 1;
}
/**
* Tooltip의 상태를 변경하여 사용자로부터 숨기는 함수
* @memberof Tooltip
* @instance
*/
hide() {
if (this.updateInterval === undefined) {
this.updateInterval = setInterval(() => this.update(), 16);
}
this.mode = 0;
}
/**
* Tooltip의 위치를 설정
* @param {number} x Tooltip의 x좌표
* @param {number} y Tooltip의 y좌표
* @memberof Tooltip
* @instance
*/
setPosition(x, y) {
let destinationX = x;
let destinationY = y;
this.determineBackgroundSize();
if (x < this.pixi.renderer.width / 2) {
this.direction = 1;
} else {
this.direction = -1;
}
destinationX += ((this.backgroundWidth / 2 + this.triangleWidth) * this.direction);
// 툴팁이 화면을 넘어갈 때
if (y + this.backgroundHeight / 2 > this.pixi.renderer.height) {
destinationY = this.pixi.renderer.height - this.backgroundHeight / 2;
} else if (y - this.backgroundHeight / 2 < 0) {
destinationY = this.backgroundHeight / 2;
}
this.destinationX = destinationX;
this.destinationY = destinationY;
if (this.positionUpdateInterval === undefined) {
this.moveStartTime = new Date();
this.positionUpdateInterval = setInterval(() => { this.positionUpdate(); }, 16);
}
}
/**
* Tooltip의 위치를 이동
* <p><b>내부 함수</b>
* @memberof Tooltip
* @instance
*/
positionUpdate() {
const elapsedTime = new Date() - this.moveStartTime;
const easingTime = elapsedTime / (this.duration * 1000);
const easing = EasingFunctions.easeOutQuart;
const distanceX = this.destinationX - this.x;
const distnaceY = this.destinationY - this.y;
const progress = easingTime < 1 ? easing(easingTime) : 1;
this.currentX = this.x + (distanceX * progress);
this.currentY = this.y + (distnaceY * progress);
this.title.setPosition(this.currentX, this.currentY - this.title.object.height / 2);
this.value.setPosition(this.currentX, this.currentY + this.value.object.height / 2);
this.draw();
if (progress >= 1) {
clearInterval(this.positionUpdateInterval);
delete this.positionUpdateInterval;
this.x = this.destinationX;
this.y = this.destinationY;
}
}
/**
* Tooltip의 데이터를 설정
* @param {string} title Tooltip의 title 설정
* @param {string} value Tooltip의 value 설정
* @memberof Tooltip
* @instance
*/
setData(title, value) {
this.title.object.text = title;
this.value.object.text = value.toLocaleString() + this.unit;
this.determineBackgroundSize();
this.draw();
}
/**
* Tooltip의 단위를 설정
* @param {string} unit Tooltip의 단위
* @memberof Tooltip
* @instance
*/
setUnit(unit) {
this.unit = unit || '';
}
/**
* Tooltip의 크기를 설정
* @memberof Tooltip
* @instance
*/
determineBackgroundSize() {
const textWidth = this.title.object.width > this.value.object.width ? this.title.object.width : this.value.object.width;
this.backgroundWidth = textWidth + this.padding.width * 2;
const textHeight = (this.value.object.y + this.value.object.height / 2) - (this.title.object.y - this.title.object.height / 2);
this.backgroundHeight = textHeight + this.padding.height * 2;
}
/**
* Tooltip의 삼각형을 그림
* @param {number} size 화면크기에 Tooltip의 크기가 달라질 수 있게 size를 받아 조절
* @memberof Tooltip
* @instance
*/
triangle(size) {
this.graphics.moveTo(this.currentX + (this.backgroundWidth / 2 * -this.direction), this.currentY + (-size / 2));
this.graphics.lineTo(this.currentX + (this.backgroundWidth / 2 * -this.direction) + (size / 2 * -this.direction), this.currentY);
this.graphics.lineTo(this.currentX + (this.backgroundWidth / 2 * -this.direction), this.currentY + size / 2);
this.graphics.lineTo(this.currentX + (this.backgroundWidth / 2 * -this.direction), this.currentY);
}
/**
* Tooltip의 배경을 그림
* @memberof Tooltip
* @instance
*/
draw() {
this.graphics.clear();
this.graphics.beginFill(0x000000, 0.7);
this.graphics.drawRoundedRect(this.currentX - this.backgroundWidth / 2, this.currentY - this.backgroundHeight / 2, this.backgroundWidth, this.backgroundHeight, 7);
this.triangle(10);
this.graphics.endFill();
}
/**
* Tooltip의 투명도를 관리하고 적용
* @memberof Tooltip
* @instance
*/
update() {
switch (this.mode) {
case 0:
if (this.alpha > 0) {
this.alpha -= this.velocity;
} else {
this.alpha = 0;
clearInterval(this.updateInterval);
delete this.updateInterval;
}
break;
case 1:
if (this.alpha < 1) {
this.alpha += this.velocity;
} else {
this.alpha = 1;
clearInterval(this.updateInterval);
delete this.updateInterval;
}
break;
default:
break;
}
this.graphics.alpha = this.alpha;
this.title.object.alpha = this.alpha;
this.value.object.alpha = this.alpha;
}
}