Reputation: 83
I want to draw SVG path with mouse on canvas. I don't want any library like rapheal.js here to draw shapes, I want pure JS.
I have creaed JS:
var svgCanvas = document.getElementById("svgCanvas");
var svgPath;
svgCanvas.addEventListener("touchstart", startDrawTouch, false);
svgCanvas.addEventListener("touchmove", continueDrawTouch, false);
svgCanvas.addEventListener("touchend", endDrawTouch, false);
function startDrawTouch(event)
{
var touch = event.changedTouches[0];
svgPath = createSvgElement("path");
svgPath.setAttribute("fill", "none");
svgPath.setAttribute("shape-rendering", "geometricPrecision");
svgPath.setAttribute("stroke-linejoin", "round");
svgPath.setAttribute("stroke", "#000000");
svgPath.setAttribute("d", "M" + touch.clientX + "," + touch.clientY);
svgCanvas.appendChild(svgPath);
}
function continueDrawTouch(event)
{
if (svgPath)
{
var touch = event.changedTouches[0];
var pathData = svgPath.getAttribute("d");
pathData = pathData + " L" + touch.clientX + "," + touch.clientY
svgPath.setAttribute("d", pathData);
}
}
function endDrawTouch(event)
{
if (svgPath)
{
var pathData = svgPath.getAttribute("d");
var touch = event.changedTouches[0];
pathData = pathData + " L" + touch.clientX + "," + touch.clientY
svgPath.setAttribute("d", pathData);
svgPath = null;
}
}
function createSvgElement(tagName)
{
return document.createElementNS("http://www.w3.org/2000/svg", tagName);
}
This take time on tablet to draw path. Having performance issue, in case you have better idea please share.
Thanks in advance.
Upvotes: 4
Views: 4410
Reputation: 15391
Maybe you can try if <polyline>
and its .points
property work and can give you better performance. Untested modification of your code:
var svgCanvas = document.getElementById("svgCanvas");
var svgPolyline;
svgCanvas.addEventListener("touchstart", startDrawTouch, false);
svgCanvas.addEventListener("touchmove", continueDrawTouch, false);
svgCanvas.addEventListener("touchend", endDrawTouch, false);
function startDrawTouch(event)
{
var touch = event.changedTouches[0];
svgPolyline = createSvgElement("polyline");
svgPolyline.setAttribute("fill", "none");
svgPolyline.setAttribute("shape-rendering", "geometricPrecision");
svgPolyline.setAttribute("stroke-linejoin", "round");
svgPolyline.setAttribute("stroke", "#000000");
svgCanvas.appendChild(svgPolyline);
continueDrawTouch(event);
}
function continueDrawTouch(event)
{
if (svgPolyline)
{
var touch = event.changedTouches[0];
var point = svgPolyline.ownerSVGElement.createSVGPoint();
point.x = touch.clientX;
point.y = touch.clientY;
var ctm = event.target.getScreenCTM();
if (ctm = ctm.inverse())
{
point = point.matrixTransform(ctm);
}
svgPolyline.points.appendItem(point);
}
}
function endDrawTouch(event)
{
continueDrawTouch(event);
svgPolyline = null;
}
function createSvgElement(tagName)
{
return document.createElementNS("http://www.w3.org/2000/svg", tagName);
}
Edit: .clientX/Y
doesn't necessarily give you the coordinates you want, depending on the structure of your document, scroll or transformations. I therefore edited the code with some inspiration from another question (but using . The method name .screenX/Y
, which should be more appropriate in connection with .getScreenCTM
).getScreenCTM()
caused me some confusion. .clientX/Y
is indeed what's needed, see the specs.
Upvotes: 1
Reputation: 124324
You are reconstructing the path element in each continueDrawTouch call. That means converting it from the internal representation to a string then appending to the string and converting it back again.
Most browsers (Firefox for certain for instance) will be more performant if you avoid this and use the SVG DOM instead. The code would become:
if (svgPath)
{
var touch = event.changedTouches[0];
var newSegment = svgPath.createSVGPathSegLinetoAbs(touch.clientX, touch.clientY);
svgPath.pathSegList.appendItem(newSegment);
}
The same comment applies to the endDrawTouch function.
Upvotes: 2