vue使用 zrenderjs绘制坐标轴
首先引入zrender.js
分别绘制y轴,x轴,以及散点。
思路:定义 点对象、x轴对象、y轴对象,在 相对定位对象里面 都引入 ,通过判断 app以及pc端显示,来使用不同的方法。
目前实现:y轴0坐标始终不动,x轴弧线可拖动,放大看。
用于:相对定位图。
(用zrender绘图,其原始坐标在左上角,整体根据 高度 来绘图。)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script type="text/javascript" src="./dist/zrender.min.js"></script>
</head>
<body>
<div id="main" style="width:80vw;height:80vh;padding: 10px;"></div>
<script>
//相对定位图中人员点
class Point {
//人员状态
color = '#2196f3';
//人员名称
name = '';
//人员高度
height = 0;
//人员距离
distant = 0;
//zrender实例
zr = null;
//Circle实例
circle = null;
//axisX实例
axisX = null;
//axisYX\实例
axisY = null;
//构造函数
constructor(data) {
let {
zr,
axisX,
axisY,
height,
distant,
name
} = data;
this.zr = zr; //zrender实例
this.height = height;
this.distant = distant;
this.name = name;
this.axisX = axisX;
this.axisY = axisY;
//绘制
this.draw();
}
draw() {
let coord = this.convertCoord();
this.circle = new zrender.Circle({
style: {
fill: this.color,
text: `${this.name}`,
textFill: this.color,
textPosition: [0, 25]
},
shape: {
cx: coord.x,
cy: coord.y,
r: 10
}
});
this.zr.add(this.circle);
}
update() {
this.draw();
}
/**
* 根据人员高度和距离转换坐标
*/
convertCoord() {
const max = Math.max(...this.axisX.extent); //x轴的最大值
const maxY = Math.max(...this.axisY.extent); //y轴的最大值
const minY = Math.min(...this.axisY.extent); //y轴的最大值
// let totalNumber = max / this.intervalCnt + Math.abs(min) / this.intervalCnt,
// intervalDis = this._height / totalNumber,//坐标轴之间的间隔距离
let r = (this.zr.getHeight() / 2 - this.axisX.textOffset) / Math.sin(this.axisX.angle), //计算弧线半径
intervalDis = (this.zr.getWidth() * this.axisX.scale) / max, //计算单位间隔
totalNumber = maxY + Math.abs(minY) , //计算Y轴条数
intervalDisY = (this.zr.getHeight() - 20) * this.axisY.scale / totalNumber,//Y轴 坐标轴之间的间隔距离
cx = r * (1 - Math.cos(this.axisX.angle)) + this.axisX.offset, //计算初始x坐标
cy = (this.zr.getHeight() / 2 - this.axisX.textOffset), //计算初始y坐标
x = cx + this.distant * intervalDis, //计算x坐标
y = cy - this.height * intervalDisY; //计算y坐标
// console.log('intervalDisY',intervalDisY)
// console.log('计算初始y坐标',cy)
// console.log('计算初始x坐标',cx)
// console.log('计算x坐标',x)
// console.log('计算y坐标',y)
//x坐标超出范围,控制在外面
if (this.distant > max) x = intervalDis * (max + Math.random());
return {
x,
y
};
}
}
//相对定位图X轴对象
class AxisX {
//坐标轴范围
extent = [0, 35];
//坐标轴宽度
_width = 0;
//坐标轴高度
_height = 0;
//坐标轴圆弧的角度(一半)
angle = 0.5;
//坐标轴间隔数
intervalCnt = 5;
//坐标轴间隔距离
intervalDis = 10;
//坐标轴的缩放比例
scale = 1;
//坐标偏移量
offset = 0;
//文字偏移距离
textOffset = 10;
//zrender实例
zr = null;
//构造函数
constructor(zr) {
this.zr = zr; //zrender实例
this._height = zr.getHeight(); //设置坐标轴高度
//绘制
this.draw();
}
//绘制弧线
draw() {
const max = Math.max(...this.extent), //最大值
min = Math.min(...this.extent), //最小值
cy = this.zr.getHeight() / 2 - this.textOffset, //圆心纵坐标
r = cy / Math.sin(this.angle); //弧形半径
this._width = this.zr.getWidth() * this.scale; //设置坐标轴宽度
this.intervalCnt = Math.ceil(5 / this.scale); //设置坐标轴间隔
let intervalDis = this._width / max, //坐标轴之间的间隔距离
chordHalf = r * Math.sin(this.angle), //弦长的一半
cx = this.offset - r * Math.cos(this.angle); //圆心横坐标
// console.log("max", max);
// console.log("min", min);
// console.log("r", r);
// console.log("cy", cy);
// console.log("cx", cx);
// console.log("chordHalfX", chordHalf* 2 );
// console.log("intervalDis", intervalDis);
// console.log("intervalCnt", this.intervalCnt);
// console.log("_width", this._width);
// console.log("angle", this.angle);
// console.log("offset", this.offset);
for (let i = 0; i <= max; i++) {
//每隔intervalCnt绘制一条弧线
if ((min + i) % this.intervalCnt !== 0 && i !== 0) continue;
//构造弧线
let arc = new zrender.Arc({
style: {
text: `${min + i} 米`, //文字内容
textPosition: [0, chordHalf * 2 + this.textOffset] //文字偏移距离
},
shape: {
cx: cx + intervalDis * i, //圆心横坐标
cy: cy, //圆心纵坐标
r: r, //半径
startAngle: -this.angle, //起始弧度
endAngle: this.angle //终止弧度
}
});
this.zr.add(arc);
}
}
//更新图形
update(data) {
let {
offset,
scale
} = data;
if (offset) this.offset = offset; //更新偏移量
if (scale) this.scale = scale; //更新缩放等级
this.draw(); //绘制图形
}
}
//相对定位图Y轴对象
class AxisY {
//坐标轴范围
extent = [-40, 40];
//坐标轴宽度
_width = 0;
//坐标轴高度
_height = 0;
//坐标轴圆弧的角度(一半)
angle = 0;
//坐标轴间隔数
intervalCnt = 4;
//坐标轴间隔距离
intervalDis = 10;
//坐标轴的缩放比例
scale = 1;
//坐标偏移量
offset = 1;
//文字偏移距离
textOffset = 10;
//zrender实例
zr = null;
//构造函数
constructor(zr) {
this.zr = zr; //zrender实例
this._height = zr.getHeight(); //设置坐标轴高度
//绘制
this.draw();
}
//绘制直线
draw() {
const max = Math.max(...this.extent), //最大值
min = Math.min(...this.extent), //最小值
cy = this.zr.getHeight() / 2 - this.textOffset, //圆心纵坐标
r = cy / Math.sin(this.angle), //弧形半径
total = max + Math.abs(min);
this._width = this.zr.getWidth() * this.scale; //设置坐标轴宽度
this._height = (this.zr.getHeight() - 20) * this.scale; //设置坐标轴高度
this.intervalCnt = Math.ceil(4 / this.scale); //设置坐标轴间隔
let chordHalf = r * Math.sin(this.angle) * 2, //弦长的一半
// totalNumber = max / this.intervalCnt + Math.abs(min) / this.intervalCnt,
totalNumber = max + Math.abs(min),
intervalDis = this._height / totalNumber,//坐标轴之间的间隔距离
x1 = 0,
y1 = 0,
y2 = 0,
x2 = this.zr.getWidth();
// console.log("max", max);
// console.log("min", min);
// console.log("x1", x1);
// console.log("y1", y1);
// console.log("x2", x2);
// console.log("y2", y2);
// console.log("坐标轴之间的间隔距离", intervalDis);
// console.log("设置坐标轴间隔", this.intervalCnt);
// console.log("_width", this._width);
// console.log("this._height", this._height);
// console.log("chordHalf",chordHalf);
// console.log("(total / (this.intervalCnt) + 1)",(total / (this.intervalCnt) + 1))
for (let i = 0; i <= total; i++) {
if ((min + i) % this.intervalCnt !== 0 && i !== 0) continue;
console.log('min',min)
let arc = new zrender.Line({
style: {
text: `${-( min + i)} 米`, //文字内容
// text: `${ this.intervalCnt* i} 米`,
textPosition: [4, -10] //文字偏移距离
},
shape: {
x1:x1,
y1:y1 + intervalDis * i,
y2:y1 + intervalDis * i,
x2:x2
}
});
this.zr.add(arc);
}
}
//更新图形
update(data) {
let {
offset,
scale
} = data;
if (offset) this.offset = offset; //更新偏移量
if (scale) this.scale = scale; //更新缩放等级
this.draw(); //绘制图形
}
}
//相对定位图对象
class XDDWChart {
//zrender实例对象
zr = null;
//dom元素id
eleId = '';
//相对定位图X轴
axisX = null;
//相对定位图Y轴
axisY = null;
//坐标轴的缩放比例
scale = 1;
//坐标偏移量 (整体移动)
offset = 0;
//相对定位图中的点集合
pointArr = [];
//触碰开始时x坐标
_touchStartX = 0;
//触碰开始时y坐标
_touchStartY = 0;
//触碰开始时两指坐标距离
_touchStartDis = 0;
//临时偏移量
_tempOffset = 0;
//构造函数
constructor(eleId) {
//初始化zrender画布
this.zr = zrender.init(document.getElementById(eleId));
//设置dom元素id
this.eleId = eleId;
//相对定位图X轴实例
this.axisX = new AxisX(this.zr);
//相对定位图X轴实例
this.axisY = new AxisY(this.zr);
//添加鼠标、手势监听事件
this.addEventListener();
}
//绘制图
draw() {
this.axisX.draw();
this.axisY.draw();
}
//更新图
update() {
this.zr.clear();
this.axisX.update({
offset: this.offset,
scale: this.scale
});
this.axisY.update({
offset: this.offset,
scale: this.scale
});
this.pointArr.forEach(v => {
v.update();
})
}
//添加点
addPoints(data) {
data.forEach(v => {
let p = new Point({
...v,
zr: this.zr,
axisX: this.axisX,
axisY: this.axisY
});
this.pointArr.push(p);
});
}
//判断是否是PC端
isPC() {
let userAgentInfo = navigator.userAgent;
let Agents = ["Android", "iPhone",
"SymbianOS", "Windows Phone",
"iPad", "iPod"
];
let flag = true;
for (let v = 0; v < Agents.length; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
}
//添加事件监听
addEventListener() {
this.isPC() ? this.addPcEventListener() : this.addAppEventListener();
}
//添加PC端事件监听
addPcEventListener() {
document.getElementById(this.eleId).addEventListener('mousewheel', (e) => {
console.log("监听mousewheel", e);
let scaleTemp = this.scale;
if (e.wheelDelta > 0) {
scaleTemp += 0.1;
} else {
scaleTemp -= 0.1;
}
// console.log("scaleTemp", scaleTemp);
if (scaleTemp > 8 || scaleTemp < 1) return;
this.scale = Number(scaleTemp.toFixed(1));
console.log("scale", this.scale);
this.update();
});
// 行为过程:按下之后,移动,再抬起
document.getElementById(this.eleId).addEventListener("mousedown", (eve) => {
console.log("mousedown", eve);
// 获取按下的坐标,在按下的事件对象身上
let e1 = eve || window.event;
this._touchStartX = e1.clientX;
this._touchStartY = e1.clientY;
this._tempOffset = this.offset;
// 因为移动事件在抬起的时候,被删除,所以提前起名
const move = (eve) => {
console.log("mousemove");
let e = eve || window.event;
// 计算元素要移动的真正的距离:为鼠标相对于页面的坐标减去按下时相对于元素的坐标
let l = e.clientX - this._touchStartX;
let t = e.clientY - this._touchStartY;
console.log("相对于元素的坐标 x,y:", l, t);
//TODO this._tempOffset + l
this.offset = this._tempOffset + l;
this.update();
}
// 为了防止移动过快,鼠标在某一瞬间离开元素,移动事件加给页面
document.addEventListener("mousemove", move);
// 抬起时,删除移动,删除抬起
document.addEventListener("mouseup", up)
function up() {
console.log("mouseup");
document.removeEventListener("mousemove", move)
document.removeEventListener("mouseup", up)
}
})
}
//添加addAppEventListener()
addAppEventListener() {
document.getElementById(this.eleId).addEventListener('touchstart', (eve) => {
console.log("touchstart", eve)
// 获取按下的坐标,在按下的事件对象身上
let e1 = eve || window.event;
e1.preventDefault();
this._tempOffset = this.offset;
if (e1.touches.length >= 2) { //双指缩放
this._touchStartDis = this.getDistance(e1.touches[0], e1.touches[1]);
console.log("touchStart.dis", this._touchStartDis);
} else {
this._touchStartX = e1.touches[0].clientX;
this._touchStartY = e1.touches[0].clientY;
}
// 因为移动事件在抬起的时候,被删除,所以提前起名
const move = (eve) => {
// console.log("touchmove", eve);
let e = eve || window.event;
// e.preventDefault();
if (e.touches.length >= 2) { //双指缩放
let nowDis = this.getDistance(e.touches[0], e.touches[1]);
let scaleTemp = this.scale;
if (nowDis > this._touchStartDis) {
scaleTemp += 0.1;
} else {
scaleTemp -= 0.1;
}
if (scaleTemp > 8 || scaleTemp < 1) return;
this.scale = Number(scaleTemp.toFixed(1));
console.log("this.scale", this.scale);
} else {
// 计算元素要移动的真正的距离:为鼠标相对于页面的坐标减去按下时相对于元素的坐标
let l = e.touches[0].clientX - this._touchStartX;
let t = e.touches[0].clientY - this._touchStartY;
console.log("相对于元素的坐标 x,y:", l, t);
this.offset = this._tempOffset + l;
}
this.update();
}
// 为了防止移动过快,鼠标在某一瞬间离开元素,移动事件加给页面
document.addEventListener("touchmove", move);
// 抬起时,删除移动,删除抬起
document.addEventListener("touchend", up)
function up() {
console.log("mouseup");
document.removeEventListener("touchmove", move)
document.removeEventListener("touchend", up)
}
})
}
//计算距离
getDistance(p1, p2) {
let x = p2.pageX - p1.pageX,
y = p2.pageY - p1.pageY;
return Math.sqrt((x * x) + (y * y));
}
}
let xddwChart = new XDDWChart('main');
let points = [{
height: 0,
distant: 0,
name: '张三'
}, {
height: 20,
distant: 12.5,
name: '李四'
}, {
height: 20,
distant: 2,
name: '王五'
}, {
height: -20,
distant: 5,
name: '赵柳'
}, {
height: -4,
distant: 3,
name: '丁一'
}, {
height: -8,
distant: 5,
name: '丁2'
}, {
height: -14,
distant: 2.2,
name: '丁3'
}, {
height: -8,
distant: 2,
name: '丁4'
}, {
height: -4,
distant: 1,
name: '丁5'
}]
xddwChart.addPoints(points);
</script>
</body>
</html>