e-virt-table
Version:
A powerful data table based on canvas. You can use it as data grid、Microsoft Excel or Google sheets. It supports virtual scroll、cell edit etc.
227 lines • 8.3 kB
JavaScript
export class Paint {
constructor(target) {
Object.defineProperty(this, "ctx", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
const ctx = target.getContext('2d');
if (!ctx)
throw new Error('canvas context not found');
this.ctx = ctx;
}
scale(dpr) {
this.ctx.scale(dpr, dpr);
}
save() {
this.ctx.save();
}
restore() {
this.ctx.restore();
}
translate(x, y) {
this.ctx.translate(x, y);
}
setCursor(cursor = 'default') {
this.ctx.canvas.style.cursor = cursor;
}
clear(x = 0, y = 0, width, height) {
this.ctx.clearRect(x, y, width || this.ctx.canvas.width, height || this.ctx.canvas.height);
}
/**
* 在 Canvas 上绘制单侧阴影
* @param {number} x - 矩形的 x 坐标
* @param {number} y - 矩形的 y 坐标
* @param {number} width - 矩形的宽度
* @param {number} height - 矩形的高度
* @param {string} side - 阴影的位置('left', 'right', 'top', 'bottom')
* @param {number} shadowWidth - 阴影的宽度
* @param {string} color - 阴影的颜色
*/
drawShadow(x, y, width, height, options) {
const { fillColor, side, shadowWidth, colorStart, colorEnd } = options;
this.ctx.save();
if (fillColor) {
this.ctx.fillStyle = fillColor;
this.ctx.fillRect(x, y, width, height);
}
let gradient;
switch (side) {
case 'left':
gradient = this.ctx.createLinearGradient(x - shadowWidth, y, x, y);
gradient.addColorStop(0, colorStart);
gradient.addColorStop(1, colorEnd);
this.ctx.fillStyle = gradient;
this.ctx.fillRect(x - shadowWidth, y, shadowWidth, height);
break;
case 'right':
gradient = this.ctx.createLinearGradient(x + width, y, x + width + shadowWidth, y);
gradient.addColorStop(0, colorStart);
gradient.addColorStop(1, colorEnd);
this.ctx.fillStyle = gradient;
this.ctx.fillRect(x + width, y, shadowWidth, height);
break;
case 'top':
gradient = this.ctx.createLinearGradient(x, y - shadowWidth, x, y);
gradient.addColorStop(0, colorStart);
gradient.addColorStop(1, colorEnd);
this.ctx.fillStyle = gradient;
this.ctx.fillRect(x, y - shadowWidth, width, shadowWidth);
break;
case 'bottom':
gradient = this.ctx.createLinearGradient(x, y + height, x, y + height + shadowWidth);
gradient.addColorStop(0, colorStart);
gradient.addColorStop(1, colorEnd);
this.ctx.fillStyle = gradient;
this.ctx.fillRect(x, y + height, width, shadowWidth);
break;
default:
console.error('Invalid side specified for shadow');
break;
}
}
// 绘制线条
drawLine(points, options) {
if (points.length < 4 || points.length % 2 !== 0) {
throw new Error('A valid array of points is required to draw a line');
}
this.ctx.save();
const { borderColor = 'black', borderWidth = 1 } = options;
this.ctx.beginPath();
this.ctx.moveTo(points[0] - 0.5, points[1] - 0.5);
for (let i = 2; i < points.length; i += 2) {
this.ctx.lineTo(points[i] - 0.5, points[i + 1] - 0.5);
}
this.ctx.strokeStyle = borderColor;
this.ctx.lineWidth = borderWidth;
if (options.lineDash) {
this.ctx.lineDashOffset = 4;
this.ctx.setLineDash(options.lineDash);
}
if (options.fillColor) {
this.ctx.fillStyle = options.fillColor;
this.ctx.fill();
}
if (options.borderColor) {
this.ctx.strokeStyle = options.borderColor;
}
this.ctx.stroke();
this.ctx.closePath();
this.ctx.restore();
}
drawImage(img, x, y, width, height) {
this.ctx.save();
this.ctx.drawImage(img, x, y, width, height);
this.ctx.restore();
}
drawRect(x, y, width, height, { borderWidth = 1, borderColor, fillColor, radius = 0 } = {}) {
this.ctx.save();
this.ctx.beginPath();
// 填充颜色
if (fillColor !== undefined) {
this.ctx.fillStyle = fillColor;
}
// 线条宽度及绘制颜色
if (borderColor !== undefined) {
this.ctx.lineWidth = borderWidth;
this.ctx.strokeStyle = borderColor;
}
if (radius === 0) {
// 绘制矩形路径,- 0.5解决1px模糊的问题
this.ctx.rect(x - 0.5, y - 0.5, width, height);
}
else {
// 确保 radius 是一个包含四个元素的数组
const [tl, tr, br, bl] = typeof radius === 'number' ? [radius, radius, radius, radius] : radius;
// 绘制圆角矩形路径
this.ctx.moveTo(x + tl, y);
this.ctx.arcTo(x + width, y, x + width, y + tr, tr); // draw right side and top-right corner
this.ctx.arcTo(x + width, y + height, x + width - br, y + height, br); // draw bottom side and bottom-right corner
this.ctx.arcTo(x, y + height, x, y + height - bl, bl); // draw left side and bottom-left corner
this.ctx.arcTo(x, y, x + tl, y, tl); // draw top side and top-left corner
}
// 如果有填充色,则填充
if (fillColor !== undefined) {
this.ctx.fill();
}
// 如果有绘制色,则绘制
if (borderColor !== undefined) {
this.ctx.stroke();
}
this.ctx.restore();
}
/**
* 画文本
* @param text
* @param x
* @param y
* @param width
* @param height
* @param options
* @returns 是否溢出
*/
drawText(text = '', x, y, width, height, options = {}) {
this.ctx.save();
const { font = '12px Arial', align = 'center', color = '#495060', padding = 0, verticalAlign = 'middle', } = options;
this.ctx.font = font;
this.ctx.fillStyle = color;
this.ctx.textBaseline = verticalAlign;
this.ctx.textAlign = align;
let yPos = 0;
if (verticalAlign === 'top') {
yPos = y + padding;
}
else if (verticalAlign === 'bottom') {
yPos = y + height - padding;
}
else {
yPos = y + (height + 1) / 2;
}
let xPos = 0;
if (align === 'left') {
xPos = x + padding;
}
else if (align === 'right') {
xPos = x + width - padding;
}
else {
xPos = x + width / 2;
}
const { _text, ellipsis } = this.handleEllipsis(text, width, padding, font);
this.ctx.fillText(_text, xPos, yPos);
this.ctx.restore();
return ellipsis;
}
handleEllipsis(text, width, padding = 0, font = '12px Arial') {
let ellipsis = false;
let _text = text;
this.ctx.font = font;
if (text === null || text === undefined || text === '') {
return {
_text: '',
ellipsis,
};
}
const ellipsesWidth = this.ctx.measureText('...').width;
const textWidth = this.ctx.measureText(text).width;
if (textWidth && textWidth + ellipsesWidth >= width - padding * 2) {
ellipsis = true;
// text字符截取并添加省略号
let textLength = 0;
for (let i = 0; i < text.length; i++) {
textLength += this.ctx.measureText(text[i]).width;
if (textLength >= width - padding * 2 - ellipsesWidth) {
_text = text.slice(0, i) + '...';
break;
}
}
}
return {
_text,
ellipsis,
};
}
}
export default Paint;
//# sourceMappingURL=Paint.js.map