問題
已知:from ,to 兩點的座標,如何求兩點連線的旋轉角度?
可以通過餘弦定理求解三個角的度數。具體說明如下:
在三角形中,已知邊A、B、C, 且A、B、C所對的內角分別是a、b、c, 則:
- cosa=[B²+C²-A²]/(2BC)
- cosb=[A²+C²-B²]/(2AC)
- cosc=[A²+B²-C²]/(2AB)
然後利用反三角函式求角度:
- a = arccos(cosa)
- b = arccos(cosb)
- c = arccos(cosc)
程式碼實現如下:
// 求角a的度數
// 1.求出三角形的三條邊
const a = to.y - from.y
const b = to.x - from.x
const c = Math.sqrt(a * a + b * b) // 勾股定理 c^2 = a^2 + b^2
// 2.求角a的cos值
const cosA = (b*b + c*c - a*a)/(2*b*c)
// 3.利用反cos求角a的度數
let rotate = Math.acos(cosA) * (180 / Math.PI) // 角度 = 弧度 * 180/π
// 4.處理正負號
to.y < from.y && (rotate = -rotate)
三角函式、反三角函式、角度與弧度互轉說明:
const cosv = Math.cos(val) // val 為弧度值
const val = Math.acos(cosv) // 結果 val 為弧度值,沒有正負
// 角度轉弧度:弧度 = 角度 * (π/180)
// 弧度轉角度:角度 = 弧度 * (180/π)
案例
svg 實現畫一條帶箭頭的邊,滑鼠hover,邊及箭頭變色並顯示文字。
react 實現如下:
import React, { useState } from 'react'
const link = {
from: {
x: 100,
y: 100
},
to: {
x: 300,
y: 50
},
}
const index = () => {
const [linkHover, setLinkHover] = useState()
const linkMouseEnter = () => {
const { from, to } = link
const a = to.y - from.y
const b = to.x - from.x
const c = Math.sqrt(a * a + b * b)
const cosA = (b*b + c*c - a*a)/(2*b*c) // 求夾角a的角度
let rotate = Math.acos(cosA) * (180 / Math.PI) // 角度 = 弧度 * 180/π
to.y < from.y && (rotate = -rotate)
setLinkHover({
x: (from.x + to.x) / 2,
y: (from.y + to.y) / 2,
rotate,
})
}
const linkMouseLeave = () => {
setLinkHover(undefined)
}
return (
<div>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<g>
<defs>
<marker id="triangle" markerUnits="strokeWidth" markerWidth="5" markerHeight="4" refX="4" refY="2" orient="auto" fill="#67c23a">
<path d="M 0 0 L 5 2 L 0 4 L 1 2 z" />
</marker>
<marker id="triangle-hover" markerUnits="strokeWidth" markerWidth="5" markerHeight="4" refX="4" refY="2" orient="auto" fill="red">
<path d="M 0 0 L 5 2 L 0 4 L 1 2 z" />
</marker>
</defs>
<line x1={link.from.x} y1={link.from.y} x2={link.to.x} y2={link.to.y}
strokeLinecap="round" stroke={linkHover ? 'red' : '#67c23a'} stroke-width="2"
style={{markerEnd: linkHover ? 'url(#triangle-hover)' : 'url(#triangle)'}}
/>
{/* 增加 hover 邊界,容易操作 */}
<line x1={link.from.x} y1={link.from.y} x2={link.to.x} y2={link.to.y}
stroke="red" stroke-opacity="0.2" stroke-width="12"
onMouseEnter={linkMouseEnter}
onMouseLeave={linkMouseLeave}
style={{cursor: 'pointer'}}
/>
</g>
{linkHover ? (
// 42 為文字寬度的一半,8 為文字高度的一半 svg transform="rotate(旋轉角度 旋轉中心x,旋轉中心y)" 預設旋轉中心為svg左上角
<text x={linkHover.x - 42} y={linkHover.y - 8} fill="red" transform={`rotate(${linkHover.rotate} ${linkHover.x},${linkHover.y})`}>點選刪除連線</text>
) : null}
</svg>
</div>
)
}
export default index
未經允許不得轉讓。