趁着清明放假的空闲,将之前写过的代码整理了一下,发现了一个比较有意思的项目,该项目其实也比较简单,就是利用Canvas的各种原生API在图像中绘制一些基础图形,以及一些图形的更改操作。顺便借此项目复习一下Canvas基础。
基本实现功能
- 图片的放大缩小和拖拽
- 绘制多边形并修改
- 绘制矩形并修改
- 绘制线段(暂无修改)
- 绘制箭头(暂无修改)
图片的放大和缩小
- drawImage() 图片的绘制
// 绘制图片
drawImage = () => {
if (this.$imageDom) {
try {
this._context.drawImage(
this.$imageDom, // 图片元素
0, // 开始剪切的 x 坐标位置
0, // 开始剪切的 y 坐标位置
this.imageOriginWidth, //被剪切图像的宽度
this.imageOriginHeight, //被剪切图像的宽度
this.offsetX, // 在画布上放置图像的 x 坐标位置
this.offsetY, //在画布上放置图像的 y 坐标位置
this.imageOriginWidth * this.currentRatio, //要使用的图像的宽度
this.imageOriginHeight * this.currentRatio //要使用的图像的高度
);
return Promise.resolve();
} catch (e) {
console.log(e)
}
}
}
- 计算画布上放置图像的坐标位置
getOffset = (pointX, pointY, scale, ratio, dir) => {
if (pointX && pointY) {
// 获取图片
const width = this.imageOriginWidth * (scale - ratio * dir);
const height = this.imageOriginHeight * (scale - ratio * dir);
const x = this.offsetX;
const y = this.offsetY;
if ((pointX < x) && (pointY >= y && pointY <= y + height)
) {
// 1
this.offsetY = pointY - (pointY - this.offsetY) / (scale - ratio * dir) * scale;
} else if ((pointX < x) && pointY >= y + height) {
// 2
this.offsetX = x;
this.offsetY = (ratio * dir * this.imageOriginHeight - this.offsetY) * (-1);
} else if (pointX > x + width && (pointY >= y && pointY <= y + height)) {
// 5
this.offsetY = pointY - (pointY - this.offsetY) / (scale - ratio * dir) * scale;
this.offsetX = (ratio * dir * this.imageOriginWidth - this.offsetX) * (-1);
} else if ((pointX >= x && pointX <= x + width) && (pointY > y + height)) {
// 3
this.offsetX = pointX - (pointX - this.offsetX) / (scale - ratio * dir) * scale;
this.offsetY = (ratio * dir * this.imageOriginHeight - this.offsetY) * (-1);
} else if (pointY < y && (pointX >= x && pointX <= x + width)) {
// 7
this.offsetX = pointX - (pointX - this.offsetX) / (scale - ratio * dir) * scale;
this.offsetY = y;
} else if (pointX > x + width && (pointY > y + height)) {
// 4
this.offsetX = (ratio * dir * this.imageOriginWidth - this.offsetX) * (-1);
this.offsetY = (ratio * dir * this.imageOriginHeight - this.offsetY) * (-1);
} else if (pointX > x + width && pointY < y) {
// 6
this.offsetY = y;
this.offsetX = (ratio * dir * this.imageOriginWidth - this.offsetX) * (-1);
} else if (pointX < x && pointY < y) {
// 8
this.offsetX = x;
this.offsetY = y;
} else {
// 9
this.offsetX = pointX - (pointX - this.offsetX) / (scale - ratio * dir) * scale;
this.offsetY = pointY - (pointY - this.offsetY) / (scale - ratio * dir) * scale;
}
}
}
多边形的绘制(线的绘制、箭头的绘制)
- moveTo()
- lineTo()
- closePath()
根据上述原生API绘制线,多边形的绘制即为坐标点大于2的路径的闭合曲线。
判断点是否在多边形内
- isPointInPath
绘制当前闭合路径,根据该函数判断点是否在路径内。
判断点是否在线上
由于上述方法是判断点是否在路径内,就无法判断点是否在线上了,我采用的方法如下:
- 先判断点的坐标是否在线的坐标范围内,如果不在则点不在线上
- 如果1满足,则根据直线公式 y = kx + b 通过线段已知两点坐标求出斜率k和偏移值b;
- 根据线段的斜率和垂直线的斜率 k * k1 = -1,求出垂直线斜率,再根据当前点计算出通过该点的垂直线公式 y = (-1/k)x + m;
- 根据垂直相交线公式求出交点坐标,根据两点(当前点和交点坐标)求出线段距离,如果该距离小于误差范围值,则认为点在线上,反之则认为不在线上。
function isPointInLinePath(line, dot, threshold) {
const x2 = dot[0] ? dot[0] : 0;
const y2 = dot[1] ? dot[1] : 0;
const p1 = line[0];
const p2 = line[1];
const [p1X, p1Y] = p1;
const [p2X, p2Y] = p2;
let l = threshold + 1;
let x = 0;
let y = 0;
if (
((p1X <= x2 && x2 <= p2X) || (p2X <= x2 && x2 <= p1X)) &&
((p1Y <= y2 && y2 <= p2Y) || (p2Y <= y2 && y2 <= p1Y))
) {
const slop = _calSlop(p1, p2); // 计算斜率
const verSlop = _calVerticalSlop(slop); // 计算垂直斜率
const x1 = p1[0] || 0;
const y1 = p1[1] || 0;
if (slop != 0 && verSlop != 0) {
if (y2 == slop * x2 + y1 - slop * y1) {
// 点在当前直线上
x = x2;
y = y2;
} else {
x = parseFloat(
(y2 - y1 + slop * x1 - verSlop * x2) / (slop - verSlop)
);
y = parseFloat(slop * x + y1 - slop * x1);
}
} else {
// 垂直于x轴或平行于x轴
if (x1 == p2X) {
// 平行于y轴
x = x1;
y = y2;
} else if (y1 == p2Y) {
// 平行于x轴
x = x2;
y = y1;
}
}
if (
(p1X <= x && x <= p2X) ||
(p2X <= x && x <= p1X && (p1Y <= y && y <= p2Y)) ||
(p2Y <= y && y <= p1Y)
) {
l = parseInt(Math.sqrt(Math.pow(x2 - x, 2) + Math.pow(y2 - y, 2)));
}
}
if (l < threshold) {
// 说明是点在线上
return true;
}
return false;
}
绘制矩形
- rect()
根据原生API绘制图形,修改时的判断方式同多边形的判断。
实现原理就介绍到这里,更多详细信息请去https://github.com/jdkwky/my-vue-example/tree/master/src/view/canvas中了解~