Reputation: 417
I have svg elements that updated based on mouse position in a mousemove
event. I tried doing the same by binding touchmove
to the elements as well, but when the element is moved, the touchmove
stops working and the element simply stutters slightly before stopping the event alltogether. Is there a way to prevent touchmove
from canceling when the element updates position?
$('svgCanvas').mousedown(function(e){
// here I get element mouse is over
}
$('svgCanvas').mousemove(function(e){
// here I update the position of the element based on e.pageX, e.pageY
// the elements are saved in an array which updates as it moves
Update();
}
//The function I'm trying to make work:
$('svgCanvas').bind('touchmove', function(e){
e.preventDefault();
var x = e.originalEvent.touches[0].pageX;
var y = e.originalEvent.touches[0].pageY;
if(elem){
// elem is the element I get from mousedown (which also work on touch)
elem.position[x,y];
}
Update();
}
Now what Update()
does is that it redraws all the svg elements on the screen from an array of elements I have. I'm pretty sure this is where the problem is, as the touch event stops working as soon as this runs once. The mousemove
event however works just fine with it.
Edit: Added a snippet of the issue.
$(document).ready(function() {
// Initialize variables
var draw = SVG.get('svgCanvas'),
group = draw.group().id('svgGroup'),
clicking = false,
data = [],
newSvgElement = {},
color = "#6658A4",
elem;
// Create a new svg element on click
$('#image').click(function(e){
var x = e.pageX;
var y = e.pageY;
var rand = Math.floor(Math.random() * 100000000);
if(document.getElementById('createElement').checked){
//Add the svg to an array as an Object
newSvgElement.size = 30;
newSvgElement.id = 'svgElement' + rand;
newSvgElement.position = [x,y];
data.push( Object.assign({}, newSvgElement) );
}
// Redraws all svg objects in the data[] array
Update();
});
// Stop dragging the svg
$("#svgCanvas").mouseup(function(){
clicking = false;
});
// Drag the svg on mouse
$("#svgCanvas").mousemove(function(e){
if(clicking && document.getElementById("moveElement").checked){
var x = e.pageX, y = e.pageY;
if(elem){
elem.position = [x,y];
Update();
}
}
});
// Drag svg on touch, this is the one I have issues with
$('#svgCanvas').bind('touchmove', function(e){
if(document.getElementById("moveElement").checked) {
var x = e.originalEvent.touches[0].pageX;
var y = e.originalEvent.touches[0].pageY;
if(elem){
elem.position = [x,y];
Update();
}
}
});
// Get the svg clicked, if its the background we dont want to move anything
$("#svgCanvas").mousedown(function(e){
if(document.getElementById("moveElement").checked) {
var x = e.clientX,
y = e.clientY,
elementMouseIsOver = document.elementFromPoint(x, y).id;
clicking = true;
elem = dataInPosition(elementMouseIsOver);
}
});
// Redraws every svg in the array
function Update(){
$("#svgGroup").empty();
for (var i = 0; i < data.length; i++) {
DrawPoint(data[i]);
}
}
// Get the id of the clicked svg
function dataInPosition(id) {
if(data.length){
var pos = data.map(function (element) {
return element.id
}).indexOf(id);
return data[pos];
}
}
// Creates the svg element using SVG.js
function DrawPoint(svgObject){
var size = svgObject.size;
var x = svgObject.position[0];
var y = svgObject.position[1];
var drawObject = group.circle(size).id(svgObject.id);
drawObject.attr({
fill: color,
'fill-opacity': "0.0",
stroke: color,
'stroke-width': size/10,
cx: x,
cy: y
});
}
});
#form{
position: absolute;
left: 30px;
top: 30px;
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.4/svg.js"></script>
<svg id="svgCanvas" height="900px" width="450px">
<image id='image' xlink:href="http://routegadget.jukola.com/kartat/41.jpg" x="0" y="0" height="900px" width="450px"/>
</svg>
<div id="form">
<form>
<input type="radio" name="state" id="createElement" class="state" checked>
<label for="createElement">Create Element <br/></label>
<input type="radio" name="state" id="moveElement" class="state">
<label for="moveElement">Move Element <br/></label>
</form>
</div>
Upvotes: 0
Views: 470
Reputation: 9235
So your widget is "crazy":) I mean it does a lot of things inefficiently imho. I rewrote it to something how I would write it.
Please check it out. Let me know if you need clarifications
As for what i think is happening:
My code is not perfect yet as well and u can still optimize it further but its current performance should be good.
// Initialize variables
// mode: on/off
var createMode = true;
// caching DOM stuff:
var svgCanvas = document.getElementById('svgCanvas');
var svgGroupContainer = document.getElementById('svgGroupContainer');
// this will hold our target element:
var elem;
// init method:
function onSVGinit() {
// this will hold SVG space coordinates:
var inputCoordinates = svgCanvas.createSVGPoint();
// adding initial event listeners:
svgCanvas.addEventListener('mousedown', mouseDown, { passive: true })
svgCanvas.addEventListener('touchstart', touchDown, { passive: true })
// on input methods:
function mouseDown(e) {
elem = e.target;
// need to do coordinates conversion to match SVG space:
inputCoordinates.x = event.clientX;
inputCoordinates.y = event.clientY;
inputCoordinates = inputCoordinates.matrixTransform(svgCanvas.getScreenCTM().inverse());
svgCanvas.addEventListener('mousemove', move, { passive: true })
svgCanvas.addEventListener('mouseup', end, {passive:true});
}
function touchDown(e) {
elem = e.target
// need to do coordinates conversion to match SVG space:
inputCoordinates.x = event.touches[0].clientX;
inputCoordinates.y = event.touches[0].clientY;
inputCoordinates = inputCoordinates.matrixTransform(svgCanvas.getScreenCTM().inverse());
svgCanvas.addEventListener('touchmove', move, { passive: true })
svgCanvas.addEventListener('touchup', end, {passive:true});
}
function move(e) {
if (!createMode) {
// need to do coordinates conversion to match SVG space:
inputCoordinates.x = event.clientX? event.clientX : event.touches[0].clientX;
inputCoordinates.y = event.clientY? event.clientY : event.touches[0].clientY;
inputCoordinates = inputCoordinates.matrixTransform(svgCanvas.getScreenCTM().inverse());
}
elem.setAttribute("cx", inputCoordinates.x);
elem.setAttribute("cy", inputCoordinates.y);
}
function end(e) {
// dynamically remove listeners:
svgCanvas.removeEventListener('touchmove', move, { passive: true });
svgCanvas.removeEventListener('mousemove', move, { passive: true });
svgCanvas.removeEventListener('touchup', move, { passive: true });
svgCanvas.removeEventListener('mouseup', move, { passive: true });
// if createMode is on - we spawn a circle:
if (createMode) {
createCircle()
}
}
// function to create circle element:
function createCircle() {
var circle = document.createElementNS("http://www.w3.org/2000/svg", "circle")
circle.setAttribute("cx", inputCoordinates.x);
circle.setAttribute("cy", inputCoordinates.y);
circle.setAttribute("r", 30);
circle.setAttribute("fill", "transparent");
circle.setAttribute("stroke","#6658A4")
svgGroupContainer.appendChild(circle);
}
};
function switchMode(e) {
createMode = !createMode;
}
#form{
position: absolute;
left: 30px;
top: 30px;
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.4/svg.js"></script>
<svg id="svgCanvas" height="900px" width="450px" onload="onSVGinit()">
<image id='image' xlink:href="http://routegadget.jukola.com/kartat/41.jpg" x="0" y="0" height="900px" width="450px"/>
<g id="svgGroupContainer"></g>
</svg>
<div id="form">
<form>
<input type="radio" name="state" id="createElement" class="state" onclick="switchMode()" checked>
<label for="createElement">Create Element <br/></label>
<input type="radio" name="state" id="moveElement" class="state" onclick="switchMode()">
<label for="moveElement">Move Element <br/></label>
</form>
</div>
Upvotes: 1