功能需求:「用戶繪製一條線,而後鏡像出另一條線。」canvas
下面是我實現的過程,怕之後要用,就記下來了。bash
首先對問題的描述轉化了一下:「求p(x, y)點, 關於直線p1(x1, y1), p2(x2, y2)鏡像 獲得P(Px, Py)」 。動畫
帶着這個問題開始思考,也在網上找了一些數學計算的公式。ui
先求關於點p1,p2的直線方程。(直線的通常方程Ax + By + C = 0
)spa
關於直線p1,p2對稱有如下幾種形式:3d
關於X軸的對稱code
(p1.x = p2.x) 可知 直線(p1 p2) 與X軸垂直,獲得直線方程 x = p1.x
;cdn
關於Y軸的對稱blog
(p1.y = p2.y) 可知 直線(p1 p2) 與Y軸垂直,獲得直線方程 y = p1.y
;ip
關於某直線(p1, p2)的對稱
(p1.x != p2.x) && (p1.y != p2.y) 帶入兩點式 (y - p1.y) / (p2.y - p1.y) = (x - p1.x) / (p2.x - p1.x)
直線(p1, p2)的斜率爲 k = (p2.y - p1.y) / (p2.x - p1.x)
獲得直線方程:y = k * (x - p1.x) + p1.y
;
求計算(p.x, p.y)關於直線Ax + By + C = 0
對稱的點:
Px = p.x - 2 * A (A * p.x + B * p.y + C) / (A^2 + B^2);
Py = p.y - 2 * B (A * p.x + B * p.y + C) / (A^2 + B^2);
複製代碼
關於X軸的對稱
有直線方程 x - p1.x = 0
得 A = 1, B = 0, C = -p1.x
,帶入對稱點計算公式獲得
Px = 2 * p1.x - p.x;
Py = p.y;
複製代碼
關於Y軸的對稱
有直線方程 y - p1.y = 0
得 A = 0, B = 1, C = -p1.y
,帶入對稱點計算公式獲得
Px = p.x;
Py = 2 * p1.y - p.y;
複製代碼
關於某直線(p1, p2)的對稱
有直線方程 y = k * (x - p1.x) + p1.y
得 A = k, B = -1, C = -k * p1.x + p1.y
,帶入對稱點計算公式獲得
Px = p.x - 2 * k * ( k * p.x - p.y + (-k * p1.x + p1.y)) / (Math.pow(k, 2) + 1);
Py = p.y - 2 * -1 * ( k * p.x - p.y + (-k * p1.x + p1.y)) / (Math.pow(k, 2) + 1);
複製代碼
點的公式都獲得了,剩下的就是canvas繪製了。canvas的繪製這裏就很少講,直接寫個demo直觀點。
// 這裏我繪製三條軸的起始點和終點(兩點肯定一條直線),便於觀察繪製的圖形。
let canvasWidth = ctx.canvas.width;
let canvasHeight = ctx.canvas.height;
let axleX = {
start: {
x: canvasWidth,
y: canvasHeight / 2
},
end: {
x: 0,
y: canvasHeight / 2
}
};
let axleY = {
start: {
x: canvasWidth / 2,
y: 0
},
end: {
x: canvasWidth / 2,
y: canvasHeight
}
};
let axle = {
start: {
x: canvasWidth,
y: 0
},
end: {
x: 0,
y: canvasHeight
}
};
function drawAxle () {
ctx.lineWidth = 1;
ctx.lineJoin = ctx.lineCap = 'round';
ctx.strokeStyle = '#000';
ctx.beginPath();
// x
ctx.moveTo(axleX.start.x, axleX.start.y);
ctx.lineTo(axleX.end.x, axleX.end.y);
ctx.stroke();
// y
ctx.beginPath();
ctx.moveTo(axleY.start.x, axleY.start.y);
ctx.lineTo(axleY.end.x, axleY.end.y);
ctx.stroke();
// 對角
ctx.beginPath();
ctx.moveTo(axle.start.x, axle.start.y);
ctx.lineTo(axle.end.x, axle.end.y);
ctx.stroke();
};
drawAxle();
複製代碼
分別關於X軸、Y軸、任意線(這裏對角線爲例子)
計算對稱點(核心)
由上述獲得公式獲得。
function calcSymmetryPoint (p1, p2, p) {
if (p1.x == p2.x) {
// 關於Y軸鏡像
return {
x: 2 * p1.x - p.x,
y: p.y
}
} else if (p1.y == p2.y) {
// 關於X軸鏡像
return {
x: p.x,
y: 2 * p1.y - p.y
}
}
// 關於任意直線鏡像
let k1 = (p2.y - p1.y) / (p2.x - p1.x);
let x = p.x - 2 * k1 * ( k1 * p.x - p.y + (-k1 * p1.x + p1.y))/ (Math.pow(k1, 2) + 1);
let y = p.y - 2 * -1 * ( k1 * p.x - p.y + (-k1 * p1.x + p1.y))/ (Math.pow(k1, 2) + 1);
return {
x: x,
y: y
}
}
複製代碼
繪製
僞代碼以下:
...
function mousemoveHandler (event) {
// 獲得須要鏡像的點(p.x, p.y)
let x = e.clientX - (w - canvasWidth) / 2;
let y = e.clientY - (h - canvasHeight) / 2;
// 獲得鏡像以後點的(Px,Py)
calcSymmetryPoint({x: axleX.start.x, y: axleX.start.y}, {x: axleX.end.x, y: axleX.end.y}, {x: x, y: y});
calcSymmetryPoint({x: axleY.start.x, y: axleY.start.y}, {x: axleY.end.x, y: axleY.end.y}, {x: x, y: y});
calcSymmetryPoint({x: axle.start.x, y: axle.start.y}, {x: axle.end.x, y: axle.end.y}, {x: x, y: y});
// 繪製..
// ctx.beginPath();
// ctx.moveTo(...);
// ctx.lineTo(...);
// ctx.stroke();
}
...
複製代碼
效果演示以下:
這裏只是重點描述一下怎麼計算鏡像點的座標,(計算的方式還有矩陣)咱們能夠用這個來作不少有趣的效果動畫,另外也能夠拓展到3d空間裏去作鏡像。
斜率: zh.wikipedia.org/wiki/%E6%96…
直線方程:zh.wikipedia.org/wiki/%E7%9B…
直線方程:baike.baidu.com/item/%E7%9B…
my_blog: www.flowers1225.com/