拖拽編寫SVG圖形化工具(二)

房東家的貓發表於2020-10-28

getAttributesNs/setAttributesNs

element.setAttributeNS(namespace,name,value)
  • namespace 是指定屬性的名稱空間的一個字串。
  • name 是標識要設定的屬性的一個字串。
  • value 是新屬性的所需字串值。

新增/或者查詢一個新屬性或更改具有給定名稱空間和名稱的一個屬性的值。

setAttribute()是DOM 1函式。setAttributeNS()是DOM 2函式

果屬性沒有定義的名稱空間字首,則第一個引數必須為null。您可以使用setAttribute(),但是為了保持一致性,建議堅持使用setAttributeNS()

應用demo

程式思路

首先我們看看程式碼對映

 // 按下
  box.onmousedown = function (event) {
    console.log(1);
    // 移動
    box.onmousemove=function () {
      console.log(2);
    }
    // 抬起
    box.onmouseup=function () {
      console.log(3);
    }
  }

通過point拿到所有circle 圖形的原點

line拿到直線的dom和path的dom

限制座標的範圍, 由於circle的半徑有一定長度,所以減去5做一定的限制

 maxX = container.offsetWidth - 5
 maxY = container.offsetHeight - 5, 
// 滑鼠範圍限制
  function PointM(x, y) {
    return {
      x: Math.max(0, Math.min(maxX, x)),
      y: Math.max(0, Math.min(maxY, y))
    }
  }
 m = PointM(e.pageX, e.pageY);

當滑鼠點在path上可以通過fill 進行切換背景顏色

+circle[i].getAttributeNS(null, 'cx')

要知道我們通過getAttributeNS 拿到的是字串所以要進行轉化

['p1', 'p2', 'c1', 'c2'].includes(id)

是為了保證按在是個點上面,然後後面的id容易報錯

整體的邏輯就是

滑鼠按下開始的時候,記錄當前的座標,拿到點選的那個dom

後續滑鼠移動的時候, 當前的座標-開始的座標的差值=本來應該指向的座標

並且移動的時候要設定一定的範圍,以便更新點不能離開當前的範圍

滑鼠離開的時候,關閉移動/鬆開的事件

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    #container {
      width: 100%;
      height: 500px;
      background-color: #fff;
      margin: 0 auto;
      text-align: center;
    }

    path {
      stroke-width: 8;
      stroke: #1E1935;
      stroke-linecap: round;
      fill: none;
    }

    circle {
      stroke-width: 2;
      stroke: #ef2d56;
      fill: #fff;
    }

    circle:hover {
      fill: #ef2d56;
      cursor: move;
    }

    line {
      stroke-width: 1;
      stroke: #999;
      stroke-linecap: round;
      stroke-dasharray: 5, 3;
    }

    path {
      stroke-width: 2;
      stroke: #1E1935;
      stroke-linecap: round;
      /* fill: none; */
    }

    path.fill {
      fill: #1E1935
    }
  </style>
</head>
<body>
<div style="text-align: center">
  <pre id="code" style="font-size: 40px;font-family: Consolas;">code</pre>
</div>
<div id="container" class="C">
  <svg id="svg" viewBox="0 0 499 499" width="500" height="500"
       preserveAspectRatio="xMidYMid meet">
    <g id="main">
      <circle id="p1" cx="100" cy="250" r="16"/>
      <circle id="p2" cx="400" cy="250" r="16"/>

      <circle id="c1" cx="100" cy="100" r="8"/>
      <circle id="c2" cx="400" cy="100" r="8"/>

      <line id="l1" x1="100" y1="250" x2="100" y2="100"/>
      <line id="l2" x1="400" y1="250" x2="400" y2="100"/>

      <path id="path" d="M100,250 C100,100 400,100 400,250"/>
    </g>
  </svg>
</div>
<script>
  let svg = document.getElementById('svg'),
    container = document.getElementById('container')
  let point = {}, line = {}
    , maxX = container.offsetWidth - 5
    , maxY = container.offsetHeight - 5,
    fill = false,
    code = document.getElementById('code'),
    // 狀態
    drap = null,
    dPonit;

  function init() {
    let circle = svg.getElementsByTagName('circle');
    for (let i = 0; i < circle.length; i++) {
      point[circle[i].getAttributeNS(null, 'id')] = {
        x: +circle[i].getAttributeNS(null, 'cx'),
        y: +circle[i].getAttributeNS(null, 'cy'),
      }
    }
    line.l1 = svg.getElementById('l1')
    line.l2 = svg.getElementById('l2')
    line.path = svg.getElementById('path')
    svg.onmousedown = document.onmousemove = document.onmouseup = Drag
    svg.ontouchstart = document.ontouchmove = document.ontouchend = Drag
  }

  // 滑鼠範圍限制
  function PointM(x, y) {
    return {
      x: Math.max(0, Math.min(maxX, x)),
      y: Math.max(0, Math.min(maxY, y))
    }
  }


  function Drag(e) {
    e.stopPropagation()
    let t = e.target, id = t.id, et = e.type, m = PointM(e.pageX, e.pageY);
    // 滑鼠按在path區域
    if (!drap && id === 'path' && et === 'mousedown') {
      fill = !fill;
      t.setAttributeNS(null, 'class', (fill ? 'fill' : ''))
      DragGet();
    }
    // 滑鼠按在開始
    // typeof(point[id]!==undefined) 意思就是要按在這幾個點上
    if (!drap && ['p1', 'p2', 'c1', 'c2'].includes(id) && (et === 'mousedown' || et === 'touchstart')) {
      drap = t;
      dPonit = m
    }
    // 移動的時候
    if (drap && ['p1', 'p2', 'c1', 'c2'].includes(id) && (et === 'mousemove' || et === 'touchmove')) {
      console.log(id);
      // 當前的座標-開始的座標等於差值
      point[id].x += m.x - dPonit.x;
      point[id].y += m.y - dPonit.y;
      if (point[id].x < maxX && point[id].y < maxY) {
        id = drap.id;
        dPonit = m;
        drap.setAttributeNS(null, "cx", point[id].x);
        drap.setAttributeNS(null, "cy", point[id].y);
      }
      DragGet()
    }

    // 鬆開的時候
    if (drap && (et === "mouseup" || et === "touchend")) {
      drap = null;
    }
  }

  // 更新資料
  function DragGet() {
    // line1
    line.l1.setAttributeNS(null, 'x1', point.p1.x);
    line.l1.setAttributeNS(null, 'y1', point.p1.y);
    line.l1.setAttributeNS(null, 'x2', point.c1.x);
    line.l1.setAttributeNS(null, 'y2', point.c1.y);
    // line2
    line.l2.setAttributeNS(null, 'x1', point.p2.x)
    line.l2.setAttributeNS(null, 'y1', point.p2.y)
    line.l2.setAttributeNS(null, 'x2', point.c2.x)
    line.l2.setAttributeNS(null, 'y2', point.c2.y)
    let d = `M ${point.p1.x},${point.p1.y} C ${point.c1.x},${point.c1.y} ${point.c2.x},${point.c2.y} ${point.p2.x},${point.p2.y}${fill ? 'Z' : ''}`
    line.path.setAttributeNS(null, 'd', d)
    code.textContent = d;
  }

  init()

</script>
</body>
</html>

相關文章