微信小程序開發交流微信羣 ↓ 掃碼入羣,備註微信小程序
承接微信小程序開發。掃碼加微信。
效果圖
之後會更新成組件
利用三次貝賽爾曲線,將折線圖變曲線圖
前提-搭框架
let curve = {
mW: 360, //canvas寬
mH: 250, //canvas高
mCenter: 180, //中心點
hCenter: 125, //中心點
curveText: [ //x軸
['自主型', '(AU)'],
['技術型', '(TF)'],
['挑戰型', '(CH)'],
['管理型', '(GM)'],
['服務型', '(SV)'],
['創造型', '(FC)'],
['安全型', '(SE)'],
['生活型', '(LS)'],
],
curveColText: ['100', '80', '60', '40', '20', '0'], //y軸
curveData: [60, 80, 48, 72, 65, 48, 72, 65],
points: []
};
Page({
data: {
curve: {
display: 'block'
},
},
onLoad() {
let curveCtx = wx.createCanvasContext('curveCanvas');
this.drawCurve(curveCtx);
},
// 曲線
drawCurve(curveCtx) {
const that = this;
this.drawLineBg(curveCtx); //畫橫縱座標框架
this.drawLineColText(curve.curveColText, curveCtx); //繪製縱座標文字
this.drawLineRowText(curve.curveText, curveCtx); //繪製橫座標文字
this.drawCurveCtx(curve.curveData, curveCtx); //繪製曲線
curveCtx.draw();
},
});
第一步-繪製橫座標
// 繪製橫座標
drawLineBg(lineCtx) {
lineCtx.setStrokeStyle("#eeeeee");
for (let i = 0; i < 6; i++) {
lineCtx.moveTo(curve.mCenter - 160, 50 + 30 * i);
lineCtx.lineTo(curve.mCenter + 160, 50 + 30 * i);
lineCtx.stroke();
}
},
第二步-繪製縱座標文字
// 繪製縱座標文字
drawLineColText(mData, lineCtx) {
lineCtx.beginPath();
lineCtx.setFillStyle("#999999");
for (let i = 0; i < 6; i++) {
lineCtx.fillText(mData[i], 10, 55 + 30 * i);
}
},
第三步-繪製橫座標文字
// 繪製橫座標文字
drawLineRowText(mData, lineCtx) {
lineCtx.setFillStyle("#555555");
lineCtx.setFontSize(12); //設置字體
for (let i = 0; i < 8; i++) {
lineCtx.fillText(mData[i][0], 10 + i * 45, 220);
lineCtx.fillText(mData[i][1], 15 + i * 45, 235);
}
},
第四步-繪製曲線
drawCurveCtx(mData, lineCtx) {
curve.points = [];
for (let i = 0; i < 8; i++) {
curve.points.push({
x: 29.5 + i * 45,
y: 200 - mData[i] / 100 * 150
});
}
this.drawCurvePath(curve.points, lineCtx);
},
// 繪製曲線背景
drawCurvePath(path, ctx) {
var point = getControlPoint(path);
ctx.beginPath();
const grd = ctx.createLinearGradient(150, 0, 150, 200);
grd.addColorStop(0, 'rgba(51,136,255,0.5)');
grd.addColorStop(0.7, 'rgba(51,136,255,0.5)');
grd.addColorStop(1, 'rgba(255,255,255,0)');
ctx.setFillStyle(grd);
ctx.setGlobalAlpha(0.5);
ctx.beginPath();
ctx.moveTo(29, 200);
ctx.lineTo(curve.points[0].x, curve.points[0].y);
var int = 0;
for (var i = 0; i < curve.points.length; i++) {
if (i == 0) {
ctx.quadraticCurveTo(point[0].x, point[0].y, curve.points[1].x, curve.points[1].y);
int = int + 1;
} else if (i < curve.points.length - 2) {
ctx.bezierCurveTo(point[int].x, point[int].y, point[int + 1].x, point[int + 1].y, curve.points[i + 1].x, curve.points[i + 1].y);
int += 2;
} else if (i == curve.points.length - 2) {
ctx.quadraticCurveTo(point[point.length - 1].x, point[point.length - 1].y, curve.points[curve.points.length - 1].x, curve.points[curve.points.length - 1].y);
}
}
ctx.lineTo(curve.points[curve.points.length - 1].x, 200);
ctx.fill();
ctx.closePath();
this.drawCurveSign(point, ctx)
},
// 繪製點加線
drawCurveSign(point, ctx) {
// 繪製線
ctx.beginPath();
ctx.setStrokeStyle("#3388FF");
ctx.setGlobalAlpha(1);
ctx.setLineWidth(1.5);
var int = 0;
ctx.moveTo(curve.points[0].x, curve.points[0].y);
for (var i = 0; i < curve.points.length; i++) {
if (i == 0) {
ctx.quadraticCurveTo(point[0].x, point[0].y, curve.points[1].x, curve.points[1].y);
int = int + 1;
} else if (i < curve.points.length - 2) {
ctx.bezierCurveTo(point[int].x, point[int].y, point[int + 1].x, point[int + 1].y, curve.points[i + 1].x, curve.points[i + 1].y);
int += 2;
} else if (i == curve.points.length - 2) {
ctx.quadraticCurveTo(point[point.length - 1].x, point[point.length - 1].y, curve.points[curve.points.length - 1].x, curve.points[curve.points.length - 1].y);
}
}
ctx.stroke();
// 繪製點
ctx.beginPath();
ctx.setGlobalAlpha(1);
for (let i = 0; i < curve.points.length; i++) {
ctx.beginPath();
ctx.arc(curve.points[i].x, curve.points[i].y, 5, 0, 2 * Math.PI);
ctx.setFillStyle("#3388FF");
ctx.fill();
ctx.closePath();
}
},
// 折線變曲線
let Vector2 = function (x, y) {
this.x = x;
this.y = y;
};
Vector2.prototype = {
"length": function () {
return Math.sqrt(this.x * this.x + this.y * this.y);
},
"normalize": function () {
let inv = 1 / this.length() == Infinity ? 0 : 1 / this.length();
return new Vector2(this.x * inv, this.y * inv);
},
"add": function (v) {
return new Vector2(this.x + v.x, this.y + v.y);
},
"multiply": function (f) {
return new Vector2(this.x * f, this.y * f);
},
"dot": function (v) {
return this.x * v.x + this.y * v.y;
},
"angle": function (v) {
return Math.acos(this.dot(v) / (this.length() * v.length())) * 180 / Math.PI;
}
};
function getControlPoint(path) {
let rt = 0.3;
let count = path.length - 2;
let arr = [];
for (let i = 0; i < count; i++) {
let a = path[i];
let b = path[i + 1];
let c = path[i + 2];
let v1 = new Vector2(a.x - b.x, a.y - b.y);
let v2 = new Vector2(c.x - b.x, c.y - b.y);
let v1Len = v1.length();
let v2Len = v2.length();
let centerV = v1.normalize().add(v2.normalize()).normalize();
let ncp1 = new Vector2(centerV.y, centerV.x * -1);
let ncp2 = new Vector2(centerV.y * -1, centerV.x);
if (ncp1.angle(v1) < 90) {
let p1 = ncp1.multiply(v1Len * rt).add(b);
let p2 = ncp2.multiply(v2Len * rt).add(b);
arr.push(p1, p2);
} else {
let p1 = ncp1.multiply(v2Len * rt).add(b);
let p2 = ncp2.multiply(v1Len * rt).add(b);
arr.push(p2, p1);
}
}
return arr;
};
全部代碼
let curve = {
mW: 360, //canvas寬
mH: 250, //canvas高
mCenter: 180, //中心點
hCenter: 125, //中心點
curveText: [ //x軸
['自主型', '(AU)'],
['技術型', '(TF)'],
['挑戰型', '(CH)'],
['管理型', '(GM)'],
['服務型', '(SV)'],
['創造型', '(FC)'],
['安全型', '(SE)'],
['生活型', '(LS)'],
],
curveColText: ['100', '80', '60', '40', '20', '0'], //y軸
curveData: [60, 80, 48, 72, 65, 48, 72, 65],
points: []
};
Page({
data: {
curve: {
display: 'block'
},
},
onLoad() {
let curveCtx = wx.createCanvasContext('curveCanvas');
this.drawCurve(curveCtx);
},
// ***************************曲線開始-職業傾向維度解析********************************
// 曲線
drawCurve(curveCtx) {
const that = this;
this.drawLineBg(curveCtx); //畫橫縱座標框架
this.drawLineColText(curve.curveColText, curveCtx); //繪製縱座標文字
this.drawLineRowText(curve.curveText, curveCtx); //繪製橫座標文字
this.drawCurveCtx(curve.curveData, curveCtx); //繪製曲線
curveCtx.draw();
},
drawCurveCtx(mData, lineCtx) {
curve.points = [];
for (let i = 0; i < 8; i++) {
curve.points.push({
x: 29.5 + i * 45,
y: 200 - mData[i] / 100 * 150
});
}
this.drawCurvePath(curve.points, lineCtx);
},
// 繪製曲線背景
drawCurvePath(path, ctx) {
var point = getControlPoint(path);
ctx.beginPath();
const grd = ctx.createLinearGradient(150, 0, 150, 200);
grd.addColorStop(0, 'rgba(51,136,255,0.5)');
grd.addColorStop(0.7, 'rgba(51,136,255,0.5)');
grd.addColorStop(1, 'rgba(255,255,255,0)');
ctx.setFillStyle(grd);
ctx.setGlobalAlpha(0.5);
ctx.beginPath();
ctx.moveTo(29, 200);
ctx.lineTo(curve.points[0].x, curve.points[0].y);
var int = 0;
for (var i = 0; i < curve.points.length; i++) {
if (i == 0) {
ctx.quadraticCurveTo(point[0].x, point[0].y, curve.points[1].x, curve.points[1].y);
int = int + 1;
} else if (i < curve.points.length - 2) {
ctx.bezierCurveTo(point[int].x, point[int].y, point[int + 1].x, point[int + 1].y, curve.points[i + 1].x, curve.points[i + 1].y);
int += 2;
} else if (i == curve.points.length - 2) {
ctx.quadraticCurveTo(point[point.length - 1].x, point[point.length - 1].y, curve.points[curve.points.length - 1].x, curve.points[curve.points.length - 1].y);
}
}
ctx.lineTo(curve.points[curve.points.length - 1].x, 200);
ctx.fill();
ctx.closePath();
this.drawCurveSign(point, ctx)
},
// 繪製點加線
drawCurveSign(point, ctx) {
// 繪製線
ctx.beginPath();
ctx.setStrokeStyle("#3388FF");
ctx.setGlobalAlpha(1);
ctx.setLineWidth(1.5);
var int = 0;
ctx.moveTo(curve.points[0].x, curve.points[0].y);
for (var i = 0; i < curve.points.length; i++) {
if (i == 0) {
ctx.quadraticCurveTo(point[0].x, point[0].y, curve.points[1].x, curve.points[1].y);
int = int + 1;
} else if (i < curve.points.length - 2) {
ctx.bezierCurveTo(point[int].x, point[int].y, point[int + 1].x, point[int + 1].y, curve.points[i + 1].x, curve.points[i + 1].y);
int += 2;
} else if (i == curve.points.length - 2) {
ctx.quadraticCurveTo(point[point.length - 1].x, point[point.length - 1].y, curve.points[curve.points.length - 1].x, curve.points[curve.points.length - 1].y);
}
}
ctx.stroke();
// 繪製點
ctx.beginPath();
ctx.setGlobalAlpha(1);
for (let i = 0; i < curve.points.length; i++) {
ctx.beginPath();
ctx.arc(curve.points[i].x, curve.points[i].y, 5, 0, 2 * Math.PI);
ctx.setFillStyle("#3388FF");
ctx.fill();
ctx.closePath();
}
},
// 畫橫座標
drawLineBg(lineCtx) {
lineCtx.setStrokeStyle("#eeeeee");
for (let i = 0; i < 6; i++) {
lineCtx.moveTo(curve.mCenter - 160, 50 + 30 * i);
lineCtx.lineTo(curve.mCenter + 160, 50 + 30 * i);
lineCtx.stroke();
}
},
// 繪製橫座標文字
drawLineRowText(mData, lineCtx) {
lineCtx.setFillStyle("#555555");
lineCtx.setFontSize(12); //設置字體
for (let i = 0; i < 8; i++) {
lineCtx.fillText(mData[i][0], 10 + i * 45, 220);
lineCtx.fillText(mData[i][1], 15 + i * 45, 235);
}
},
// 繪製縱座標文字
drawLineColText(mData, lineCtx) {
lineCtx.beginPath();
lineCtx.setFillStyle("#999999");
for (let i = 0; i < 6; i++) {
lineCtx.fillText(mData[i], 10, 55 + 30 * i);
}
},
});
// 折線變曲線
let Vector2 = function(x, y) {
this.x = x;
this.y = y;
};
Vector2.prototype = {
"length": function() {
return Math.sqrt(this.x * this.x + this.y * this.y);
},
"normalize": function() {
let inv = 1 / this.length() == Infinity ? 0 : 1 / this.length();
return new Vector2(this.x * inv, this.y * inv);
},
"add": function(v) {
return new Vector2(this.x + v.x, this.y + v.y);
},
"multiply": function(f) {
return new Vector2(this.x * f, this.y * f);
},
"dot": function(v) {
return this.x * v.x + this.y * v.y;
},
"angle": function(v) {
return Math.acos(this.dot(v) / (this.length() * v.length())) * 180 / Math.PI;
}
};
function getControlPoint(path) {
let rt = 0.3;
let count = path.length - 2;
let arr = [];
for (let i = 0; i < count; i++) {
let a = path[i];
let b = path[i + 1];
let c = path[i + 2];
let v1 = new Vector2(a.x - b.x, a.y - b.y);
let v2 = new Vector2(c.x - b.x, c.y - b.y);
let v1Len = v1.length();
let v2Len = v2.length();
let centerV = v1.normalize().add(v2.normalize()).normalize();
let ncp1 = new Vector2(centerV.y, centerV.x * -1);
let ncp2 = new Vector2(centerV.y * -1, centerV.x);
if (ncp1.angle(v1) < 90) {
let p1 = ncp1.multiply(v1Len * rt).add(b);
let p2 = ncp2.multiply(v2Len * rt).add(b);
arr.push(p1, p2);
} else {
let p1 = ncp1.multiply(v2Len * rt).add(b);
let p2 = ncp2.multiply(v1Len * rt).add(b);
arr.push(p2, p1);
}
}
return arr;
};