SVG 取得滑鼠座標資訊

Describe how to get point in the viewport coordinate system and point in the SVG coordinate system. In addition, How to transform between them.

by Po-Jung Chen

HTML

<svg id="svg" width="600" height="300" viewBox="0 0 600 300">
  <rect x="0" y="0" fill="none" stroke="#EB7B2D" stroke-width="2" width="600" height="300" />
  <circle cx="300" cy="150" r="5" fill="#EB7B2D" />
</svg>
<!-- 取得滑鼠座標資訊 -->
<div id="info">
  <p id="offset">offset</p>
  <p id="client">client</p>
  <p id="showSvg">svg</p>
</div>
<!-- 設定 viewBox 用 -->
<div class="manipulate">
  <div id="currentViewBox"></div>
  <input type="number" name="viewBox-x-min" id="viewBox-x-min" placeholder="x-min">
  <input type="number" name="viewBox-y-min" id="viewBox-y-min" placeholder="y-min">
  <input type="number" name="viewBox-width" id="viewBox-width" placeholder="width">
  <input type="number" name="viewBox-height" id="viewBox-height" placeholder="height">
  <button id="setViewBoxBtn">設定</button>
</div>

CSS

svg {
  border: 1px solid #333;
}

#info {
  box-sizing: border-box;
  border: 1px solid #DEDEDE;
  padding: 10px;
  width: 600px;
}

#info p {
  margin: 0;
  padding: 1px;
}


/* Other CSS Style reference from Pure CSS */

.manipulate input {
  width: 100px;
  padding: .5em .6em;
  display: inline-block;
  border: 1px solid #ccc;
  box-shadow: inset 0 1px 3px #ddd;
  border-radius: 4px;
  vertical-align: middle;
  box-sizing: border-box;
}

button {
  font-family: "微軟正黑體";
  font-size: 100%;
  padding: .5em .6em;
  color: #444;
  color: rgba(0, 0, 0, .8);
  border: 1px solid #999;
  border: transparent;
  background-color: #E6E6E6;
  text-decoration: none;
  border-radius: 2px;
}
button:hover{
  background-image: linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));
}

JavaScript

//	取得當前的 viewBox 資訊
function showViewBox() {
  const currentViewBox = document.getElementById('currentViewBox')
  let viewBox = svg.getAttribute('viewBox')
  currentViewBox.textContent = `現在的 viewBox 為:(${viewBox})`
}

//	讓使用者可以設定 viewBox
function setViewBox() {
  //	取得使用者輸入的 viewBox 值
  const xMin = document.querySelector('[name="viewBox-x-min"]')
  const yMin = document.querySelector('[name="viewBox-y-min"]')
  const width = document.querySelector('[name="viewBox-width"]')
  const height = document.querySelector('[name="viewBox-height"]')
  let viewBox = `${xMin.value} ${yMin.value} ${width.value} ${height.value}`

  //	設定 SVG viewBox 屬性值
  svg.setAttribute('viewBox', viewBox)

  //	顯示最新的 viewBox 資訊
  showViewBox()
}
/* 
 *	開始:取得游標座標資訊
 */
function reportCurrentPoint(e) {
  //	選取 HTML 各元素
  const info = document.getElementById('info')
  const offset = document.getElementById('offset')
  const client = document.getElementById('client')
  const showSvg = document.getElementById('showSvg')

  //	取得 viewport 座標系統中的 offset 和 client 座標值,並顯示於視窗上
  offset.textContent = `offset (${e.offsetX}, ${e.offsetY})`
  client.textContent = `client (${e.clientX}, ${e.clientY})`

  //	建立 SVG 座標點(0, 0)
  const clientPoint = svg.createSVGPoint()
    //	取得 CTM
  const CTM = svg.getScreenCTM()
    //  將 SVG 座標點的 x, y 設成 client(x, y)
  clientPoint.x = e.clientX
  clientPoint.y = e.clientY
    //	將 client 的座標點轉成 SVG 座標點
  SVGPoint = clientPoint.matrixTransform(CTM.inverse())
    //  將資訊顯示於視窗上
  showSvg.textContent = `svg (${SVGPoint.x.toFixed(0)}, ${SVGPoint.y.toFixed(0)})`
} //	結束:取得游標座標資訊

const svg = document.getElementById('svg')
const setViewBoxBtn = document.getElementById('setViewBoxBtn')
svg.addEventListener('mousemove', reportCurrentPoint)
setViewBoxBtn.addEventListener('click', setViewBox)
showViewBox()