├── pages
└── index
│ ├── index.json
│ ├── index.wxml
│ ├── index.wxss
│ └── index.js
├── image
├── pie.png
├── line.png
├── barchart.png
└── barline.png
├── app.js
├── app.wxss
├── app.json
├── utils
├── util.js
├── chartUtils.js
└── chart.js
├── LICENSE
└── README.md
/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/image/pie.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ioneday/wxchart/HEAD/image/pie.png
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | //app.js
2 | App({
3 | onLaunch: function () {
4 |
5 | }
6 | })
--------------------------------------------------------------------------------
/image/line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ioneday/wxchart/HEAD/image/line.png
--------------------------------------------------------------------------------
/image/barchart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ioneday/wxchart/HEAD/image/barchart.png
--------------------------------------------------------------------------------
/image/barline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ioneday/wxchart/HEAD/image/barline.png
--------------------------------------------------------------------------------
/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app.wxss:
--------------------------------------------------------------------------------
1 | /**app.wxss**/
2 | .container {
3 | height: 100%;
4 | display: flex;
5 | flex-direction: column;
6 | align-items: center;
7 | justify-content: space-between;
8 | padding: 200rpx 0;
9 | box-sizing: border-box;
10 | }
11 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages":[
3 | "pages/index/index"
4 | ],
5 | "window":{
6 | "backgroundTextStyle":"light",
7 | "navigationBarBackgroundColor": "#fff",
8 | "navigationBarTitleText": "WeChat",
9 | "navigationBarTextStyle":"black"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | page {
2 | text-align: center;
3 | background-color: #ffffff;
4 | }
5 | .myCanvas{
6 | background-color: #f4f4f4;
7 | }
8 | .downloadChart {
9 | font-size: 16px;
10 | background: #699fed;
11 | border-radius: 6px;
12 | color: white;
13 | width: 70%;
14 | margin-top: 32px;
15 | }
16 |
--------------------------------------------------------------------------------
/utils/util.js:
--------------------------------------------------------------------------------
1 | function formatTime(date) {
2 | var year = date.getFullYear()
3 | var month = date.getMonth() + 1
4 | var day = date.getDate()
5 |
6 | var hour = date.getHours()
7 | var minute = date.getMinutes()
8 | var second = date.getSeconds()
9 |
10 |
11 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
12 | }
13 |
14 | function formatNumber(n) {
15 | n = n.toString()
16 | return n[1] ? n : '0' + n
17 | }
18 |
19 | module.exports = {
20 | formatTime: formatTime
21 | }
22 |
--------------------------------------------------------------------------------
/pages/index/index.js:
--------------------------------------------------------------------------------
1 | // test.js
2 | var chart = require("../../utils/chart.js");
3 | Page({
4 |
5 | data: {
6 | },
7 | onLoad: function (options) {
8 | chart.draw(this, 'canvas1', {
9 | title: {
10 | text: "2017城市人均收入(万)",
11 | color: "#333333"
12 | },
13 | xAxis: {
14 | data: ['北京', '上海', '杭州', '深圳', '广州', '成都', '南京', '西安']
15 | },
16 | series: [
17 | // {
18 | // name: "第一季度",
19 | // category: "bar",
20 | // data: [37, 63, 60, 78, 92, 63, 57, 48]
21 | // },
22 | // {
23 | // name: "第二季度",
24 | // category: "line",
25 | // data: [20, 35, 38, 59, 48, 27, 43, 35]
26 | // },
27 | {
28 | name: ['北京', '上海', '杭州', '深圳', '广州', '成都'],
29 | category: "pie",
30 | data: [40, 38, 39, 28, 27, 33]
31 | }
32 | ]
33 | });
34 | },
35 | onSaveClick: function () {
36 | chart.saveCanvans(function () {
37 |
38 | });
39 | }
40 | })
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Dee
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # wxchart
2 | 微信小程序,图表组件(柱状图、折线图、饼图、雷达图...)。持续更新中...
3 |
4 |
5 |
6 | 
7 |
8 |
9 |
10 |
11 |
12 | ### Useage
13 |
14 | wxml:
15 |
16 | ```javascript
17 | //自适应屏幕宽度
18 |
19 | ```
20 |
21 | js:
22 |
23 | ```javascript
24 | var chart = require("../../utils/chart.js");
25 |
26 | chart.draw(this, 'canvasId', {
27 | hideYaxis: false,
28 | color: ['#394655', '#74DAE5', '#ED7672', '#F3AA59', '#FEE746'],
29 | title: {
30 | text: "2017城市人均收入(万)",
31 | color: "#333333",
32 | size: 16
33 | },
34 | xAxis: {
35 | color: "#666A73",
36 | size: 10,
37 | data: ['北京', '上海', '杭州', '深圳', '广州', '成都', '南京', '西安']
38 | },
39 | series: [
40 | {
41 | name: "第一季度",
42 | category: "bar", //切换柱状图或折线图
43 | data: [37, 63, 60, 78, 92, 63, 57, 48]
44 | },
45 | {
46 | name: "第二季度",
47 | category: "line",
48 | data: [20, 35, 38, 59, 48, 27, 43, 35]
49 | },
50 | {
51 | name: ['北京', '上海', '杭州', '深圳', '广州', '成都'],
52 | category: "pie",//饼图不能与以上类型(bar、line)同时绘制
53 | data: [40, 38, 39, 28, 27, 33]
54 | }
55 | ]
56 | });
57 | ```
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/utils/chartUtils.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | mesureText: mesureText,
3 | calculateY: calculateY,
4 | drawDashLine: drawDashLine,
5 | drawRoundBar: drawRoundBar
6 | }
7 | /**
8 | * 测量文字宽度,
9 | * Canvas宽度太大,微信提供的setTextAlign(center)
10 | * 方法并不能准确居中显示
11 | */
12 | function mesureText(text, textSize) {
13 | var ratio = textSize / 20;
14 | var text = text.split('');
15 | var width = 0;
16 | text.forEach(function (item) {
17 | if (/[a-zA-Z]/.test(item)) {
18 | width += (14 * ratio);
19 | } else if (/[0-9]/.test(item)) {
20 | width += (11 * ratio);
21 | } else if (/\./.test(item)) {
22 | width += (5.4 * ratio);
23 | } else if (/-/.test(item)) {
24 | width += (6.5 * ratio);
25 | } else if (/[\u4e00-\u9fa5]/.test(item)) {
26 | width += (20 * ratio);
27 | }
28 | });
29 | return width;
30 | }
31 | /**
32 | * 计算Y轴显示刻度
33 | */
34 | function calculateY(dMin, dMax, iMaxAxisNum) {
35 | if (iMaxAxisNum < 1 || dMax < dMin)
36 | return;
37 |
38 | var dDelta = dMax - dMin;
39 | if (dDelta < 1.0) {
40 | dMax += (1.0 - dDelta) / 2.0;
41 | dMin -= (1.0 - dDelta) / 2.0;
42 | }
43 | dDelta = dMax - dMin;
44 |
45 | var iExp = parseInt(Math.log(dDelta) / Math.log(10.0)) - 2;
46 | var dMultiplier = Math.pow(10, iExp);
47 | var dSolutions = [1, 2, 2.5, 5, 10, 20, 25, 50, 100, 200, 250, 500];
48 | var i;
49 | for (i = 0; i < dSolutions.length; i++) {
50 | var dMultiCal = dMultiplier * dSolutions[i];
51 | if ((parseInt(dDelta / dMultiCal) + 1) <= iMaxAxisNum) {
52 | break;
53 | }
54 | }
55 |
56 | var dInterval = dMultiplier * dSolutions[i];
57 |
58 | var dStartPoint = (parseInt(dMin / dInterval) - 1) * dInterval;
59 | var yIndex = [];
60 | var iAxisIndex;
61 | for (iAxisIndex = 1; true; iAxisIndex++) {
62 | var y = dStartPoint + dInterval * iAxisIndex;
63 | console.log(y);
64 | yIndex.push(y);
65 | if (y > dMax)
66 | break;
67 | }
68 | return yIndex;
69 | }
70 | /**
71 | * 绘制虚线
72 | */
73 | function drawDashLine(ctx, x1, y1, x2, y2, dashLen) {
74 | dashLen = dashLen === undefined ? 4 : dashLen;
75 | var beveling = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
76 | var num = Math.floor(beveling / dashLen);
77 |
78 | ctx.beginPath();
79 | for (var i = 0; i < num; i++) {
80 | var x = x1 + (x2 - x1) / num * i;
81 | var y = y1 + (y2 - y1) / num * i;
82 | if (i % 2 == 0) {
83 | ctx.moveTo(x, y);
84 | } else {
85 | ctx.lineTo(x, y);
86 | }
87 |
88 | }
89 | ctx.stroke();
90 | ctx.closePath();
91 | }
92 | /**
93 | * 绘制圆角矩形
94 | */
95 | function drawRoundBar(ctx, x, y, width, height, radius) {
96 | ctx.beginPath();
97 | ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2);
98 | ctx.lineTo(width - radius + x, y);
99 | ctx.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2);
100 | ctx.lineTo(width + x, height + y - radius);
101 | ctx.arc(width - radius + x, height - radius + y, radius, 0, Math.PI * 1 / 2);
102 | ctx.lineTo(radius + x, height + y);
103 | ctx.arc(radius + x, height - radius + y, radius, Math.PI * 1 / 2, Math.PI);
104 | ctx.closePath();
105 | ctx.fill();
106 | }
107 |
108 | function requestAnimation(callback) {
109 | var that = this;
110 | // 保证如果重复执行callback的话,callback的执行起始时间相隔16ms
111 | var currTime = new Date().getTime();
112 | var timeToCall = Math.max(0, 16 - (currTime - lastTime));
113 | var id = setTimeout(function () {
114 | callback(currTime + timeToCall);
115 | }, timeToCall);
116 | lastTime = currTime + timeToCall;
117 | return id;
118 | }
119 |
120 | function easeOut(t, b, c, d) {
121 | return c * ((t = t / d - 1) * t * t + 1) + b;
122 | }
--------------------------------------------------------------------------------
/utils/chart.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = {
3 | draw: init,
4 | saveCanvans: saveCanvans
5 | }
6 |
7 | var util = require("chartUtils.js");
8 | var canvasId = '';
9 | var pageObj = null;
10 | var chartOpt = {
11 | chartPieCount: 0,
12 | hideXYaxis: false,
13 | axisMark: [],
14 | barLength: 0,
15 | barNum: 0,
16 | // bgColor: "transparent",
17 | lineColor: "#c2c2c2",
18 | bgColor: "#ffffff",
19 | chartWidth: 0,
20 | chartHeight: 0,
21 | legendWidth: 0,
22 | legendHeight: 0,
23 | chartSpace: 10,
24 | textSpace: 5,
25 | top: 0,
26 | left: 0,
27 | right: 0,
28 | bottom: 0,
29 | axisLeft: 0,
30 | axisBottom: 0,
31 | axisTop: 0
32 | }
33 | var dataSet = {
34 | hideYaxis: false,
35 | title: {
36 | color: "#394655",
37 | size: 16,
38 | text: ""
39 | },
40 | legend: {
41 | color: "",
42 | size: 12
43 | },
44 | color: ['#74DAE5', '#394655', '#FEE746', '#B9A39B', '#C18734', '#9EC3AD', '#6D9BA3', '#7E9C82', '#DAEE59', '#CFDCED'],
45 | xAxis: {
46 | color: "#666A73",
47 | size: 10,
48 | data: []
49 | },
50 | series: [
51 | {
52 | name: "",
53 | category: "bar",
54 | data: []
55 | },
56 | {
57 | name: "",
58 | category: "line",
59 | data: []
60 | }
61 | ]
62 | }
63 |
64 | function init(page, canvas, data) {
65 | canvasId = canvas;
66 | pageObj = page;
67 | checkData(data);
68 |
69 | var ctx = initCanvas(page, canvasId);
70 | drawChart(ctx);
71 | }
72 | /**
73 | * 初始化Canvas
74 | */
75 | function initCanvas(page, canvasId) {
76 | var ctx = wx.createCanvasContext(canvasId);
77 | var Sys = wx.getSystemInfoSync();
78 |
79 | chartOpt.chartWidth = Sys.windowWidth;
80 | chartOpt.chartHeight = Sys.windowWidth * 0.8;//Canvas组件的宽高比
81 |
82 | chartOpt.legendWidth = dataSet.legend.size * 1.3;
83 | chartOpt.legendHeight = dataSet.legend.size * 0.8;
84 |
85 | chartOpt.top = chartOpt.left = chartOpt.chartSpace;
86 | chartOpt.right = chartOpt.chartWidth - chartOpt.chartSpace;
87 | chartOpt.bottom = chartOpt.chartHeight - chartOpt.chartSpace;
88 |
89 | //3个数字的文字长度
90 | var textWidth = util.mesureText('100', dataSet.xAxis.size);
91 | var legendHeight = dataSet.series.length > 1 ? (chartOpt.legendHeight + chartOpt.chartSpace * 2) : 0;
92 |
93 | chartOpt.axisLeft = chartOpt.left + (dataSet.hideYaxis ? 0 : textWidth + chartOpt.textSpace);
94 | chartOpt.axisBottom = chartOpt.bottom - dataSet.xAxis.size - chartOpt.textSpace - legendHeight;
95 | chartOpt.axisTop = chartOpt.top + dataSet.title.size + chartOpt.textSpace + dataSet.xAxis.size * 2;
96 |
97 |
98 |
99 | //更新页面Canvas的宽度、高度
100 | page.setData({
101 | chartWidth: chartOpt.chartWidth,
102 | chartHeight: chartOpt.chartHeight
103 | });
104 |
105 | return ctx;
106 | }
107 | /**
108 | * 检查并更新图表数据
109 | */
110 | function checkData(data) {
111 |
112 | if (data.title != undefined) {
113 | if (data.title.color != undefined && data.title.color != '') {
114 | dataSet.title.color = data.title.color;
115 | }
116 | dataSet.title.text = data.title.text;
117 |
118 | }
119 | if (data.color != undefined && data.color != [] && data.color.length > 0) {
120 | dataSet.color = data.color;
121 | }
122 | dataSet.xAxis.data = data.xAxis.data;
123 |
124 |
125 | dataSet.series = data.series;
126 |
127 | var value = new Array();
128 | for (var i = 0; i < dataSet.series.length; i++) {
129 | var item = dataSet.series[i];
130 | var itemLenght = item.data.length;
131 | if (itemLenght > chartOpt.barLength) {
132 | chartOpt.barLength = itemLenght;
133 | }
134 | for (var k = 0; k < itemLenght; k++) {
135 | value.push(item.data[k]);
136 | }
137 | if (item.category == 'bar') {
138 | chartOpt.barNum += 1;
139 |
140 | }
141 | if (item.category == 'pie') {
142 | chartOpt.hideXYaxis = true;
143 | for (var k = 0; k < itemLenght; k++) {
144 | chartOpt.chartPieCount += item.data[k];
145 | }
146 | }
147 |
148 | }
149 |
150 | var minNum = Math.min.apply(null, value);
151 | var maxNum = Math.max.apply(null, value);
152 | //计算Y轴刻度尺数据
153 | chartOpt.axisMark = util.calculateY(minNum, maxNum, 5);
154 | }
155 | /**
156 | * 绘制图表
157 | */
158 | function drawChart(ctx) {
159 | drawBackground(ctx);
160 | drawTitle(ctx);
161 | drawLegend(ctx);
162 | if (!chartOpt.hideXYaxis) {
163 | drawXaxis(ctx);
164 | drawYaxis(ctx);
165 | }
166 |
167 | // drawBarChart(ctx);
168 | drawCharts(ctx);
169 | ctx.draw();
170 | }
171 | /**
172 | * 绘制图表背景
173 | */
174 | function drawBackground(ctx) {
175 | if (chartOpt.bgColor != "" && chartOpt.bgColor != "transparent") {
176 | ctx.setFillStyle(chartOpt.bgColor);
177 | ctx.fillRect(0, 0, chartOpt.chartWidth, chartOpt.chartHeight);
178 | }
179 | }
180 | /**
181 | * 绘制标题
182 | */
183 | function drawTitle(ctx) {
184 | var title = dataSet.title;
185 | if (title.text != '') {
186 |
187 | var textWidth = util.mesureText(title.text, title.size);
188 | ctx.setFillStyle(title.color);
189 | ctx.setFontSize(title.size)
190 | ctx.setTextAlign('left');
191 | ctx.fillText(title.text, (chartOpt.chartWidth - textWidth) / 2, chartOpt.top + title.size);
192 | }
193 |
194 | }
195 | /**
196 | * 绘制X轴刻度尺
197 | */
198 | function drawXaxis(ctx) {
199 | //绘制X轴横线
200 | ctx.setLineWidth(0.5);
201 | ctx.setLineCap('round');
202 | ctx.moveTo(chartOpt.axisLeft, chartOpt.axisBottom)
203 | ctx.lineTo(chartOpt.right, chartOpt.axisBottom)
204 | ctx.stroke();
205 |
206 |
207 | var width = (chartOpt.right - chartOpt.axisLeft) / chartOpt.barLength;
208 | var data = dataSet.xAxis.data;
209 | //绘制X轴显示文字
210 | for (var i = 0; i < data.length; i++) {
211 | var textX = (width * (i + 1)) - (width / 2) + chartOpt.axisLeft;
212 | ctx.setFillStyle(dataSet.xAxis.color);
213 | ctx.setFontSize(dataSet.xAxis.size);
214 | ctx.setTextAlign('center');
215 | ctx.fillText(data[i], textX, chartOpt.axisBottom + dataSet.xAxis.size + chartOpt.textSpace);
216 | }
217 | }
218 | /**
219 | * 绘制Y轴刻度尺
220 | */
221 | function drawYaxis(ctx) {
222 |
223 | //绘制Y轴横线
224 | ctx.setLineWidth(0.5);
225 | ctx.setLineCap('round');
226 |
227 | var height = (chartOpt.axisBottom - chartOpt.axisTop) / (chartOpt.axisMark.length - 1);
228 | //绘制Y轴显示数字
229 | for (var i = 0; i < chartOpt.axisMark.length; i++) {
230 | var y = chartOpt.axisBottom - height * i;
231 | if (i > 0) {
232 | ctx.setStrokeStyle(chartOpt.lineColor);
233 | util.drawDashLine(ctx, chartOpt.axisLeft, y, chartOpt.right, y);
234 | }
235 |
236 | if (!dataSet.hideYaxis) {
237 | ctx.setFillStyle(dataSet.xAxis.color);
238 | ctx.setFontSize(dataSet.xAxis.size)
239 | ctx.setTextAlign('right');
240 | ctx.fillText(chartOpt.axisMark[i], chartOpt.axisLeft - chartOpt.textSpace, y + chartOpt.textSpace);
241 | }
242 | }
243 | }
244 |
245 | /**
246 | * 绘制图例
247 | */
248 | function drawLegend(ctx) {
249 | var series = dataSet.series;
250 |
251 | for (var i = 0; i < series.length; i++) {
252 | var names = series[i].name;
253 | var isPie = series[i].category == 'pie';
254 | var textWidth = util.mesureText(isPie?names[0]:names, dataSet.xAxis.size);
255 | var legendWidth = chartOpt.legendWidth + textWidth + chartOpt.chartSpace * 2;
256 | var startX = (chartOpt.chartWidth / 2) - (legendWidth * (isPie ?names.length:series.length)) / 2;
257 |
258 | if (series[i].category == 'pie') {
259 | for (var k = 0; k < names.length; k++) {
260 | var x = startX + legendWidth * k;
261 | var y = chartOpt.bottom - chartOpt.legendHeight;
262 |
263 | ctx.setFillStyle(dataSet.xAxis.color);
264 | ctx.setFontSize(dataSet.legend.size)
265 | ctx.setTextAlign('left');
266 | ctx.fillText(names[k], x + chartOpt.textSpace + chartOpt.legendWidth, chartOpt.bottom);
267 |
268 | var color = getColor(k);
269 | ctx.setFillStyle(color);
270 | ctx.fillRect(x, y + 1, chartOpt.legendWidth, chartOpt.legendHeight);
271 | }
272 | } else {
273 |
274 | var x = startX + legendWidth * i + chartOpt.legendWidth * i;
275 | var y = chartOpt.bottom - chartOpt.legendHeight;
276 |
277 | ctx.setFillStyle(dataSet.xAxis.color);
278 | ctx.setFontSize(dataSet.legend.size)
279 | ctx.setTextAlign('left');
280 | ctx.fillText(series[i].name, x + chartOpt.chartSpace + chartOpt.legendWidth, chartOpt.bottom);
281 |
282 | var color = getColor(i);
283 | ctx.setFillStyle(color);
284 | ctx.setLineWidth(2);
285 | ctx.setStrokeStyle(color);
286 | if (series[i].category == 'bar') {
287 | ctx.fillRect(x, y + 1, chartOpt.legendWidth, chartOpt.legendHeight);
288 | } else if (series[i].category == 'line') {
289 | var lx = x + chartOpt.legendWidth / 2;
290 | var ly = y + chartOpt.legendHeight / 2 + 1;
291 | ctx.beginPath();
292 | ctx.moveTo(x, ly);
293 | ctx.lineTo(x + chartOpt.legendWidth, ly);
294 | ctx.stroke();
295 | ctx.closePath();
296 | drawPoint(ctx, lx, ly, chartOpt.legendHeight / 2, color);
297 | drawPoint(ctx, lx, ly, chartOpt.legendHeight / 4, chartOpt.bgColor);
298 | }
299 | }
300 | }
301 | }
302 | /**
303 | * 绘制数据标签
304 | */
305 | function drawToolTips(ctx, text, x, y, color) {
306 | ctx.setFillStyle(color);
307 | ctx.setFontSize(dataSet.xAxis.size)
308 | ctx.setTextAlign('center');
309 | ctx.fillText(text, x, y);
310 | }
311 | /**
312 | * 画图
313 | */
314 | function drawCharts(ctx) {
315 | var series = dataSet.series;
316 | for (var i = 0; i < series.length; i++) {
317 | var category = series[i].category;
318 | var barWidth = (chartOpt.right - chartOpt.axisLeft) / chartOpt.barLength;
319 | var barHeight = chartOpt.axisBottom - chartOpt.axisTop;
320 | var maxMark = chartOpt.axisMark[chartOpt.axisMark.length - 1];
321 |
322 | if (category == "bar") {
323 | barWidth = barWidth - chartOpt.chartSpace;
324 | drawBarChart(ctx, i, series, barWidth, barHeight, maxMark);
325 | } else if (category == "line") {
326 | drawLineChart(ctx, i, series, barWidth, barHeight, maxMark);
327 | } else if (category == 'pie') {
328 | drawPieChart(ctx, i, series);
329 | }
330 | }
331 | }
332 | /**
333 | * 绘制柱状图
334 | */
335 | function drawBarChart(ctx, i, series, barWidth, barHeight, maxMark) {
336 | var item = series[i];
337 | var itemWidth = barWidth / chartOpt.barNum;
338 |
339 | for (var k = 0; k < item.data.length; k++) {
340 | var itemHeight = barHeight * (item.data[k] / maxMark);
341 | var x = (barWidth * k + chartOpt.axisLeft + k * chartOpt.chartSpace + (chartOpt.chartSpace / 2)) + (i * itemWidth);
342 | var y = chartOpt.axisBottom - itemHeight;
343 | var color = getColor(series.length <= 1 ? k : i);
344 | ctx.setFillStyle(color);
345 | ctx.fillRect(x, y, itemWidth, itemHeight);
346 |
347 | drawToolTips(ctx, item.data[k], x + itemWidth / 2, y - chartOpt.textSpace, color);
348 | }
349 | }
350 | /**
351 | * 绘制折线图
352 | */
353 | function drawLineChart(ctx, i, series, barWidth, barHeight, maxMark) {
354 | var item = series[i];
355 | var color = getColor(i);
356 | ctx.setLineWidth(2);
357 | ctx.setStrokeStyle(color);
358 | ctx.beginPath();
359 | for (var k = 0; k < item.data.length; k++) {
360 | var point = getLinePoint(k, item, barWidth, barHeight, maxMark);
361 | if (k == 0) {
362 | ctx.moveTo(point.x, point.y);
363 | } else {
364 | ctx.lineTo(point.x, point.y);
365 | }
366 | }
367 | ctx.stroke();
368 | ctx.closePath();
369 | for (var k = 0; k < item.data.length; k++) {
370 | var point = getLinePoint(k, item, barWidth, barHeight, maxMark);
371 | drawPoint(ctx, point.x, point.y, 3, color);
372 | drawPoint(ctx, point.x, point.y, 1, chartOpt.bgColor);
373 | drawToolTips(ctx, item.data[k], point.x, point.y - chartOpt.chartSpace, color);
374 | }
375 | }
376 | function getLinePoint(k, item, barWidth, barHeight, maxMark) {
377 | var x = barWidth * k + chartOpt.axisLeft + barWidth / 2;
378 | var y = chartOpt.axisBottom - (barHeight * (item.data[k] / maxMark));
379 | return { x: x, y: y }
380 | }
381 | function drawPoint(ctx, x, y, radius, color) {
382 | ctx.setFillStyle(color);
383 | ctx.beginPath();
384 | ctx.arc(x, y, radius, 0, 2 * Math.PI);
385 | ctx.fill();
386 | ctx.closePath();
387 | }
388 | /**
389 | * 绘制饼图
390 | */
391 | function drawPieChart(ctx, i, series) {
392 | var item = series[i];
393 |
394 | var x = (chartOpt.right - chartOpt.left) / 2 + chartOpt.left;
395 | var radius = (chartOpt.axisBottom - chartOpt.axisTop) / 3;
396 | var y = (chartOpt.axisBottom - chartOpt.axisTop) / 2 + chartOpt.axisTop
397 |
398 | var lastAngel = 0;
399 | for (var k = 0; k < item.data.length; k++) {
400 | var color = getColor(k);
401 | var curAngel = 2 / chartOpt.chartPieCount * item.data[k];
402 | var precent = 100 / chartOpt.chartPieCount * item.data[k];
403 |
404 | drawPieToolTips(ctx, item.data[k] + "(" + Math.round(precent) + "%)", color, x, y, radius, lastAngel, curAngel);
405 |
406 | ctx.setFillStyle(color);
407 | ctx.beginPath();
408 | ctx.moveTo(x, y);
409 | ctx.arc(x, y, radius, (lastAngel - 0.5) * Math.PI, (lastAngel + curAngel - 0.5) * Math.PI);
410 | ctx.fill();
411 | ctx.closePath();
412 | lastAngel += curAngel;
413 |
414 | }
415 | }
416 | /**
417 | * 绘制饼图数据标签
418 | */
419 | function drawPieToolTips(ctx, value, color, x, y, radius, lastAngel, curAngel) {
420 | var textWidth = util.mesureText(value, dataSet.xAxis.size);
421 | var cosc = Math.cos((lastAngel - 0.5 + curAngel / 2) * Math.PI);
422 | var sinc = Math.sin((lastAngel - 0.5 + curAngel / 2) * Math.PI);
423 | var x1 = (radius) * cosc + x;
424 | var y1 = (radius) * sinc + y;
425 |
426 | var x2 = (radius + 20) * cosc + x;
427 | var y2 = (radius + 20) * sinc + y;
428 |
429 | ctx.setFillStyle(color);
430 | ctx.setTextAlign(x2 < x1 ? 'right' : 'left');
431 | ctx.setFontSize(dataSet.xAxis.size);
432 | ctx.setStrokeStyle(color);
433 | ctx.setLineWidth(1);
434 | ctx.beginPath();
435 | ctx.moveTo(x1, y1);
436 | if (x1 >= x && y1 < y) {
437 | ctx.quadraticCurveTo(x2, y2, x2 + 15, y2)
438 | ctx.fillText(value, x2 + 15 + chartOpt.textSpace, y2 + dataSet.xAxis.size / 2);
439 | } else if (x1 >= x && y1 >= y) {
440 | ctx.quadraticCurveTo(x2, y2, x2 + 15, y2)
441 | ctx.fillText(value, x2 + 15 + chartOpt.textSpace, y2 + dataSet.xAxis.size / 2);
442 | } else if (x1 < x && y1 >= y) {
443 | ctx.quadraticCurveTo(x2, y2, x2 - 15, y2)
444 | ctx.fillText(value, x2 - 15 - chartOpt.textSpace, y2 + dataSet.xAxis.size / 2);
445 | } else if (x1 < x && y1 < y) {
446 | ctx.quadraticCurveTo(x2, y2, x2 - 15, y2)
447 | ctx.fillText(value, x2 - 15 - chartOpt.textSpace, y2 + dataSet.xAxis.size / 2);
448 | }
449 | ctx.stroke();
450 | ctx.closePath();
451 | }
452 | /**
453 | * 获取柱状图颜色值,循环渲染
454 | */
455 | function getColor(index) {
456 | var cLength = dataSet.color.length;
457 | if (index >= cLength) {
458 | return dataSet.color[index % cLength];
459 | } else {
460 | return dataSet.color[index];
461 | }
462 | }
463 | /**
464 | * 保存图表为图片
465 | */
466 | function saveCanvans(func) {
467 | wx.canvasToTempFilePath({
468 | canvasId: canvasId,
469 | success: function (res) {
470 | console.log(res.tempFilePath)
471 | // wx.previewImage({
472 | // urls: [res.tempFilePath],
473 | // })
474 | wx.saveImageToPhotosAlbum({
475 | filePath: res.tempFilePath,
476 | success(ress) {
477 | console.log(ress)
478 | }
479 | })
480 | }
481 | })
482 | }
--------------------------------------------------------------------------------