小程序中canvas绘图
程序员文章站
2024-02-11 17:18:46
...
- 小程序项目github地址:https://github.com/yangdongMC/Eyepetizer.git
- 并非原生小程序写法,使用wepy框架进行开发,但wepy并没有把canvas绘图衔接的很好,还是用到小程序部分api
- 例如:template中的标签还是使用小程序提供的,js中的声明还是使用const ctx = wx.createCanvasContext(‘id’),与原生的canvas绘制时api有点语法上的区别,但功能都一样,如很多前面加了set,不支持’#333’,改成’#cdcdcd’
- 如果项目中要使用动画,简单可考虑canvas,但是复杂了很好的替代品为gif,虽然gif大,但可以微调减帧,减帧了也大,放服务器上、请求、再显示,完美解决。
- 详情如下:代码全贴
<style>
.radar-canvas {
width: 100%;
height: 300px;
}
</style>
<template>
<view>
<view>
<text>雷达图:</text>
<canvas class="radar-canvas" canvas-id="radar" disable-scroll="{{false}}"></canvas>
</view>
<view>
<Bar></Bar>
</view>
</view>
</template>
<script>
import wepy from 'wepy';
//注意:组件引入时,路径必须放在components文件夹下,不然放在pages下,会找不到
import bar from '../components/bar';
/**
canvas常用属性
beginPath 起始一条路径,或重置当前路径
strokeStyle 设置或返回颜色、渐变或模式
lineTo 创建到达位置x,y的一条线
colsePath 创建从当前点回到起始点的路径
stroke 绘制已定义的路径
fillStyle 填充绘制的颜色
fill 填充当前绘图(颜色)
*/
//获取媒介宽度
const windowW = wx.getSystemInfoSync().windowWidth;
const centerPointX = windowW / 2;
const centerPointY = centerPointX;
const offset = 3.3;
export default class Radar extends wepy.page {
config = {
navigationBarTitleText: 'charts'
};
components = {
Bar: bar
};
data = {
//建立雷达图需要的数据
radarData: [
{ desc: 'React', value: '0.6' },
{ desc: 'Angular', value: '0.5' },
{ desc: 'Vue', value: '0.8' },
{ desc: 'Wepy', value: '0.5' },
{ desc: 'Canvas', value: '0.3' }
],
//定义半径,减去的部分为文字留的空位
radius: centerPointX - this.rpx(200)
};
onLoad() {}
//wepy组件,只在page页面中存在的生命周期函数
onShow() {
const ctx = wx.createCanvasContext('radar');
const sideNum = this.radarData.length;
//定义角度
const angle = Math.PI * 2 / sideNum;
this.drawPolygon(ctx, sideNum, angle);
this.drawRib(ctx, sideNum, angle);
this.addTags(ctx, this.radarData, sideNum, angle);
this.addDataPoint(ctx, this.radarData, sideNum, angle);
this.linePoint(ctx, this.radarData, sideNum, angle);
ctx.draw();
}
//draw polygon 绘制多边形
drawPolygon(ctx, sideNum, angle) {
ctx.setStrokeStyle('rgba(83,139,81)');
//获取单位半径
const r = this.radius / 5;
for (let i = 0; i < 5; i++) {
ctx.beginPath();
//当前半径
let currentR = r * (i + 1);
for (let j = 0; j < sideNum; j++) {
//Math.PI/3.3是为了设置偏移量,可以自行设置
const x =
centerPointX + currentR * Math.cos(angle * j + Math.PI / offset);
const y =
centerPointY + currentR * Math.sin(angle * j + Math.PI / offset);
ctx.lineTo(x, y);
}
ctx.setLineDash([2, 2]); //虚线
ctx.closePath();
ctx.stroke();
}
}
//line point
linePoint(ctx, radarData, sideNum, angle) {
ctx.setStrokeStyle('rgba(83,139,81)');
ctx.beginPath();
for (let i = 0; i < sideNum; i++) {
const x =
centerPointX +
this.radius *
Math.cos(angle * i + Math.PI / offset) *
radarData[i].value;
const y =
centerPointY +
this.radius *
Math.sin(angle * i + Math.PI / offset) *
radarData[i].value;
ctx.lineTo(x, y);
}
ctx.closePath();
ctx.setFillStyle('rgba(0,91,51,0.2)');
ctx.fill();
ctx.stroke();
}
//add dataPoint
addDataPoint(ctx, radarData, sideNum, angle) {
for (var i = 0; i < sideNum; i++) {
var x =
centerPointX +
this.radius * Math.cos(angle * i + Math.PI / 3.3) * radarData[i].value;
var y =
centerPointY +
this.radius * Math.sin(angle * i + Math.PI / 3.3) * radarData[i].value;
ctx.beginPath();
ctx.arc(x, y, 3, 0, 2 * Math.PI);
ctx.setFillStyle('rgb(0, 91, 51)');
ctx.fill();
ctx.closePath();
}
}
addTags(ctx, radarData, sideNum, angle) {
ctx.setFontSize(this.rpx(30));
ctx.setFillStyle('rgb(95, 153, 32)');
//确定文本位置,可以根据微信小程序文档中的具体方法来设置
for (var i = 0; i < sideNum; i++) {
var x = parseInt(
centerPointX + this.radius * Math.cos(angle * i + Math.PI / 3.3)
);
var y = parseInt(
centerPointY + this.radius * Math.sin(angle * i + Math.PI / 3.3)
);
var center = parseInt(centerPointX);
var centerY = parseInt(centerPointY);
// console.log('x' + x, 'y' + y, 'center' + center, 'centerY' + centerY);
if (x < center && y < centerY) {
ctx.setTextAlign('left');
ctx.fillText(radarData[i].desc, x - this.rpx(120), y);
} else if (x - this.rpx(20) > center && y < centerY) {
ctx.setTextAlign('right');
ctx.fillText(radarData[i].desc, x + this.rpx(120), y);
} else if (y > centerY) {
ctx.setTextAlign('center');
ctx.fillText(radarData[i].desc, x, y + this.rpx(80));
} else {
ctx.setTextAlign('center');
ctx.fillText(radarData[i].desc, x, y - this.rpx(40));
}
}
}
//draw rib
drawRib(ctx, sideNum, angle) {
ctx.setStrokeStyle('#cdcdcd');
ctx.beginPath();
for (var i = 0; i < sideNum; i++) {
var x = centerPointX + this.radius * Math.cos(angle * i + Math.PI / 3.3);
var y = centerPointY + this.radius * Math.sin(angle * i + Math.PI / 3.3);
ctx.moveTo(centerPointX, centerPointY);
ctx.lineTo(x, y);
}
ctx.closePath();
ctx.stroke();
}
//定义公共返回方法
rpx(num) {
return Number(Number(windowW / 750 * num).toFixed(2));
}
}
</script>
<style>
.bar-canvas {
width: 100%;
height: 500px;
}
</style>
<template>
<view>
<text>柱状图</text>
<view>
<canvas class="bar-canvas" canvas-id="bar"></canvas>
</view>
</view>
</template>
<script>
import wepy from 'wepy';
/**
可以参考原生绘制https://www.cnblogs.com/linxin/p/6892389.html
*/
//获取媒介的宽
const windowW = wx.getSystemInfoSync().windowWidth;
export default class bar extends wepy.page {
config = {
navigationBarTitleText: 'bar'
};
onLoad() {
//这里就不能放在onShow中进行初始化了,因为onShow只渲染当前page页面中存在的生命周期函数,而本组件是被当做子组件显示,所以它自己没有对应的可视化page页面,而是借助父页面显示
const ctx = wx.createCanvasContext('bar');
//定义数据源
const source = [
{ city: '北京', number: 345, color: 'red' },
{ city: '上海', number: 645, color: 'green' },
{ city: '广州', number: 545, color: 'pink' },
{ city: '深圳', number: 945, color: 'purple' }
];
//定义轴线配置
const x = 50,
y = 250; //原点坐标
const xWidth = 30;
const yWidth = 20;
//定义矩形配置
const rectX = 0;
const rectY = 0;
const rectWidth = 30;
const rectHeight = 20;
//逐步调用绘制方法
this.darwAxis(ctx, x, y);
this.darwRect(ctx, source, xWidth, yWidth);
this.darwScale(ctx, source, yWidth);
ctx.draw();
}
//绘制坐标轴
darwAxis(ctx, x, y) {
console.log(ctx);
ctx.setStrokeStyle('orange');
ctx.save();
ctx.translate(x, y);
ctx.beginPath();
//X
ctx.moveTo(0, 0);
ctx.lineTo(windowW - 90, 0);
ctx.lineTo(windowW - 90 - 10, -10);
ctx.moveTo(windowW - 90, 0);
ctx.lineTo(windowW - 90 - 10, 10);
//Y
ctx.moveTo(0, 0);
ctx.lineTo(0, -20 * 11);
ctx.lineTo(-10, -20 * 11 + 10);
ctx.moveTo(0, -20 * 11);
ctx.lineTo(10, -20 * 11 + 10);
ctx.stroke();
}
//绘制矩形
darwRect(ctx, source, xWidth, yWidth) {
ctx.beginPath();
ctx.setTextAlign('center');
ctx.setTextBaseline('top');
ctx.setFontSize('15');
for (let i = 0; i < source.length; i++) {
let item = source[i];
console.log(item);
ctx.setFillStyle(item.color);
ctx.fillRect(
i * 2 * xWidth + xWidth - 20,
-item.number / 100 * yWidth,
(windowW - 90) / 9,
item.number / 100 * yWidth
);
ctx.fillText(item.city, -20 + i * 2 * xWidth + xWidth + xWidth / 2, 10);
}
}
//绘制刻度
darwScale(ctx, source, yWidth) {
ctx.setTextAlign('end');
ctx.setTextBaseline('middle');
for (let i = 0; i <= 10; i++) {
ctx.moveTo(0, -i * yWidth);
ctx.lineTo(10, -i * yWidth);
ctx.fillText(i * 100, -10, -i * yWidth);
}
ctx.setStrokeStyle('orange');
ctx.stroke();
}
}
</script>
上一篇: C#获取图片文件扩展名的方法
下一篇: C#读取csv格式文件的方法