效果展示

引入颜色生成器 colormap
- 初始化vue环境,引入colormap依赖
npm i colormap
- 导入
const colormap = require('colormap')
- 初始化colormap
created() {
// 此处生成 150 个渐变色
this.colormap = colormap({
colormap: 'jet',
nshades: 150,
format: 'rba',
alpha: 1
});
},
制作瀑布图图例
- 画布样式调整
<!-- 瀑布图图例 -->
<view>
<canvas canvas-id="legend"
id="legend"
style="margin-left:60rpx; margin-right: 5rpx; width: 10px; height: 150px;">
</canvas>
</view>
- 初始化图例
initLegend() {
// 构建像素点数据
let colorList = [];
for (let i = this.colormap.length - 1; i >= 0; i--) {
const color = this.colormap[i];
colorList.push(color[0])
colorList.push(color[1])
colorList.push(color[2])
colorList.push(255)
}
// 图像像素点数据,一维数组,每四项表示一个像素点的rgba
const data = new Uint8ClampedArray(colorList)
// 声明画布对象
const legend = uni.createCanvasContext('legend');
// 将像素点绘制在画布上
// 绘制时宽度和高度注意保持一致,否则会绘制失败
uni.canvasPutImageData({
canvasId: 'legend',
x: 0,
y: 0,
width: 1,
data: data,
success(res) {
// 当前画布指定区域的内容导出生成指定大小的图片
uni.canvasToTempFilePath({
canvasId: 'legend',
x: 0,
y: 0,
width: 1,
heigth: CANVAS_HEIGHT,
quality: 1,
success(res) {
// 绘制图像至画图
legend.drawImage(res.tempFilePath, 0, 0, 10, CANVAS_HEIGHT)
legend.draw();
}
})
},
fail(res) {
console.info("失败标识:", res)
}
}, this)
},
绘制瀑布图
1. 布局代码
<view>
<canvas canvas-id="soldierCanvas"
style="height: 150px; width: 300px;"
ref="myCanvas">
</canvas>
</view>
- 逻辑代码 2.1 取色值
/**
* 取色值
* data: 对应y轴数据
* outMin: 颜色下限取值
* outMax: 颜色上限取值
* lineUpLimit: y轴曲线上限值
*/
squeeze(data, outMin, outMax) {
if (data <= 0) {
return outMin
} else if (data >= this.lineUpLimit) {
return outMax
} else {
return Math.round(data / this.lineUpLimit * outMax)
}
},
2.2 绘制瀑布图
async drawWaterfallCurve(points) {
const that = this;
// 调整采样点
points = this.adjustSampleData(points);
let dataLength = points.length;
// 获取像素点集合
let arr = [];
for (let i = 0; i < dataLength; i++) {
const cindex = this.squeeze(points[i], 0, 149)
const color = this.colormap[cindex]
arr.push(color[0])
arr.push(color[1])
arr.push(color[2])
arr.push(255)
}
let data = new Uint8ClampedArray(arr)
points = null;
arr = [];
// 1. 截取画布全图像
uni.canvasToTempFilePath({
canvasId: 'soldierCanvas',
x: 0,
y: 0,
width: CANVAS_WIDTH,
height: CANVAS_HEIGHT,
quality: 0.5,
success(res) {
// 截取图后,重新绘制到画图上并使其向下移动 1像素 距离
that.soldierCanvas.drawImage(res.tempFilePath, 0, 1, CANVAS_WIDTH, CANVAS_HEIGHT);
that.soldierCanvas.draw(true, () => {
res = null;
// 绘制 1像素 高的图像在画图起始位置
uni.canvasPutImageData({
canvasId: 'soldierCanvas',
x: 0,
y: 0,
width: CANVAS_WIDTH,
data: data,
success(res) {
data = null;
},
fail(res) {
console.info("失败标识:", res)
}
})
})
},
fail(res) {
console.info("获取图像失败标识:", res)
}
})
},
2.3 数据抽样算法
仅供参考
因画图宽度像素点有限,对应展示数据有限,需对上报数据进行处理
画布整体左右拖动逻辑暂无,所以需要对数据采样进行处理
adjustSampleData(points) {
// 1. 获取振动曲线纵坐标范围
let yAxisStart = this.yStartLocation ? this.yStartLocation : 0;
let yAxisEnd = this.yEndLocation ? this.yEndLocation : CANVAS_WIDTH;
// // 排除异常情况
if (yAxisStart >= yAxisEnd) {
yAxisStart = 0;
yAxisEnd = CANVAS_WIDTH;
}
// 2. 截取
let renderPoints = points.slice(yAxisStart, yAxisEnd);
// 3. 判断点数是否为 300(匹配画布像素点)
let renderPointLength = renderPoints.length;
let remainder = 0;
if (renderPointLength < CANVAS_WIDTH) {
// 待补位数
remainder = CANVAS_WIDTH - (renderPointLength % CANVAS_WIDTH);
// while循环次数
let loopCount = Math.floor(remainder / renderPointLength) + 1;
// while循环控制条件
let _loopCount = 1;
// 已补点数
let remainder_count = 0;
while (_loopCount <= loopCount) {
for (let i = 0; i < renderPoints.length; i += _loopCount) {
if (remainder_count !== remainder) {
// 在当前位置之后插入元素
renderPoints.splice(i + 1, 0, renderPoints[i]);
i++;
remainder_count++;
continue;
}
}
_loopCount++;
}
} else if (renderPointLength > CANVAS_WIDTH) {
// ============= 方式一 抽样取整 =================
// 间隔取点数
let interval = Math.floor(renderPointLength / CANVAS_WIDTH);
// 剩余数
let surplus = renderPointLength % CANVAS_WIDTH;
let surplusCount = 0;
let temp_arr = [];
for (let i = 0; i < renderPoints.length; i += interval) {
if (surplusCount !== surplus) {
let tempArr = renderPoints.slice(i, i + interval + 1);
let temp_max = Math.max(...tempArr);
temp_arr.push(temp_max);
i++;
surplusCount++;
continue;
}
if (interval === 1) {
temp_arr.push(renderPoints[i]);
} else {
let tempArr = renderPoints.slice(i, i + interval);
let temp_max = Math.max(...tempArr);
temp_arr.push(temp_max);
}
}
renderPoints = temp_arr;
// ================= 方式二 补整抽样 ===================
// 待补0位数
// remainder = CANVAS_WIDTH - (renderPointLength % CANVAS_WIDTH);
// let remainder_count = 0;
// for (let i = 0; i < renderPoints.length; i++) {
// // 补0
// if (remainder_count === remainder) {
// break;
// }
// renderPoints.splice(i + 1, 0, 0);
// i++;
// remainder_count++;
// }
// // 取点间隔
// let interval = renderPoints.length / CANVAS_WIDTH;
// let temp_arr = [];
// for (let j = 0; j < renderPoints.length; j += interval) {
// let temp_max = 0;
// for (let k = 0; k < interval - 1; k++) {
// temp_max = Math.max(renderPoints[j + k], renderPoints[j + k + 1])
// }
// temp_arr.push(temp_max);
// }
// renderPoints = temp_arr;
// temp_arr = [];
}
return renderPoints;
},