因为公司的业务需要,highcharts&echarts这些图表库的样式不符合设计的要求,所以在考虑了不用考虑ie8等浏览器的情况下,选择canvas封装了一个支持个性化配置的柱状图。希望有需要类似图的前端伙伴,可以有用。
因为个性化,需要各种自定义大小颜色的参数,所以我把参数分为了4类——基础参数、坐标轴、柱子数据、增强属性。
-
先看个效果图
-
接入方式
- html中放置一个canvas
<canvas id="canvas"></canvas>
引入下面的function drawHistogram
执行方法
drawHistogram(chartsConfigObj);
-
参数解释
- 第一层参数解释
/** * 第一层参数 * @param {basicData} 必传,基础参数 * @param {axisData} 必传,坐标轴参数 * @param {columnData} 必传,柱子数据 * @param {enhancementAttr} 非必传,一些增强属性 */
- 第二层参数
let { basicData: { wrapEleId, width, height }, axisData: { basic: { axislineWidth, axisColor, axisTextColor, axisTextFont, }, axisX: { axisXStart, axisXWidth, axisXTextDistance }, axisY: { axisYStart, axisYWidth, axisYIntervalNum, axisYTextDistance, axisYMaxNum } }, columnData: { eachColumnWidth, spaceDistance, columnMaxHeight, columnColor, firstColumnStartX, firstColumnStartY }, graphs } = chartsConfigObj; const {enhancementAttr} = chartsConfigObj; let { isColumnNeedRadius, columnGradientData, isRemoveAxisArrow, horizontalLineObj } = enhancementAttr || {}; let { gradientStartColor, gradientEndColor } = columnGradientData || {}; let { horizontalLineColor, horizontalLineDistance } = horizontalLineObj || {}; /** * 具体的每个柱状图参数 * @param {wrapEleId} canvas画布id * @param {width} 画布宽 * @param {height} 画布高 * @param {eachColumnWidth} 一个柱子的宽度 * @param {spaceDistance} 柱子间的间距 * @param {firstColumnStartX} 第一个柱子左下角的X轴坐标 * @param {firstColumnStartY} 第一个柱子左下角的Y轴坐标 * @param {columnMaxHeight} 柱子最大高度 * @param {axislineWidth} 坐标轴的线的宽度 * @param {axisXStart} 坐标轴X轴起点坐标 * @param {axisYStart} 坐标轴Y轴起点坐标 * @param {axisXWidth} 坐标轴X轴长度 * @param {axisYWidth} 坐标轴Y轴长度 * @param {axisXTextDistance} 坐标轴X轴文字距离X轴垂直方向的偏移量 * @param {axisYTextDistance} 坐标轴Y轴水平方向的偏移量 * @param {axisYIntervalNum} 左边轴y轴间隔 * @param {axisYMaxNum} 左边轴y轴标尺最大值 * @param {axisColor} 坐标轴线颜色 * @param {axisTextColor} 坐标轴文本颜色 * @param {axisTextFont} 坐标轴文本fontSize、fontFamily * @param {columnColor} 柱状图颜色 * @param {graphs} 图表数据-数组,[{x:,y:},...] * @param {columnGradientData} 柱子渐变色信息,不需要渐变就不传此参数 * @param {gradientStartColor} 渐变开始色(靠近x轴) * @param {gradientEndColor} 渐变结束色 * @param {isColumnNeedRadius} 柱子是否需要圆角 * @param {gradientEndColor} 渐变结束色 * @param {gradientEndColor} 渐变结束色 * @param {gradientEndColor} 渐变结束色 * @param {horizontalLineObj} 水平虚线背景参数,不需要则不传{color,space} */
功能实现代码
export default function drawHistogram(chartsConfigObj) {
let {
basicData: {
wrapEleId,
width,
height
},
axisData: {
basic: {
axislineWidth,
axisColor,
axisTextColor,
axisTextFont,
},
axisX: {
axisXStart,
axisXWidth,
axisXTextDistance
},
axisY: {
axisYStart,
axisYWidth,
axisYIntervalNum,
axisYTextDistance,
axisYMaxNum
}
},
columnData: {
eachColumnWidth,
spaceDistance,
columnMaxHeight,
columnColor,
firstColumnStartX,
firstColumnStartY
},
graphs
} = chartsConfigObj;
const {enhancementAttr} = chartsConfigObj;
let {
isColumnNeedRadius,
columnGradientData,
isRemoveAxisArrow,
horizontalLineObj
} = enhancementAttr || {};
let {
gradientStartColor,
gradientEndColor
} = columnGradientData || {};
let {
horizontalLineColor,
horizontalLineDistance
} = horizontalLineObj || {};
horizontalLineColor = horizontalLineColor ? horizontalLineColor : '#eee';
horizontalLineDistance = horizontalLineDistance ? horizontalLineDistance: 5;
isRemoveAxisArrow = isRemoveAxisArrow ? isRemoveAxisArrow : false;
var canvas = document.getElementById(wrapEleId);
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
context.beginPath();
//画x轴
context.beginPath();
context.moveTo(axisXStart, axisYStart);
context.lineTo(axisXStart + axisXWidth, axisYStart);
context.strokeStyle = axisColor;
context.lineWidth = axislineWidth;
context.stroke();
context.beginPath();
if (!isRemoveAxisArrow) {
context.moveTo(axisXStart + axisXWidth - 6, axisYStart-6);
context.lineTo(axisXStart + axisXWidth, axisYStart);
context.lineTo(axisXStart + axisXWidth - 6, axisYStart+6);
context.strokeStyle = axisColor;
context.lineWidth = axislineWidth;
context.stroke();
}
//画y轴
context.beginPath();
var axisYEnd = height - (height - axisYStart + axisYWidth);
context.moveTo(axisXStart, axisYStart);
context.lineTo(axisXStart, axisYEnd);
context.strokeStyle = axisColor;
context.lineWidth = axislineWidth;
context.stroke();
context.beginPath();
if (!isRemoveAxisArrow) {
context.moveTo(axisXStart - 6, axisYEnd + 6);
context.lineTo(axisXStart, axisYEnd);
context.lineTo(axisXStart + 6, axisYEnd + 6);
context.strokeStyle = axisColor;
context.lineWidth = axislineWidth;
context.stroke();
}
//画y轴上的数字
var axisYNum = axisYMaxNum / axisYIntervalNum;
var axisYLength = columnMaxHeight / axisYNum;
for (var j = 0; j <= axisYNum; j++) {
context.beginPath();
context.moveTo(axisXStart, axisYStart);
context.font = axisTextFont;
context.textAlign = "center";
context.fillStyle = axisTextColor;
context.fillText(j*axisYIntervalNum,axisXStart - 15, axisYStart - j * axisYLength + axisYTextDistance);
context.beginPath();
context.moveTo(axisXStart, axisYStart - j * axisYLength);
context.lineTo(axisXStart+5, axisYStart - j * axisYLength);
context.strokeStyle = axisColor;
context.lineWidth = axislineWidth;
context.stroke();
if (horizontalLineObj && j !== 0) {
context.beginPath();
context.moveTo(axisXStart, axisYStart - j * axisYLength);
context.lineTo(axisXStart+axisXWidth, axisYStart - j * axisYLength);
context.setLineDash([horizontalLineObj.space || 10]);
context.strokeStyle = horizontalLineObj.color || '#eee';
context.stroke();
}
}
context.beginPath();
//画柱状图
var columnLength = graphs.length;
for (var i = 0; i < columnLength; i++) {
var colData = graphs[i];
var colStratX = firstColumnStartX+(i*spaceDistance)+(i*eachColumnWidth);
var colStratY = height - (height - firstColumnStartY + (colData.y/100) * columnMaxHeight);
if(isColumnNeedRadius) {
const r = eachColumnWidth / 2;
const w = eachColumnWidth;
const h = -((colData.y/100) * columnMaxHeight);
const x = colStratX;
const y = firstColumnStartY;
context.beginPath();
context.moveTo(x + r, y);
context.arcTo(x + w, y, x + w, y + h, r);
context.arcTo(x + w, y + h, x, y + h, r);
context.arcTo(x, y + h, x, y, r);
context.arcTo(x, y, x + w, y, r);
context.closePath();
} else {
context.beginPath();
context.moveTo(colStratX, firstColumnStartY);
context.lineTo(colStratX, colStratY);
context.lineTo(colStratX+eachColumnWidth, colStratY);
context.lineTo(colStratX+eachColumnWidth, firstColumnStartY);
context.closePath();
}
//需要渐变
if (columnGradientData) {
var grad = context.createLinearGradient(0, firstColumnStartY, 0,colStratY);
grad.addColorStop(0, gradientStartColor); // 绿
grad.addColorStop(1, gradientEndColor); // 紫
context.fillStyle = grad;
} else {
context.fillStyle = columnColor;
}
context.fill();
context.font = axisTextFont;
context.textAlign="center";
context.fillStyle = axisTextColor;
context.fillText(colData.x, colStratX+eachColumnWidth/2, firstColumnStartY + axisXTextDistance);
}
}
- 例子数据
const chartsConfigObj = {
basicData: {
wrapEleId: 'canvas',
width: 860,
height: 400
},
axisData: {
basic: {
axislineWidth: 1,
axisColor: '#999',
axisTextColor: '#666',
axisTextFont: '12px PingFangSC-Regular',
},
axisX: {
axisXStart: 26,
axisXWidth: 850,
},
axisY: {
axisYStart: 365,
axisYWidth: 190,
axisYIntervalNum: 20,
axisYTextDistance: 2,
axisYMaxNum: 100
}
},
columnData: {
eachColumnWidth: 14,
spaceDistance: 30,
columnMaxHeight: 156,
columnColor: '#f00',
firstColumnStartX: 50,
firstColumnStartY: 365
},
enhancementAttr: {
isColumnNeedRadius: true,
columnGradientData: {
gradientStartColor: '#35E1A0',
gradientEndColor: '#29CDC0'
},
horizontalLineObj: {
horizontalLineColor: '#999',
horizontalLineDistance: 5
},
isRemoveAxisArrow: true
},
graphs: [
{
'x': '2011',
'y': '100'
}, {
'x': '2012',
'y': '90'
}, {
'x': '2013',
'y': '60'
}, {
'x': '2014',
'y': '90'
}, {
'x': '2015',
'y': '70'
}, {
'x': '2016',
'y': '10'
}, {
'x': '2011',
'y': '100'
}, {
'x': '2012',
'y': '90'
}, {
'x': '2013',
'y': '60'
}, {
'x': '2014',
'y': '90'
}, {
'x': '2015',
'y': '70'
}, {
'x': '2016',
'y': '10'
}, {
'x': '2011',
'y': '100'
}, {
'x': '2012',
'y': '90'
}, {
'x': '2013',
'y': '60'
}, {
'x': '2014',
'y': '30'
}, {
'x': '2015',
'y': '10'
}, {
'x': '2016',
'y': '95'
}
]
};