Skip to content
Advertisement

Is there a way to translate a nested element with transform=”translate(mouseX,mouseY)” in JS?

Edit: Code:

jQuery.noConflict()
const turnEnum = ["Noughts", "Crosses"]
var turn = Math.round(Math.random()) ? turnEnum[0] : turnEnum[1];
var mouseX;
var mouseY;
var snapX;
var snapY;

jQuery(document).ready(function() {
  document.getElementById("turn-indicator").innerHTML = turn + "' turn!";
});

window.onload = function() {
  document.getElementById("game-board").addEventListener('mousemove', e => {
    var offset = jQuery("#game-board").offset();
    mouseX = Math.round(e.pageX - offset.left);
    mouseY = Math.round(e.pageY - offset.top);
    snapX = calcSnap(mouseX, 100);
    snapY = calcSnap(mouseY, 100);
    // document.getElementById("snap-icon-x-container").transform.baseVal.initialize(document.getElementById("snap-icon-x").viewportElement.createSVGTransform.setTranslate("mouseX, mouseY"));

    document.getElementById("snap-icon-x-container").setAttribute("transform", `translate(${mouseX}, ${mouseY})`);
  })
};


function calcSnap(val, gridSize) {
  var snap_candidate = gridSize * Math.round(val / gridSize);
  if (Math.abs(val - snap_candidate) < 50) {
    return snap_candidate;
  } else {
    return 0;
  }
}

function afterplay() {
  jQuery("#title-screen").remove();
  jQuery("#after-play").removeClass("invisible");
}

function play() {
  jQuery("#title-screen").fadeOut(300, afterplay);
}
#snap-icon-x {
  transform-origin: 3.5px 3.5px;
  transform: scale(10);
  -ms-transform: scale(10);
  -webkit-transform: scale(10);
  z-index: 10000;
}

#snap-icon-circle {
  transform-origin: -2px -2px;
  transform: scale(5);
  -ms-transform: scale(5);
  -webkit-transform: scale(5);
  z-index: 10000;
}

#game-board {
  z-index: 5;
}

#snap-icon-x-container {
  position: relative;
}

#snap-icon-circle-container {
  position: relative;
}
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
  <link rel="stylesheet" href="style.css">

  <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css">
  <script src="script.js" type="text/javascript"></script>
  <title>Noughts and Crosses</title>
</head>

<body>
  <div id="game-container" class="border border-2 rounded-3 game-container d-flex flex-column mt-4">
    <div id="title-screen" class="d-flex flex-column">
      <h1 class="p-4 d-flex justify-content-center user-select-none">Noughts and Crosses</h1>
      <button class="p-4 col-6 mb-5 mx-auto btn btn-outline-dark btn-lg" id="playButton" onclick="play()">Play</button>
    </div>
    <div class="d-flex justify-content-center invisible p-5 flex-column" id="after-play">
      <h1 class="mx-auto user-select-none" id="turn-indicator">{}' turn!</h1>
      <svg width="300" height="300" xmlns="http://www.w3.org/2000/svg" id="game-board" class="w-auto mx-auto">
                <line stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel" id="svg_1" y2="300" x2="100"
                    y1="0" x1="100" stroke="#9b9b9b" fill="none" />
                <line stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel" id="svg_2" y2="300" x2="200"
                    y1="0" x1="200" stroke="#9b9b9b" fill="none" />
                <line stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel" id="svg_3" y2="100" x2="0"
                    y1="100" x1="300" stroke="#9b9b9b" fill="none" />
                <line stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel" id="svg_4" y2="200" x2="0"
                    y1="200" x1="300" stroke="#9b9b9b" fill="none" />
                <svg id="snap-icon-x-container" transform="translate(0, 0)">
                    <path
                        d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"
                        id="snap-icon-x" />
                </svg>
      <svg id="snap-icon-circle-container" transform="translate(0, 0)">
                    <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"
                        id="snap-icon-circle" class="invisible" />
                </svg>
      </svg>
    </div>
  </div>





  <footer class="navbar fixed-bottom d-flex justify-content-center fs-5 user-select-none">Made by circles.png.</footer>

  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous"></script>
</body>

</html>

note: similar but not a duplicate of Creating a new SVGTransform object to append to SVGTransformList because I can’t comment xD

and I want to move an SVG in an SVG to my mouse position. The problem is, the #snap-icon-x-container element appears to have no viewportElement. In Creating a new SVGTransform object to append to SVGTransformList, it has a comment from RBarryYoung saying that I should use the svg.viewportElement. What does svg refer to (I have tried the element) and (not that relevant) why does viewportElement not show up in VSCode ctrlspace menu?

Advertisement

Answer

Since after editing your question changed I’m adding a new answer. I hope this is what you need:

To the svg I’m adding 9 rectangles, thecells you intend to click. For every rectangle I am adding an event listener: when clicked the cross appear in the clicked rectangle. In order to know the position I’m using the value ofthe x and y attributes of the rectangle + 25

Please observe that the nested svg element (#cross) has a viewBox a x a y a width and a height attributes. Alsoinstead of translating the svg element I’m changing the x and y attributes.

let rects = document.querySelectorAll("rect");
let cross = document.querySelector("#cross")

rects.forEach(r => {
  r.addEventListener("click",(e)=>{
  let x = Number(r.getAttribute("x")) || 0;
  let y = Number(r.getAttribute("y")) || 0;
  
  cross.setAttribute("x",x+25);  
  cross.setAttribute("y",y+25);  

  })
})
<svg width="300" height="300" id="game-board" class="w-auto mx-auto">
  <g stroke-width="3" stroke="#9b9b9b">
  <line y2="300" x2="100" y1="0" x1="100" stroke="#9b9b9b" />
  <line stroke-width="3" y2="300" x2="200" y1="0" x1="200" />
  <line stroke-width="3" y2="100" x2="0" y1="100" x1="300" />
  <line stroke-width="3" y2="200" x2="0" y1="200" x1="300" />
  </g>
  <rect width="100" height="100" />
  <rect x="100" width="100" height="100" />
  <rect x="200" width="100" height="100" />
  <rect y="100" width="100" height="100" />
  <rect y="100" x="100" width="100" height="100" />
  <rect y="100" x="200" width="100" height="100" />
  <rect y="200" width="100" height="100" />
  <rect y="200" x="100" width="100" height="100" />
  <rect y="200" x="200" width="100" height="100" />

  <line stroke-width="3" y2="300" x2="100" y1="0" x1="100" stroke="#9b9b9b" fill="none" />
  <line stroke-width="3" y2="300" x2="200" y1="0" x1="200" stroke="#9b9b9b" fill="none" />
  <line stroke-width="3" y2="100" x2="0" y1="100" x1="300" stroke="#9b9b9b" fill="none" />
  <line stroke-width="3" y2="200" x2="0" y1="200" x1="300" stroke="#9b9b9b" fill="none" />

  <svg id="cross" viewBox="4 4 8 8" width="50" height="50" x="25" y="25">
    <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z" stroke="silver" />
  </svg>

</svg>
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement